Linux 学习记录59(ARM篇)
一、IIC总线
1. 概念
- I2C总线是PHLIPS公司在八十年代初推出的一种串行半双工同步总线,主要用于连接整体电路
- 两个芯片之间通讯 SOC(stm32mp157a)<------- IIC总线 ------->温湿度传感器(si7006)
- 两个设备之间通讯 PC<------ UART总线 ------>开发板
- I2C总线为两线制,只有两根双向信号线,一根是数据线SDA,另一根是时钟线SCL
- SDA数据线作用:完成数据传输
- SCL时钟线作用:完成数据收发同步
- IIC传输速率
- 低速:100K
- 中速:400K
- 高速:3.4M
- I2C硬件结构简单,接口连接方便,成本较低。因此在各个领域得到了广泛的应用
- 、IIC总线外接两个上拉电阻作用:在总线处于空闲状态时,SCL线和SDA线处于高电平状态
2. IIC总线硬件连接
1. IIC总线支持多主机多从机模式,在同一时刻,只能与一个从机进行通讯
2. 在实际使用过程中,大多数都使用单主机多从机模式
3. 挂接到IIC总线上的每个从机设备,都有自己唯一的7位从机地址(从对应的芯片手册中进行查找从机地址)
4. 主动发起数据的叫做主机(起始信号),只能被动接收数据的叫做从机
5. 在总线上,发送数据叫做发送器,接收数据的叫做接收器
6. 起始信号、时钟信号、停止信号都是由主机产生
7. 时钟信号只能由主机产生,作用给从机,完成数据收发同步
二、系统框图
三、IIC时序
1. 起始信号 / 停止信号
1、起始信号:在SCL为高电平期间,SDA从高到低的变化(下降沿)
2、停止信号:在SCL为高电平期间,SDA从低到高的变化(上升沿)
3、起始信号和停止信号由主机产生
4、起始信号产生之后,总线处于占用状态
5、停止信号产生之后,总线处于空闲状态
2. 数据传输信号
1、在SCL为高电平期间,数据线上数据保持稳定,接收器从数据线上读取数据
2、在SCL为低电平期间,数据线上数据允许发生变化,发送器向数据线上写入数据
3. 应答信号 / 非应答信号
1、每一个字节必须保证是8位长度,数据传输时,先传送最高位,在传送低位
每一个被传送的字节后面都必须跟随一位应答位,一帧数据 = 8位数据位 + 1位应答位 = 9位
2、发送器在发送完8位数据之后,接收器在第九个时钟周期,返回一个应答信号(0)/非应答信号(1)
第九个时钟周期,接收器向数据线上写入应答/非应答信号
第九个时钟周期,发送器从数据线上读
读0:应答信号
读1:非应答信号
4. 寻址信号
1、IIC总线上数据传输是广义的,包括从机地址,传输数据信号
2、在起始信号产生之后,寻址从机,需要发送7位从机地址 + 读(1)/写(0)
3、从总线上读取数据:7位从机地址 + 读(1)
4、向总线上写入数据:7位从机地址 + 写(0)
四、IIC协议
1. 主机给从机发送一个字节(写)
主机作为 “发送器” 从机作为“接收器”
IIC_Start();//起始信号
/*add_RH 为从机地址例如0x40 或上0表示要写入数据*/
IIC_Send_Byte((add_RH << 1) | 0);//发送从机地址
IIC_Wait_Ack();//等待回应
/*USER_W表示要写入的寄存器或命令*/
IIC_Send_Byte(USER_W);//发送从机地址
IIC_Wait_Ack();//等待回应
/*start_M表示要写入该寄存器的数据*/
IIC_Send_Byte(start_M);//发送从机地址
IIC_Wait_Ack();//等待回应
IIC_Stop();
2. 主机给从机发送多个连续字节
每发送一个8位数据后寄存器地址自动偏移
3. 从机给主机发送一个字节(读)
文章来源:https://www.toymoban.com/news/detail-612319.html
uint8_t buf;
IIC_Start();//起始信号
/*add_RH 为从机地址例如0x40 或上0表示要写入数据*/
IIC_Send_Byte(add_RH << 1);//发送从机地址
IIC_Wait_Ack();//等待回应
/*USER_R表示要读取的寄存器或命令*/
IIC_Send_Byte(USER_R);//发送从机命令
IIC_Wait_Ack();//等待回应
/*从机开始作为发送方*/
IIC_Start();//起始信号
/*add_RH 为从机地址例如0x40 或上1表示要读取数据*/
IIC_Send_Byte((add_RH << 1) |1);//发送从机地址
IIC_Wait_Ack();//等待回应
/*IIC_R_NACK 表示不读取后续数据*/
buf= IIC_Read_Byte(IIC_R_NACK);//IIC读取一个字节
IIC_Stop();
4. 从机给主机发送多个连续字节
文章来源地址https://www.toymoban.com/news/detail-612319.html
五、软件模拟IIC
1. IIC的GPIO初始化
//初始化IIC
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_MP_AHB4_ENSETR |= (0x1 << 1);//使能GPIO F-E时钟;//使能GPIOB时钟
//GPIOB8,B9初始化设置
GPIO_InitStructure.GPIO_Pin = IIC_SCL_Pin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Fast_Speed;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(IIC_GPIOx, &GPIO_InitStructure);//初始化
GPIO_InitStructure.GPIO_Pin = IIC_SDA_Pin;
GPIO_Init(IIC_GPIOx, &GPIO_InitStructure);//初始化
IIC_SCL_H;//将时钟线和数据线拉高
IIC_SDA_H;
}
2. 宏定义及函数声明
#define IIC_GPIOx GPIOF
#define IIC_R_ACK 1
#define IIC_R_NACK 0
#define IIC_SCL_Pin 14
#define IIC_SDA_Pin 15
#define IIC_SCL_H GPIO_SetBits(IIC_GPIOx,IIC_SCL_Pin)
#define IIC_SDA_H GPIO_SetBits(IIC_GPIOx,IIC_SDA_Pin)
#define IIC_SCL_L GPIO_ResetBits(IIC_GPIOx,IIC_SCL_Pin)
#define IIC_SDA_L GPIO_ResetBits(IIC_GPIOx,IIC_SDA_Pin)
#define READ_SDA GPIO_ReadInputDataBit(IIC_GPIOx,IIC_SDA_Pin)
//IIC所有操作函数
void IIC_Init(void); //初始化IIC的IO口
void IIC_Start(void); //发送IIC开始信号
void IIC_Stop(void); //发送IIC停止信号
void IIC_Send_Byte(uint8_t txd); //IIC发送一个字节
uint8_t IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
uint8_t IIC_Wait_Ack(void); //IIC等待ACK信号
void IIC_Ack(void); //IIC发送ACK信号
void IIC_NAck(void); //IIC不发送ACK信号
3. 切换SDA的GPIO模式
void SDA_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//GPIOB8,B9初始化设置
GPIO_InitStructure.GPIO_Pin = IIC_SDA_Pin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Fast_Speed;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(IIC_GPIOx, &GPIO_InitStructure);//初始化
}
void SDA_IN(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//GPIOB8,B9初始化设置
GPIO_InitStructure.GPIO_Pin = IIC_SDA_Pin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//普通输出模式
GPIO_Init(IIC_GPIOx, &GPIO_InitStructure);//初始化
}
4. 起始信号
//产生IIC起始信号
void IIC_Start(void)
{
SDA_OUT(); //sda线输出
IIC_SDA_H;
IIC_SCL_H;
delay_us(4);
IIC_SDA_L;//START:when CLK is high,DATA change form high to low
delay_us(4);
IIC_SCL_L;//钳住I2C总线,准备发送或接收数据
}
5. 停止信号
//产生IIC停止信号
void IIC_Stop(void)
{
SDA_OUT();//sda线输出
IIC_SCL_L;
IIC_SDA_L;//STOP:when CLK is high DATA change form low to high
delay_us(4);
IIC_SCL_H;
IIC_SDA_H;//发送I2C总线结束信号
delay_us(4);
}
6. 等待应答信号
//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
uint8_t IIC_Wait_Ack(void)
{
uint8_t ucErrTime=0;
SDA_IN(); //SDA设置为输入
IIC_SDA_H;delay_us(1);
IIC_SCL_H;delay_us(1);
while(READ_SDA)
{
ucErrTime++;
if(ucErrTime>250)
{
IIC_Stop();
return 1;
}
}
IIC_SCL_L;//时钟输出0
return 0;
}
7. ACK应答
//产生ACK应答
void IIC_Ack(void)
{
IIC_SCL_L;
SDA_OUT();
IIC_SDA_L;
delay_us(2);
IIC_SCL_H;
delay_us(2);
IIC_SCL_L;
}
//不产生ACK应答
void IIC_NAck(void)
{
IIC_SCL_L;
SDA_OUT();
IIC_SDA_L;
delay_us(2);
IIC_SCL_H;
delay_us(2);
IIC_SCL_L;
}
8. 发送/读取一个字节
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void IIC_Send_Byte(uint8_t txd)
{
uint8_t t;
SDA_OUT();
IIC_SCL_L;//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
if((txd&0x80)>>7){
IIC_SDA_H;
}else{
IIC_SDA_L;
}
txd<<=1;
delay_us(2); //对TEA5767这三个延时都是必须的
IIC_SCL_H;
delay_us(2);
IIC_SCL_L;
delay_us(2);
}
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
uint8_t IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
IIC_SCL_L;
delay_us(2);
IIC_SCL_H;
receive<<=1;
if(READ_SDA)
receive |= 1;
else
receive |= 0;
delay_us(1);
}
if (!ack)
IIC_NAck();//发送nACK
else
IIC_Ack(); //发送ACK
return receive;
}
到了这里,关于Linux 学习记录59(ARM篇)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!