54。I2C通信实验
2018-10-17 来源:eefocus
C语言小知识:
如何定义字符串:
可以通过字符数组或字符指针来定义字符串,也可以用宏定义对常量字符串进行定义。
//定义字符串的几种方式
char names2[ ] = 'jack'; 字符串数组
char * names3 = 'jack'; 字符串指针
字符串是按ASSII码表以十六进制码存储的,比如 W就是0x57, a就是0x61
注意:
声明存储字符串的数组时,数组大小至少比所存储的字符串多1,因为编译器会自动在字符串常量的末尾添加空字符\0。
一。IIC通信协议
(一)概述
IIC器件的连接方式
每个器件都有自己相应的地址。
IIC(Inter-Integrated Circuit)总线是一种由 PHILIPS 公司开发的两线式串行总线,用于连接微控制器及其外围设备。它是由数据线 SDA 和时钟 SCL 构成的串行总线,可发送和接收数据。在 CPU 与被控 IC 之间、IC 与 IC 之间进行双向传送,高速 IIC 总线一般可达 400kbps 以上。I2C 总线在传送数据过程中共有三种类型信号, 它们分别是:开始信号、结束信号和应答信号。
开始信号:SCL 为高电平时,SDA 由高电平向低电平跳变,开始传送数据。
结束信号:SCL 为高电平时,SDA 由低电平向高电平跳变,结束传送数据。
应答信号:接收数据的 IC 在接收到 8bit 数据后,向发送数据的 IC 发出特定的低电平脉冲,表示已收到数据。 CPU 向受控单元发出一个信号后,等待受控单元发出一个应答信号, CPU 接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障。
(二)IIC协议
1. 空闲状态
SCL和SDA分别接两个上拉电阻,以使IIC在空闲的时候为高电平。
2. 开始信号和停止信号
3. 应答信号
有效应答ACK必须在第9个时钟脉冲为高电平期间保持稳定,因此先要将SCL拉低,然后发出ACK信号,再拉高SCL,再把SCL拉低。
4. 数据的有效性
数据必须要在SCL为高电平期间保持稳定,因此要先把SCL拉低,然后发送数据,再把SCL拉高,再把SCL拉低,这样就保证了在SCL为高电平期间数据是稳定的,再把SCL拉低,在SCL为低电平期间数据才允许变化。
5. 数据传输
(三)IIC底层驱动程序
STM32 的 CRH 控制着每组 IO 端口(A~G)的高 8 位的模式。每个 IO 端口的位占用 CRL 的 4 个位,高两位为 CNF,低两位为 MODE。
0X3 表示推挽输出模式(做输出口用,50M 速率)、0X8 表示上/下拉输入模式(做输入口用)。
myiic.h
//IO方向设置,控制IO工作模式
#define SDA_IN() {GPIOB->CRH&=0XFFFF0FFF;GPIOB->CRH|=8<<12;}
#define SDA_OUT() {GPIOB->CRH&=0XFFFF0FFF;GPIOB->CRH|=3<<12;}
//IO操作函数
#define IIC_SCL PBout(10) //SCL
#define IIC_SDA PBout(11) //SDA
#define READ_SDA PBin(11) //输入SDA
myiic.c
//初始化IIC
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE ); //使能GPIOB时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_10|GPIO_Pin_11); //PB6,PB7 输出高,因为IIC总线在空闲是都为高电平
}
//产生IIC起始信号
void IIC_Start(void)
{
SDA_OUT(); //sda线输出
IIC_SDA=1;
IIC_SCL=1;
delay_us(4);
IIC_SDA=0;//START:when CLK is high,DATA change form high to low
delay_us(4);
IIC_SCL=0;//钳住I2C总线,准备发送或接收数据
}
//产生IIC停止信号
void IIC_Stop(void)
{
SDA_OUT();//sda线输出
IIC_SCL=0;
IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
delay_us(4);
IIC_SCL=1;
IIC_SDA=1;//发送I2C总线结束信号
delay_us(4);
}
要先把SCL拉低,然后准备好ACK应答数据,再拉高SCL,这样就可以保证在SCL为高电平期间ACK信号是稳定的。
//产生ACK应答,也就是有效应答
void IIC_Ack(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=0;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
//不产生ACK应答,无效应答
void IIC_NAck(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=1;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
同样是先把SCL拉低,然后准备好数据,再拉高SCL,然后再拉低SCL,这样是为了保证在SCL为高电平期间数据是稳定的,数据只有在SCL为低电平时才允许改变。
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT();
IIC_SCL=0;//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
if((txd&0x80)>>7) //判断数据最高位是不是1
IIC_SDA=1; //是1 , SDA输出高
else
IIC_SDA=0; //是0,SDA输出低
txd<<=1; //数据左移一位
delay_us(2); //对TEA5767这三个延时都是必须的
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
delay_us(2);
}
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
u8 IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
IIC_SCL=0;
delay_us(2);
IIC_SCL=1;
receive<<=1;
if(READ_SDA)receive++;
delay_us(1);
}
if (!ack)
IIC_NAck();//发送nACK
else
IIC_Ack(); //发送ACK
return receive;
}
二。24C02操作
A0,A1,A2为地址线,确定IIC器件的地址
24C02容量为2K,A0,A1,A2都接地, 读为1,写为0
所以这里24C02读地址为 1010 0001,也就是A1,写地址为 1010 0000,也就是A0。
24cxx.c文件
初始化程序
//初始化IIC接口
void AT24CXX_Init(void)
{
IIC_Init();
}
写一个字节程序
//在AT24CXX指定地址写入一个数据
//WriteAddr :写入数据的目的地址
//DataToWrite:要写入的数据
void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
{
IIC_Start();
if(EE_TYPE>AT24C16)
{
IIC_Send_Byte(0XA0); //发送写命令,这里24C02写地址为A0
IIC_Wait_Ack();
IIC_Send_Byte(WriteAddr>>8);//发送高地址
}else
{
IIC_Send_Byte(0XA0+((WriteAddr/256)<<1)); //发送器件地址0XA0,写数据,为了兼容其他型号 的IIC芯片
}
IIC_Wait_Ack();
IIC_Send_Byte(WriteAddr%6); //发送低地址
IIC_Wait_Ack();
IIC_Send_Byte(DataToWrite); //发送字节
IIC_Wait_Ack();
IIC_Stop();//产生一个停止条件
delay_ms(10);
}
读一个字节程序
前面一段为DUMMY WRITE,后面才读入一个DATA
//在AT24CXX指定地址读出一个数据
//ReadAddr:开始读数的地址
//返回值 :读到的数据
u8 AT24CXX_ReadOneByte(u16 ReadAddr)
{
u8 temp=0;
IIC_Start();
if(EE_TYPE>AT24C16)
{
IIC_Send_Byte(0XA0); //发送写命令
IIC_Wait_Ack();
IIC_Send_Byte(ReadAddr>>8);//发送高地址
IIC_Wait_Ack();
}else IIC_Send_Byte(0XA0+((ReadAddr/256)<<1)); //发送器件地址0XA0,写数据,为了兼容其他型号 的IIC芯片
IIC_Wait_Ack();
IIC_Send_Byte(ReadAddr%6); //发送低地址
IIC_Wait_Ack();
IIC_Start();
IIC_Send_Byte(0XA1); //进入接收模式 ,这里24C02读地址为A1
IIC_Wait_Ack();
temp=IIC_Read_Byte(0);
IIC_Stop();//产生一个停止条件
return temp;
}
三。实验程序讲解
//要写入到24c02的字符串数组
const u8 TEXT_Buffer[]={'WarShipSTM32 IIC TEST'}; //定义一个字符串数组
#define SIZE sizeof(TEXT_Buffer) //SIZE为数组大小,比定义的数组大1,因为编译器自动在字符串结尾加了 一个结束符 \0
int main(void)
{
u8 a,key;
u16 i=0;
u8 datatemp[SIZE];
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
LED_Init(); //初始化与LED连接的硬件接口
LCD_Init(); //初始化LCD
KEY_Init(); //按键初始化
AT24CXX_Init(); //IIC初始化
POINT_COLOR=RED;//设置字体为红色
LCD_ShowString(30,50,200,16,16,'WarShip STM32');
LCD_ShowString(30,70,200,16,16,'IIC TEST');
LCD_ShowString(30,90,200,16,16,'ATOM@ALIENTEK');
LCD_ShowString(30,110,200,16,16,'2015/1/15');
LCD_ShowString(30,130,200,16,16,'KEY1:Write KEY0:Read'); //显示提示信息
while(AT24CXX_Check())//检测不到24c02
{
LCD_ShowString(30,150,200,16,16,'24C02 Check Failed!');
delay_ms(500);
LCD_ShowString(30,150,200,16,16,'Please Check! ');
delay_ms(500);
LED0=!LED0;//DS0闪烁
}
LCD_ShowString(30,150,200,16,16,'24C02 Ready!');
POINT_COLOR=BLUE;//设置字体为蓝色
while(1)
{
key=KEY_Scan(0);
if(key==KEY1_PRES)//KEY_UP按下,写入24C02
{
LCD_Fill(0,170,239,319,WHITE);//清除半屏
LCD_ShowString(30,170,200,16,16,'Start Write 24C02....');
AT24CXX_Write(0,(u8*)TEXT_Buffer,SIZE); //写入字符串数组
LCD_ShowString(30,170,200,16,16,'24C02 Write Finished!');//提示传送完成
}
if(key==KEY0_PRES)//KEY1按下,读取字符串并显示
{
LCD_ShowString(30,170,200,16,16,'Start Read 24C02.... ');
AT24CXX_Read(0,datatemp,SIZE); //读出字符串数组
LCD_ShowString(30,170,200,16,16,'The Data Readed Is: ');//提示传送完成
LCD_ShowString(30,190,200,16,16,datatemp);//显示读到的字符串
}
i++;
delay_ms(10);
if(i==20)
{
LED0=!LED0;//提示系统正在运行
i=0;
}
}
}
上一篇:55.SPI接口原理与配置
下一篇:53.PWM DAC实验