IIC通讯实验
1、实验概述 本实验通过两块LKS32MC081的demo板进行IIC通讯的发送和接收实验。实验使用P1.10作为SCL时钟信号线,P1.11作为SDA数据信号线,将两块081的核心板对应的P1.10与P1.11用杜邦线进行连接。由于IIC通讯需要进行上拉,因此需要在程序中对P1.10与P1.11进行开启上拉。081系列的IIC通讯不使用DMA进行搬运时需要利用IIC的中断进行判断,实现IIC的数据传输。
2、IIC通讯特点

在物理连接上分别由 SDA(串行数据线)和 SCL(串行时钟线)及上拉电阻组成。通信原理是通过对 SCL和 SDA线高低电平时序的控制,来产生 IIC 总线协议所需要的信号进行数据的传递。在总线空闲状态时,这两根线一般被上面所接的上拉电阻拉高,保持着高电平,否则无法输出高电平。
IIC 总线接口连接微控制器和串行 IIC 总线。它提供多主机功能,控制所有IIC总线特定的时序、协议、仲裁和定时。支持标准和快速两种模式。
IIC有以下特点:
① IIC是半双工,而不是全双工;
② IIC是真正的多主机总线,(对比SPI在每次通信前都需要把主机定死,而IIC可以在通讯过程中,改变主机),如果两个或更多的主机同时请求总线,可以通过冲突检测和仲裁防止总线数据被破坏;
③ 起始和终止信号都是由主机发出的,连接到I2C总线上的器件,若具有IIC总线的硬件接口,则很容易检测到起始和终止信号;
④ 在起始信号后必须发送一个7位从机地址+1位方向位,用“0”表示主机发送数据,“1”表示主机接收数据;
⑤ 每当主机向从机发送完一个字节的数据,主机总是需要等待从机给出一个应答信号,以确认从机是否成功接收到了数据;
⑥ 起始信号是必需的,结束信号和应答信号,都可以不要。
注:实际使用中,一般是单片机作为主机,其它器件作为从机,单片机先向器件发送信息表示要读取数据,之后转变传输方向,器件发送数据到单片机。
图2.2 IIC传输时序图
如图2.2所示为IIC基本的传输时序图,空闲状态时SCL与SDA均为高电平,在主机发出START信号后,SCL根据设定的波特率产生时钟信号,SDA首先输出7位从机地址+1位方向位,然后进行8位数据位的传输。需要注意的是,在一个字节传输的 8 个时钟后的第 9 个时钟期间,从机必须回送一个应答位(ACK)给发送器,每个字节后会跟随一个 ACK 信号。ACK bit使得接收者通知主机已经成功接收数据并准备接收下一个数据。当从机响应NACK信号后,判定为传输完成,主机产生STOP信号,完成一组信号的传输。
3、程序配置3.1 主函数配置
在main函数中首先定义了发送数组I2C_TX_BUFF[8]与接收数组I2C_RX_BUFF[8],以及模式选择标志位Test_Pattern,0为发送接收轮流执行,1为仅发送,2为仅接收。
1. u8 I2C_TX_BUFF[8] = {0x75,0x11,0x22,0x88,0x44,0x55,0x66,0x77};
2. u8 I2C_RX_BUFF[8] = {0};
3. u8 Test_Pattern = 2; /*三种模式测试*/
4. int main(void)
5. {
6. Hardware_init();
7. while (1)
8. {
9. if(Test_Pattern == 0)/*发送接收;轮流执行*/
10. {
11. I2C_TX_Function(0x3C,I2C_TX_BUFF,8,I2C_TX_AND_RX);
12. I2C_RX_Function(0x3C,I2C_RX_BUFF,8,I2C_TX_AND_RX);
13. }
14. if(Test_Pattern == 1)/*只发送*/
15. {
16. I2C_TX_Function(0x3C,I2C_TX_BUFF,5,I2C_TX_OR_RX);
17. }
18. if(Test_Pattern == 2)/*只接收*/
19. {
20. I2C_RX_Function(0x3C,I2C_RX_BUFF,5,I2C_TX_OR_RX);
21. }
22. SoftDelay(100);
23. }
24. }
3.2 GPIO配置
实验中使用 P1.10 为 SCL 时钟信号线,P1.11 为 SDA 数据信号线,将P1.10与P1.11配置为输入模式,在复用为IIC功能后,输入和输出模式会由IIC进行接管。
1. void GPIO_init(void)
2. {
3. GPIO_InitTypeDef GPIO_InitStruct;
4. GPIO_StructInit(&GPIO_InitStruct);
5. GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN;
6. GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10 |GPIO_Pin_11;
7. GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;//开启上拉,否则无法输出高电平
8. GPIO_Init (GPIO1,&GPIO_InitStruct);
9. GPIO_PinAFConfig(GPIO1,GPIO_PinSource_10,AF6_I2C ); //p1.10复用功能SCL
10. GPIO_PinAFConfig(GPIO1,GPIO_PinSource_11,AF6_I2C ); //p1.11复用功能SDA
11. }
3.3 IIC初始化配置
IIC硬件地址比较仅在使用DMA模式下开启才有效;初始化时 IIC 发送地址数据不进行配置,后续在发送函数进行配置;初始化主要开启 IIC 主模式使能,和IIC的时钟分频设置;然后就是使能 IIC的各种中断,使用主要用到 NACK,数据传输完成,停止时间(STOP)事件中断使能,总线错误中断。
在主机中将主模式ENABLE,从模式DISABLE,从机中将主模式DISABLE,从模式ENALBE。
1. void I2C_init(u32 div0)
2. {
3. I2C_InitTypeDef I2C_InitStruct;
4. I2C_StructInit(&I2C_InitStruct);
5. I2C_InitStruct.ADRCMP = DISABLE ; // I2C 硬件地址比较使能开关
6. I2C_InitStruct.MST_MODE = ENABLE ; // I2C 主模式使能
7. I2C_InitStruct.SLV_MODE = DISABLE ; // I2C 从模式使能
8. I2C_InitStruct.DMA = DISABLE ; // I2C DMA传输使能
9. I2C_InitStruct.BaudRate = div0 ; // I2C 波特率
10. I2C_InitStruct.IE = ENABLE ; // I2C 中断使能
11. I2C_InitStruct.TC_IE = ENABLE ; // I2C 数据传输完成中断使能
12. I2C_InitStruct.BUS_ERR_IE = DISABLE ; // I2C 总线错误事件中断使能
13. I2C_InitStruct.STOP_IE = ENABLE ; // I2C STOP 事件中断使能
14. I2C_InitStruct.BURST_NACK = ENABLE ; // I2C 传输NACK 事件中断使能
15. I2C_InitStruct.BURST_ADDR_CMP = DISABLE ; // 硬件地址匹配中断使能
16. I2C_Init(I2C, &I2C_InitStruct);
17. I2C_Par.IIC_div_t = div0;//保存IIC波特率设置,在传输错误中断会重新硬件初始化
18. I2C_Par.Idle_RX_Flag = 1;/* I2C接收时检查空闲标志位初始化*/
19. I2C_Par.Idle_TX_Flag = 1;/* I2C发送时检查空闲标志位初始化*/
20. }
3.4 IIC发送函数配置
IIC发送函数最主要就是将 7bit 地址数据移位到高7位,最低位写0操作。对于数组传输的搬运判断处理在IIC中断函数内完成。当发送数据完成后,i2c_delay_txok()返回0,表示总线处于空闲状态,可以进行下一组数据的发送与接收,对所有中间状态变量进行复位操作。
1. u8 I2C_TX_Function(u8 addr, u8 *i2c_data, u32 len, u8 mode)
2. {
3. I2C_Par.Tran_Mode = mode; //发送模式确认
4. if (!i2c_delay_txok()) //等待I2C发送完成
5. {
6. I2C_Par.Data_Length_TX = len;
7. I2C_Par.I2C_DATA_TX = i2c_data;
8. I2C_Par.IIC_ADDR = addr << 1;
9. I2C_Par.Data_Temp_Length_TX = 0;
10. I2C0_DATA = I2C_Par.IIC_ADDR; // 地址信号
11. I2C0_MSCR |= BIT0; // 触发I2C发送地址
12. I2C_Par.I2C_Mode = 0; // 发送模式
13. return 0x0; // 发送成功
14. }
15. else
16. {
17. return 0xff; /*发送失败*/
18. }
19. }
3.5 IIC接收函数配置
接收函数主要功能也是对地址的写入,先对 7bit 地址数据移位后最低为写 1 (表示接收数据),其它操作与发送函数一样。
1. u8 I2C_RX_Function(u8 addr, u8 *i2c_data, u32 len, u8 mode)
2. {
3. I2C_Par.Tran_Mode = mode;
4. if (!i2c_delay_rxok())
5. {
6. I2C_Par.IIC_ADDR = addr << 1;
7. I2C_Par.Data_Length_RX = (len - 1);
8. I2C_Par.I2C_DATA_RX = i2c_data;
9. I2C_Par.Data_Temp_Length_RX = 0;
10. I2C0_DATA = I2C_Par.IIC_ADDR | 0x01; // 地址信号
11. I2C0_MSCR |= BIT0; // 触发I2C发送地址
12. I2C_Par.I2C_Mode = 1; // 接收模式
13. return 0x0; // 发送成功
14. }
15. else
16. {
17. return 0xff; /*发送失败*/
18. }
19. }
3.6 IIC中断函数配置
IIC中断函数读取 I2C_SCR 寄存器,获得当前 I2C 总线状态及当前传输处于什么阶段,判断接下来需要进行的操作。
case0x01:接收模式,当接收未完成时,接收当前字节个数Data_Temp_Length_RX自加1,返回ACK信号给主机;当接收完成时,返回NACK信号给主机。
case0x05:如果第一个数据发送成功从设备返回 ACK 应答信号,则产生发送完成中断,在中断内判断I2C_SCR=0x05。数据触发发送,发送完成且接收到ACK信号,则对 I2C_DATA 写入下一字节发送数据,操作I2C_SCR的BIT2位触发发送数据,直到发送最后一个直接后清零I2C_SCR,硬件自动产生STOP信号。
case0x07:如果任意字节发送接收到NACK信号,就会进入I2C_SCR=0x07;软件对 I2C_SCR清零,然后产生STOP信号结束发送。
case0x09:用来判断数据发送完成且第一个发送字节为地址且接收到ACK应答,然后继续要发送数据第一个字节。若为发送模式,则将I2C_SCR寄存器的第二位DATA_DIR置1,触发发送功能;若为接收模式,将I2C_SCR寄存器的第二位DATA_DIR置0,触发接收功能。
case0x20:根据不同模式对发送状态变量和接收状态变量置1,该变量主要用于在执行 I2C_TX_Function()和 I2C_RX_Function()函数时判断IIC总线是否空闲,如果置1表示总线空闲,可以进行地址数据的发送。
1. void I2C0_IRQHandler(void)
2. {
3. switch (I2C0_SCR)
4. {
5. case 0x01: // byte complete
6. if (I2C_Par.Data_Temp_Length_RX >= I2C_Par.Data_Length_RX)
7. {
8. I2C_Par.I2C_DATA_RX[I2C_Par.Data_Temp_Length_RX] = I2C0_DATA;
9. I2C0_SCR = 0x00; //字节接收完成,返回 NACK 回应
10. break;
11. }
12. else
13. {
14. I2C_Par.I2C_DATA_RX[I2C_Par.Data_Temp_Length_RX] = I2C0_DATA;
15. I2C_Par.Data_Temp_Length_RX += 1;
16. I2C0_SCR = BIT4; //字节接收完成,返回 ACK 回应
17. break; }
18.
19. case 0x05://发送模式 and I2C数据发送完成 且接收到ACK信号
20. if (I2C_Par.Data_Temp_Length_TX >= I2C_Par.Data_Length_TX) //传输最后1Byte数据I2C_Par.Data_Length
21. {
22. I2C0_SCR = 0x00; /*清零ACK,触发STOP信号*/
23. break;
24. }
25. else
26. {
27. I2C0_DATA = I2C_Par.I2C_DATA_TX[I2C_Par.Data_Temp_Length_TX]; //再次发送数据
28. I2C_Par.Data_Temp_Length_TX += 1;
29. I2C0_SCR = BIT2; /*触发I2C发送数据*/
30. break;
31. }
32.
33. case 0x07: // 发送,字节完成和接收到NACK
34. /*发送模式,检查从机返回NACK,该内容根据实际情况进行处理*/
35. I2C0_SCR = 0x00;
36. break;
37.
38. case 0x09: //当前传输为地址数据
39. if (!I2C_Par.I2C_Mode) //发送模式
40. {
41. I2C0_DATA = I2C_Par.I2C_DATA_TX[I2C_Par.Data_Temp_Length_TX]; // transmit first data
42. I2C_Par.Data_Temp_Length_TX += 1;
43. I2C0_SCR = BIT2; /*触发I2C发送数据*/
44. }
45. else
46. { //接收模式
47. I2C0_SCR = BIT0; /*触发I2C接收数据*/
48. }
49. break;
50.
51. case 0x0B: // 从地址不匹配;
52. /*检查从模式地址匹配错误事件,该内容根据实际情况进行处理*/
53. I2C0_SCR = 0x00;
54. break;
55.
56. case 0x20: // stop;
57. if (I2C_Par.Tran_Mode == I2C_TX_OR_RX)
58. {
59. I2C_Par.Idle_TX_Flag = 1;
60. I2C_Par.Idle_RX_Flag = 1;
61. }
62. else if (I2C_Par.Tran_Mode == I2C_TX_AND_RX)
63. {
64. if (!I2C_Par.I2C_Mode) //发送模式
65. {
66. I2C_Par.Idle_RX_Flag = 1;
67. }
68. else
69. {
70. I2C_Par.Idle_TX_Flag = 1;
71. }
72. }
73. I2C0_SCR = 0x00;
74. break;
75. default:
76. I2C_init(I2C_Par.IIC_div_t); //传输错误,复位IIC
77. I2C0_SCR = 0x00;
78. break;
79. }
80. }
1.
4、硬件连接
利用两块LKS081C8T8demo板进行实验验证,一块作为主机发送数据,一块作为从机接收数据,将两块芯片的P1.10与P1.11引脚用杜邦线进行连接。
图4.1 实验硬件连接
5、实验验证
实验波形如下,发送数据数组定义为{0x75,0x11,0x22,0x88,0x44,0x55,0x66,0x77},主函数中发送数组中的前5位,如图5.1所示为示波器的实验波形,1通道为SCL信号,2通道为SDA信号,通过示波器的解码功能对波形进行IIC解码,可以看到接收数据为数组的前5位,实验功能可行。

图5.1 实验波形
---------------------
作者:ajzh
链接:https://bbs.21ic.com/icview-3302494-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。