本文介绍STM8L051F3的IIC相关知识。内容分为以下几部分:
1、IIC基础知识IIC(Inter-integrated circuit):集成电路总线。STM8L051F3的IIC拥有提供多主机功能,可以控制特定的IIC总线序列、协议、仲裁和时间管理,支持标准和快速模式,可用于各种应用上,如CRC生成和验证、SMBus(系统管理总线)和PMBus(电源管理总线),使用DMA功能可以减少CPU的负荷。STM8L051F3的IIC主要特点如下:
- 并行总线/IIC协议转换器
- 多主机功能:同一个接口可作为主机或从机
- IIC主机特点:
- IIC从机特点:
- 可编程的IIC地址检测
- 停止位检测
- IIC双重寻址能力,可拥有两个2个IIC地址
- 7位/10位和一般呼叫地址生成和检测
- 支持不同的通讯速度:
- 状态标志:
- 错误标志:
- 主模式仲裁条件丢失
- 在地址/数据发送后应答失败
- 检测到错误的起始或停止条件
- 禁止时钟延展的Overrun/underrun
- 三种类型中断
- 唤醒功能:
- 在从机模式下通过地址检测可将MCU从低功耗模式唤醒
- 可选择的时钟延展
- 在DMA功能下的1字节缓冲器
- 可配置的PEC(数据表错误检测)产生或验证:
- PEC的值能在发送模式下作为最后一个字节发送
- 在接收到的最后一个字节PEC错误检测
- 兼容SMBus2.0
- 25ms时钟拉低超时延迟
- 10ms主机积累时钟拉低延展时间
- 25ms从机积累时钟拉低延展时间
- 硬件PEC生成/验证和应答控制
- 支持地址分辨协议(ARP)
- PMBus 能力
STM8L051F3的IIC能运行在从机发送、从机接收、主机发送和主机接收四中模式,在默认的情况下工作在从机模式,当产生一个起始条件后,会自动地从从机模式转换为主机模式。IIC模块的框图如下:
STM8L051F3的IIC功能很多,在本章主要介绍常用的主机模式下的发送与接收功能。IIC的输入时钟(系统时钟)在标准模式下要大于1MHz,在快速模式下要大于4MHz。
主机模式下的发送序列如下图:
主模式下的读模式会根据读不同数据大小有不通的程序序列,在IIC用于中断的模式下并拥有高的中断优先级时,读序列如下:
主模式接收的数据N > 2时(N为字节数),读序列如下:
主模式接收的数据N = 2时(N为字节数),读序列如下:
主模式接收的数据N = 1时(N为字节数),读序列如下:
说明:
S = 起始条件,Sr =重复起始条件,P=停止条件,A=应答,NA=不应答
EVX=事件x
EV5:SB=1,读SR1寄存器清除,之后接着往DR寄存器写入地址
EV6:ADDR=1,读SR1寄存器清除,之后接着读SR3
EV6_1:没有相关的标志事件,应答在EV6后应该禁止,这是在ADDRS被清除后发生
EV6_3:ADDR=1,编程ACK=0,在读SR1寄存器后清除接着读SR3寄存器编程STOP=1
EV7:RXNE=1,读DR寄存器清除
EV7_1:RXNE=1,读DR寄存器清除,编程ACK=0和STOP请求
EV7_2:BTF=1,第N-2个数据在DR寄存器,第N-1个数据在转移寄存器,编程ACK=0,在 DR寄存器中读出第N-2个数据,编程STOP=1,读出第N-1个数据
EV7_3:BTF=1,编程STOP=1后连续读DR寄存器两次
EV8_1:转移寄存器空,数据寄存器空,写DR寄存器
EV8:TXE=1,转移寄存器非空,数据寄存器空,写DR寄存器清除
EV8_2:TXE=1,BTF=1,编程停止条件请求,在硬件产生停止条件后自动清除
EV9:ADD10=1,读SR1寄存器清除,接着写DR寄存器
IIC在主模式的通讯中就是严格按照上述的序列进行通讯,理解了每个事件的含义对理解上述IIC通讯的序列有很大帮助。STM8L051F3的编程采用的是官方库函数,相对来说较为容易。
2、OLED显示2.1 IIC配置本小节介绍如何初始化IIC以及实现IIC与OLED进行通讯。OLED采用的是技新0.96寸OLED(4PIN),可以直接插入核心板P6接口。本次实验设计的内容为:点亮0.96’OLED屏,使用OLED显示‘OLED TEST’ 字符串,同时LED1闪烁。使用的例程:STM8L051F3_09_IIC。IIC初始化配置步骤:
1)打开IIC外设时钟
2)使能IIC1
3)配置IIC参数:IIC1、时钟100KHz、IIC模式、快速模式工作周期Tlow/Thigh = 2、使能应答、应答从机地址7位
本章所使用例程主要在于IIC外设的应用,OLED使用的是技新0.96寸OLED(4PIN),相关内容在这里不介绍,用户可以通过技新的0.96'OLED(4Pin)学习手册来了解OLED相关的设计原理与程序应用。同时要注意的是:OLED的IIC通讯地址是0x78、OLED的IIC时序只存在写,不存在读。
OLED的通讯时序如下:
2.2 例程介绍IIC初始化函数在例程的iic.c文件中实现:
void IIC_Init(void)
{ //打开IIC外设时钟 CLK_PeripheralClockConfig(CLK_Peripheral_I2C1, ENABLE); I2C_Cmd(I2C1, ENABLE); //使能IIC外设 /* 配置IIC参数 */ I2C_Init(I2C1, 100000, 0x78, I2C_Mode_I2C, I2C_DutyCycle_2, I2C_Ack_Enable, I2C_AcknowledgedAddress_7bit); }
根据OLED的时序,通过IIC协议对OLED写入命令函数如下(在oled.c文件中实现):
void Write_OLED_Command(unsigned char IIC_Command)
{
//检测总线忙 while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); //产生IIC起始条件 I2C_GenerateSTART(I2C1, ENABLE); //检测IIC的EV5 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); //发送从机器件地址 I2C_Send7bitAddress(I2C1, 0x78, I2C_Direction_Transmitter); //检测IIC的EV6 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); //发送数据(命令) I2C_SendData(I2C1, 0x00); //检测IIC的EV8 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); // 发送命令 I2C_SendData(I2C1, IIC_Command); //检测IIC的EV8 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //产生停止条件 I2C_GenerateSTOP(I2C1, ENABLE); }
根据OLED的时序,通过IIC协议对OLED写入数据函数如下(在oled.c文件中实现):
void Write_OLED_Data(unsigned char IIC_Data)
{
//检测总线忙 while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); //产生IIC起始条件 I2C_GenerateSTART(I2C1, ENABLE); //检测IIC的EV5 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); //发送从机器件地址 I2C_Send7bitAddress(I2C1, 0x78, I2C_Direction_Transmitter); //检测IIC的EV6 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); //发送数据(命令) I2C_SendData(I2C1, 0x40); //检测IIC的EV8 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); // 发送命令 I2C_SendData(I2C1, IIC_Data); //检测IIC的EV8 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //产生停止条件 I2C_GenerateSTOP(I2C1, ENABLE); }
最后主函数如下:
void main(void)
{
LED_Init(); //初始化LED IIC_Init(); //IIC初始化 OLED_Init(); //OLED初始化 OLED_Clear(); //OLED清屏 OLED_ShowString(30,0,"OLED TEST"); //显示 OELD TEST while(1) { delay_ms(300); GPIO_ToggleBits(LED1_GPIO_PORT, LED1_GPIO_PINS); //切换LED1状态 } }
使用ST-LINK把程序下载到开发板,LED1闪烁,OLED显示:OLED TEST。注:对于移植OLED的例程,主需要针对OLED写入数据&写入命令两个函数即可,其他的不需要改动。效果如下:
|