I2c总线驱动

I2C总线

硬件

作为多设备串行通信的代表
I2C
通过一台总设备(控制与计算中心设备,及单片机)
与多台从设备(一共210=1023(加一台单片机))
以I2c总线的方式c
在这里插入图片描述

接口功能使用标准
GND所有设备共地
SCK时钟频率
SDA数据传输详见电平时序图

from I2C_BUS_Specification

对应时序图而编写的
i2c.c
分别用于发送和接受字符&字符串

void I2C_SAND_BUFFER(u8 SlaveAddr,u8 WriteAddr,u8* pBuffer,u16 NumByteToWrite){ //I2C发送数据串(器件地址,寄存器,内部地址,数量)
	I2C_GenerateSTART(I2C1,ENABLE);//产生起始位
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)); //清除EV5
	I2C_Send7bitAddress(I2C1,SlaveAddr,I2C_Direction_Transmitter);//发送器件地址
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//清除EV6
	I2C_SendData(I2C1,WriteAddr); //内部功能地址
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));//移位寄存器非空,数据寄存器已空,产生EV8,发送数据到DR既清除该事件
	while(NumByteToWrite--){ //循环发送数据	
		I2C_SendData(I2C1,*pBuffer); //发送数据
		pBuffer++; //数据指针移位
		while (!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));//清除EV8
	}
	I2C_GenerateSTOP(I2C1,ENABLE);//产生停止信号
}
void I2C_SAND_BYTE(u8 SlaveAddr,u8 writeAddr,u8 pBuffer){ //I2C发送一个字节(从地址,内部地址,内容)
	I2C_GenerateSTART(I2C1,ENABLE); //发送开始信号
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)); //等待完成	
	I2C_Send7bitAddress(I2C1,SlaveAddr, I2C_Direction_Transmitter); //发送从器件地址及状态(写入)
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); //等待完成	
	I2C_SendData(I2C1,writeAddr); //发送从器件内部寄存器地址
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //等待完成	
	I2C_SendData(I2C1,pBuffer); //发送要写入的内容
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //等待完成	
	I2C_GenerateSTOP(I2C1,ENABLE); //发送结束信号
}
void I2C_READ_BUFFER(u8 SlaveAddr,u8 readAddr,u8* pBuffer,u16 NumByteToRead){ //I2C读取数据串(器件地址,寄存器,内部地址,数量)
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY));
	I2C_GenerateSTART(I2C1,ENABLE);//开启信号
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));	//清除 EV5
	I2C_Send7bitAddress(I2C1,SlaveAddr, I2C_Direction_Transmitter); //写入器件地址
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//清除 EV6
	I2C_Cmd(I2C1,ENABLE);
	I2C_SendData(I2C1,readAddr); //发送读的地址
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //清除 EV8
	I2C_GenerateSTART(I2C1,ENABLE); //开启信号
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)); //清除 EV5
	I2C_Send7bitAddress(I2C1,SlaveAddr,I2C_Direction_Receiver); //将器件地址传出,主机为读
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); //清除EV6
	while(NumByteToRead){
		if(NumByteToRead == 1){ //只剩下最后一个数据时进入 if 语句
			I2C_AcknowledgeConfig(I2C1,DISABLE); //最后有一个数据时关闭应答位
			I2C_GenerateSTOP(I2C1,ENABLE);	//最后一个数据时使能停止位
		}
		if(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)){ //读取数据
			*pBuffer = I2C_ReceiveData(I2C1);//调用库函数将数据取出到 pBuffer
			pBuffer++; //指针移位
			NumByteToRead--; //字节数减 1 
		}
	}
	I2C_AcknowledgeConfig(I2C1,ENABLE);
}
u8 I2C_READ_BYTE(u8 SlaveAddr,u8 readAddr){ //I2C读取一个字节
	u8 a;
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY));
	I2C_GenerateSTART(I2C1,ENABLE);
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));
	I2C_Send7bitAddress(I2C1,SlaveAddr, I2C_Direction_Transmitter); 
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
	I2C_Cmd(I2C1,ENABLE);
	I2C_SendData(I2C1,readAddr);
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));
	I2C_GenerateSTART(I2C1,ENABLE);
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));
	I2C_Send7bitAddress(I2C1,SlaveAddr, I2C_Direction_Receiver);
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
	I2C_AcknowledgeConfig(I2C1,DISABLE); //最后有一个数据时关闭应答位
	I2C_GenerateSTOP(I2C1,ENABLE);	//最后一个数据时使能停止位
	a = I2C_ReceiveData(I2C1);
	return a;
}

配置文件

void I2C_GPIO_Init(void){ //I2C接口初始化
	GPIO_InitTypeDef  GPIO_InitStructure; 	
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);       
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); //启动I2C功能 
    GPIO_InitStructure.GPIO_Pin = I2C_SCL | I2C_SDA; //选择端口号                      
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; //选择IO接口工作方式       
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO接口速度(2/10/50MHz)    
	GPIO_Init(I2CPORT, &GPIO_InitStructure);
}

void I2C_Configuration(void){ //I2C初始化
	I2C_InitTypeDef  I2C_InitStructure;
	I2C_GPIO_Init(); //先设置GPIO接口的状态
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;//设置为I2C模式
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
	I2C_InitStructure.I2C_OwnAddress1 = HostAddress; //主机地址(从机不得用此地址)
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;//允许应答
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; //7位地址模式
	I2C_InitStructure.I2C_ClockSpeed = BusSpeed; //总线速度设置 	
	I2C_Init(I2C1,&I2C_InitStructure);
	I2C_Cmd(I2C1,ENABLE);//开启I2C					
}

void I2C_SEND_BYTE(u8 SlaveAddr,u8 WriteAddr,u8 pBuffer);
//I2C_READ_BUFFER(u8 器件地址,u8 数据地址,u8 数据流);
void I2C_SEND_BUFFER(u8 SlaveAddr,u8 WriteAddr,u8* pBuffer, u16 NumByteToWrite);
//I2C_READ_BUFFER(u8 器件地址,u8 数据地址,u8 数据流);
u8 I2C_READ_BYTE(u8 SlaveAddr, u8 readAddr);

连接在i2c上的

LM75A

(以此为例)
在这里插入图片描述
LM75A
在I2C总线中的地址
由SB=LSB+MSB决定
其中高四位的LSB已在芯片内部被硬件设置为(不可改写的设置为)“1001”
而低四位的MSB的前三位
由引脚A2-A0连接的 高(VCC)或低(GND)电平 而决定
第四位RW被硬件设置为0
在这里插入图片描述

因此
同一个控制器只可以在I2C总线上同时连接至多8个LM75A温度传感芯片
且其地址一定为以下之一

1001 1110 = 0x9E;
1001 1100 = 0x9C;
1001 1010 = 0x9A;
1001 1000 = 0x98;
1001 0110 = 0x96;
1001 0100 = 0x94;
1001 0010 = 0x92;
1001 0000 = 0x90;

LM75A的寄存器表
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
可知
只要通过以下这样一段程序
lm75a.h

#ifndef __LM75A_H
#define __LM75A_H	 
#include "sys.h"
#include "i2c.h"


#define LM75A_ADD	0x9E	//器件地址



void LM75A_GetTemp(u8 *Tempbuffer);//读温度
void LM75A_POWERDOWN(void); //掉电模式
		 				    
#endif

lm75a.c

void LM75A_GetTemp(u8 *Tempbuffer)
{
	u8 buf[3];
	u8 t=0, a=0;
	I2C_READ_BUFFER(LM75A_ADD, 0X00, BUF, 3);
	//器件地址,子地址(温度地址),数据缓存器,字节数
	//读出温度
	t = buf[0];
	*TempBuffer = 0;//用于标记温度是否为负值
	if(t & 0x80)
	{
		*Tempbuffer = 1;//温度低于0度,取负值
		t = ~t; t++;//温度低于0时取补码,且取反后加1
	}
	//根据寄存器的数据记录方式,可知温度的整数位,应由对应寄存器上的7位二进制数转化而来
	//你当然可以通过超级终端直接printf("%d",*0x00);
	//但是在数据传输时,就要下场进行一次进制转换了
	if(t & 0x01) a+=1if(t & 0x02) a+=2if(t & 0x04) a+=4if(t & 0x08) a+=8if(t & 0x10) a+=16if(t & 0x20) a+=32if(t & 0x40) a+=64;
	Tempbuffer++;
	*Temmpbuffer = a;
	t = buf[1];//写入温度的整数位
	
	//接下来如法炮制处理温度的小数位
	a = 0;
	if(t & 0x20)a+=12;//应该是12.5,但是没有那么高的精准要求,int也不接受小数
	if(t & 0x40)a+=25;
	if(t & 0x20)a+=50;
	Tempbuffer++;
	*Temmpbuffer = a;
	t = buf[2];//写入温度的整数位
	
}//此时buf[0]与buf[1],buf[2]中分别存储着温度的
	//正负值,整数位与小数位

即可实现对温度数据的转换


TM1640

ps:TM1640数码管驱动芯片不是这里的主角,只给出驱动函数,具体的可以去翻数据手册和固件库程序函数
TM1640.h

#ifndef __TM1640_H
#define __TM1640_H	 
#include "sys.h"

#define TM1640_GPIOPORT	GPIOA	//定义IO接口
#define TM1640_DIN	GPIO_Pin_12	//定义IO接口
#define TM1640_SCLK	GPIO_Pin_11	//定义IO接口

#define TM1640_LEDPORT	0xC8	//定义IO接口


void TM1640_Init(void);//初始化
void TM1640_led(u8 date);//
void TM1640_display(u8 address,u8 date);//
void TM1640_display_add(u8 address,u8 date);//

		 				    
#endif

最后

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "TM1640.h"

#include "lm75a.h"

int main (void){//主程序
	u8 buffer[3];
	u8 c=0x01;
	RCC_Configuration(); //系统时钟初始化 

	I2C_Configuration();//I2C初始化

	TM1640_Init(); //TM1640初始化
	TM1640_display(0,20); //初始显示内容
	TM1640_display(1,20);
	TM1640_display(2,20);
	TM1640_display(3,20);
	TM1640_display(4,20);
	TM1640_display(5,20);
	TM1640_display(6,20);
	TM1640_display(7,20);

	while(1){
		LM75A_GetTemp(buffer); //读取LM75A的温度数据
			
		TM1640_display(0,buffer[1]/10); //显示数值
		TM1640_display(1,buffer[1]%10+10);
		TM1640_display(2,buffer[2]/10);
		TM1640_display(3,buffer[2]%10);

		TM1640_led(c); //与TM1640连接的8个LED全亮
		c<<=1; //数据左移 流水灯
		if(c==0x00)c=0x01; //8个灯显示完后重新开始
		delay_ms(150); //延时
	}
}
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值