本文章是作为学习理解的一个IIC底层驱动,是以STM32F407芯片为例子,如有问题请大佬们指点。
那么IIC在MCU开发者应聘中是必不可少的提问的一个问题,所以我在正点原子和一个大哥的例子上学习,再修改一些自己所理解的注释,方便能更容易的理解代码的意思。
在IIC底层中无非就是开始信号、结束信号、应答信号、不应答信号、发送一个字节、接收一个字节。
起始信号和停止信号时序图:
对于延时在正点的文章里解释的是:为了控制IIC的读写速度,通过示波器检测读写速度在250KHz内,所以一秒钟传送500Kb数据,换算一下即一个bit位需要2us,在这个延时时间内可以让器件进行获得一个稳定性的数据采集。
应答信号和非应答信号时序图:
这里的区别就是在SCL为1时应答信号SDA线由高到低,非应答信号SDA线由低到高变化。
IIC总时序图:
从这里不难看出,当数据传输第一数据的时候SDA为高,而SCL经过一个完整的脉冲且最后一位R/W位为读写控制位,这里用到一个循环8次就可以完成读写一字节的数据了。
接下来就看看下方总结来的IIC底层驱动代码结合上面的图片来分析就很容易理解到位。
#ifdef _MYIIC_H_
#define _MYIIC_H_
//---------------------------
//此区域用做IIC配置以STM32F407芯片为例子
#define IIC_SCL PB8
#define IIC_SDA PB9
#define IIC_SDA_IN {GPIOB->MODER& = ~(3<<(9*2)),GPIOB->MODER|= 0<<(9*2);} //先将PB9->MODER先清零再置为输出模式 01
#define IIC_SDA_OUT {GPIOB->MODER& = ~(3<<(9*2)),GPIOB->MODER|= 1<<(9*2);} //输入模式为00
#define IIC_SCL_H do{GPIOB->BSRR |= (0x1 << 8);}while(0)
#define IIC_SCL_L do{GPIOB->BRR |= (0x1 << 8);}while(0)
#define IIC_SDA_H do{GPIOB->BSRR |= (0x1 << 9);}while(0)
#define IIC_SDA_L do{GPIOB->BRR |= (0x1 << 9);}while(0)
#define IIC_SDA_READ GPIOB->IDR &(1<<9)
//IIC所有操作函数
void IIC_Init(void); //初始化IIC的IO口
void IIC_Start(void); //发送IIC开始信号
void IIC_Stop(void); //发送IIC停止信号
void IIC_Write_Byte(u8 dat); //IIC发送一个字节
u8 IIC_Read_Byte(bool ack); //IIC读取一个字节
u8 IIC_Wait_Ack(void); //IIC等待ack信号
void IIC_Ack(void); //IIC发送ack信号
void IIC_nAck(void); //IIC不发送ACK信号
#endif
#include "stm32f4xx.h"
#include "MYIIC.h"
#include "gpio.h"
#include "delay.h"
#typedef unsigned char u8;
#typedef unsigned char u16;
#define IIC_SCL PA_1
#define IIC_SDA PA_2
void IIC_Init(void)
{
RCC->AHB1ENR|=1<<1;//使能GPIOB时钟
GPIOB ->MODER &= (~0xF<<(8*2)); //先清零
GPIOB ->MODER |= 10 <<(8*2); //在为引脚PB8、PB9设置为通用的输出功能
GPIOB ->OTYPER &= (~(0x3 << 8)); //设置为推挽输出
GPIOB ->OSPEEOR |=0xF <<(8*2); //设置高速输出
GPIOB ->PUPDR &= (~(0xF << (8*2))); //禁止上拉和下拉
IIC_SCL_H;
IIC_SDA_H;
}
/******************************************************************************
* 函数名: IIC_Start
* 功能描述: IIC_Start 开始信号
******************************************************************************/
void IIC_Start(void)
{
IIC_SDA_OUT ;
IIC_SCL_H;
IIC_SDA_H;
delay_us(2);
IIC_SDA_L;
delay_us(2);
IIC_SCL_L;
delay_us(2);
}
/******************************************************************************
* 函数名: IIC_Stop
* 功能描述: IIC_Stop 结束信号
******************************************************************************/
void IIC_Stop(void)
{
IIC_SDA_OUT ;
IIC_SDA_L;
delay_us(2);
IIC_SCL_H;
delay_us(2);
IIC_SDA_H;
delay_us(2);
}
/******************************************************************************
* 函数名: IIC_Ack
* 功能描述: IIC_Ack 应答信号
******************************************************************************/
//此区域作为应答信号,不应答信号以及等待信号函数,该组信号在对于从机是否收到信号极为重要
void IIC_Ack(void)
{
IIC_SCL_L;
IIC_SDA_OUT;
IIC_SDA_L; //应答与不应答信号主要区别的数据线的高低脉冲的区别;
delay_us(2);
IIC_SCL_H;
delay_us(2);
IIC_SDA_L;
delay_us(2);
}
/******************************************************************************
* 函数名: IIC_nAck
* 功能描述: IIC_nAck 非应答信号
******************************************************************************/
void IIC_nAck()
{
IIC_SCL_L;
IIC_SDA_OUT;
IIC_SDA_H;
delay_us(2);
IIC_SCL_H;
delay_us(2);
IIC_SDA_L;
delay_us(2);
}
/******************************************************************************
* 函数名: IIC_Wait_Ack
* 功能描述: IIC master 等待应答信号
* 参数说明: Flase,接收应答失败
* True,接收应答成功
******************************************************************************/
//在主机发送一个字节后,从机需给主机返回一个应答信号,在此用bool类型返回,当等待应答接受成功后返回True,反之则是False
bool IIC_Wait_Ack(void)
{
u8 errtime=0;
IIC_SDA_IN;
IIC_SDA_H;
delay_us();
IIC_SCL_H;
delay_us();
while(I2C_SDA_READ)
{
errtime++;
if(errtime >250)
{
IIC_Stop();
return False;
}
}
IIC_SCL_L;
return True;
}
/******************************************************************************
* 函数名: IIC_Write_Byte
* 功能描述: 发送一个字节
******************************************************************************/
//此区域用作IIC的写和读字节
/* 时钟在低电平时候,发送器向数据线上写入数据
时钟在高电平时候,接收器从数据线上读取数据 */
void IIC_Write_Byte(u8 dat)
{
u16 i ;
IIC_SDA_OUT ;
IIC_SCL_L; //拉低时钟开始传输数据
for(i=0;i<8;i++)
{
if(dat & 0x80) //检测最高位是否为1
{
IIC_SDA_H; //当检测到有数据就把数据线拉高
}
else
{
IIC_SDA_L;
}
delay_us(2);
IIC_SCL = 1;
delay_us(2);
IIC_SCL = 0;
dat<< = 1; //从左向右传递数据,因此需要移位
}
IIC_SDA_H;
}
/******************************************************************************
* 函数名: IIC_Read_Byte
* 功能描述: 读取一个字节
******************************************************************************/
u8 IIC_Read_Byte(bool ack)
{
u16 i,dat=0;
IIC_SDA_IN;
for(i=0;i<8;i++)
{
dat<< = 1;
IIC_SCL_H;
delay_us(2);
if(I2C_SDA_READ)dat++;
IIC_SCL_L;
delay_us(2);
}
if(ack == True)IIC_Ack();
else IIC_nAck();
return dat;
}
void AT24CXX_Init(void)
{
IIC_Init();
}
u8 AT24CXX_Read_one_dByte(u16 ReadAddr,u8 data)
{
IIC_Start();
IIC_Send_Byte(0XA0+((ReadAddr/256)<<1)); //发送器件地址0xa0,写数据
IIC_Wait_Ack();
IIC_Send_Byte(ReadAddr%256); //发送低地址
IIC_Wait_Ack();
IIC_Start();
IIC_Send_Byte(0XA1); //进入接收模式,从IIC器件读取数据
IIC_Wait_Ack();
temp=IIC_Read_Byte(0);
IIC_Stop();
return temp;
}
u8 AT24XX_Write_one_Byte(u16 WriteAddr,u8 data)
{
IIC_Start();
IIC_Send_Byte(0XA0+((WriteAddr/256)<<1));
IIC_Wait_Ack();
IIC_Send_Byte(WriteAddr%256); ·
IIC_Wait_Ack();
IIC_Send_Byte(data);
IIC_Wait_Ack();
IIC_Stop();
delay_ms(10);
}