I2C总线
硬件
作为多设备串行通信的代表
I2C
通过一台总设备(控制与计算中心设备,及单片机)
与多台从设备(一共210=1023(加一台单片机))
以I2c总线的方式c
接口 | 功能 | 使用标准 |
---|---|---|
GND | 所有设备共地 | |
SCK | 时钟 | 频率 |
SDA | 数据传输 | 详见电平时序图 |
对应时序图而编写的
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+=1;
if(t & 0x02) a+=2;
if(t & 0x04) a+=4;
if(t & 0x08) a+=8;
if(t & 0x10) a+=16;
if(t & 0x20) a+=32;
if(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); //延时
}
}