STM32中的IIC可分为硬件IIC和软件IIC,但实际应用中更倾向于软件IIC。即可自行指定两个端口为数据线和时钟线进行IIC的模拟。但是相对于52单片机的IIC协议,STM32的IIC时序操作完全一致,唯一的差别在与STM32需要对端口的信号输入输出端口进行配置。
IIC总线协议之起始信号: IIC的启动实在SDA和SCL均为高电平期间,SDA拉低后SCL接着拉低。
void IIC_Start(void)
{
SDA_OUT(); // 配置SDA为输出模式
SDA_SET;
SCL_SET; // 初始状态SDA、SCL均为高电平
delay_us(5);
SDA_CLR; // SDA拉低
delay_us(5); // 延时5us以上
SCL_CLR;
}
IIC总线协议之停止信号:IIC的停止是发生在SCL为高电平期间,SDA突然拉高。
void IIC_Stop(void)
{
SDA_OUT();
SDA_CLR;
SCL_SET;
delay_us(5);
SDA_SET;
delay_us(5);
}
IIC总线协议之伪造应答:IIC从器件的应答主要是发生在SCL为高电平期间,SDA为低电平,延时5us以后SCL拉低。
void IIC_Ack(void)
{
SDA_OUT();
SDA_CLR;
SCL_SET;
delay_us(5);
SCL_CLR;
delay_us(5);
}
IIC总线协议之伪造非应答:IIC器件的非应答是指在SCL为高电平期间,SDA也为高电平,延时5us以后SCL拉低。
void IIC_NoAck(void)
{
SDA_OUT();
SDA_SET;
SCL_SET;
delay_us(5);
SCL_CLR;
delay_us(5);
}
IIC总线协议之写单字节:IIC发送数据时从高到低位发送。
void IIC_WriteByte(unsigned char data)
{
unsigned char i ;
SDA_OUT();
for(i=0;i<8;i++)
{
if((data<<i)&0x80) // 先发送高位数据
SDA_SET;
else
SDA_CLR;
// __Nop(); //确保数据线上电平稳定
SCL_SET; // 时钟线高电平期间,数据线上数据稳定有效
delay_us(5);
SCL_CLR; // 时钟线为低电平期间,开始发送数据
}
}
IIC总线协议之读单字节:IIC读取数据时也是先读取高位。
unsigned char IIC_ReadByte(void)
{
unsigned char i,dat=0;
SDA_IN();
SDA_SET;
for(i=0;i<8;i++)
{
SCL_CLR;
delay_us(5);
SCL_SET;
dat<<=1;
if(SDA)
dat+=1;
}
SCL_CLR;
delay_us(5);
return(dat);
}
IIC总线协议之写数据:按照总线协议发送。
void IIC_WriteData(unsigned char add,unsigned char dat)
{
IIC_Start(); // 开启IIC总线
IIC_WriteByte(0XA0); // 写入从器件地址
IIC_Ack(); // 伪造应答
IIC_WriteByte(add); // 写入从器件内存地址
IIC_Ack(); // 伪造应答
IIC_WriteByte(dat); // 写入数据
IIC_Ack(); // 伪造应答
IIC_Stop(); // 停止IIC总线
}
IIC总线协议之读数据:在读取时首先发送从期间地址和要读取的内存地址,然后重新启动总线读取数据。
unsigned char IIC_ReadData(unsigned char add)
{
unsigned char temp=0;
IIC_Start();
IIC_WriteByte(0XA0);
IIC_Ack();
IIC_WriteByte(add);
IIC_Ack();
IIC_Start();
IIC_WriteByte(0XA1); // 从期间读操作
IIC_Ack();
temp=IIC_ReadByte(); // 读取值
SCL_CLR;
IIC_NoAck();
IIC_Stop();
return temp;
}
当然在这些操作之前,还需要配置相关的IO口。
void IIC_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct ;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7 ;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP ; // 推挽输出
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz ;
GPIO_Init(GPIOB,&GPIO_InitStruct);
GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7); //先拉高
}
除此之外,还有关于端口的宏定义。
#define SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}
#define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}
#define SCL_SET GPIO_SetBits(GPIOB,GPIO_Pin_6)
#define SCL_CLR GPIO_ResetBits(GPIOB,GPIO_Pin_6)
#define SDA_SET GPIO_SetBits(GPIOB,GPIO_Pin_7)
#define SDA_CLR GPIO_ResetBits(GPIOB,GPIO_Pin_7)
#define SDA GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_7)
最后当然是我们验证对24C02的数据读写结果是否正确。
int main(void)
{
u8 flag=0 ;
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(9600); //串口初始化为115200
LCD_Init();
POINT_COLOR=RED;
IIC_Config(); //IIC相关端口配置
flag=IIC_ReadData(0x00); // 读取之前开机次数
flag++; // 加1次
IIC_WriteData(0x00,flag); // 将当前开机启动次数写进去
printf("%d",flag); // 串口显示当前开机次数
while(1)
{
POINT_COLOR=RED;
LCD_ShowString(30,40,210,16,16,"This is the");
LCD_ShowNum(124,40,flag,2,16);
LCD_ShowString(140,40,210,24,16,"th Start The System");
LED0=!LED0;
delay_ms(1000);
}
}
到此,就完成了关于IIC协议操作24C02的操作,实现开机记录次数。