stm32i2c通信[操作寄存器+库函数]
作者:
Changing发表时间:07-1222:01分类:电子相关No
Comments
I2C总线是由NXP(原PHILIPS)公司设计,有十分简洁的物理层定义,其特性如下:
只要求两条总线线路:一条串行数据线SDA,一条串行时钟线SCL;
每个连接到总线的器件都可以通过唯一的地址和一直存在的简单的主机/从机关系软件设定地址,主机可以作为主机发送器或主机接收器;
它是一个真正的多主机总线,如果两个或更多主机同时初始化,数据传输可以通过冲突检测和仲裁防止数据被破坏;
串行的8位双向数据传输位速率在标准模式下可达100kbit/s,快速模式下可达400kbit/s,高速模式下可达3.4Mbit/s;
连接到相同总线的IC数量只受到总线的最大电容400pF限制。
其典型的接口连线如下:
I2C的协议很简单:
数据的有效性
在传输数据的时候,SDA线必须在时钟的高电平周期保持稳定,SDA的高或低电平状态只有在SCL线的时钟信号是低电平时才能改变。
起始和停止条件
SCL线是高电平时,SDA线从高电平向低电平切换,这个情况表示起始条件;
SCL线是高电平时,SDA线由低电平向高电平切换,这个情况表示停止条件。
字节格式
发送到SDA线上的每个字节必须为8位,每次传输可以发送的字节数量不受限制。每个字节后必须处理一个响应位。
应答响应
数据传输必须带响应,相关的响应时钟脉冲由主机产生。在响应的时钟脉冲期间发送器释放SDA线(高)。
在响应的时钟脉冲期间,接收器必须将SDA线拉低,使它在这个时钟脉冲的高电平期间保持稳定的低电平。
也就是说主器件发送完一字节数据后要接收一个应答位(低电平),从器件接收完一个字节后要发送一个低电平。
寻址方式(7位地址方式)
第一个字节的头7位组成了从机地址,最低位(LSB)是第8位,它决定了传输的普通的和带重复开始条件的7位地址格式方向。第一个字节的最低位是
“0”,表示主机会写信息到被选中的从机;
“1”表示主机会向从机读信息。
当发送了一个地址后,系统中的每个器件都在起始条件后将头7位与它自己的地址比较,如果一样,器件会判定它被主机寻址,至于是从机接收器还是从机发送器,都由R/W位决定。
仲裁
I2C是所主机总线,每个设备都可以成为主机,但任一时刻只能有一个主机。
stm32至少有一个I2C接口,提供多主机功能,可以实现所有I2C总线的时序、协议、仲裁和定时功能,支持标准和快速传输两种模式,同时与SMBus2.0兼容。
本实验直接操作寄存器实现对I2C总线结构的EEPROMAT24c02的写入和读取。AT24c02相关操作详见单片机读取EEPROM(AT24C02)。
库函数实现使用stm32的两个I2C模拟I2C设备间的数据收发,并通过串口查看数据交换情况。
直接操作寄存器
首先需要配置I2C接口的时钟,相关寄存器如下:
I2C_CR2寄存器低五位:
FREQ[5:0]:I2C模块时钟频率,必须设置正确的输入时钟频率以产生正确的时序,允许的范围在2~36MHz之间:
000000:禁用000001:禁用000010:2MHz...100100:36MHz大于100100:禁用。
用于设置I2C设备的输入时钟,本例使用的是PLCK1总线上的时钟所以为36Mhz;时钟控制寄存器(I2C_CCR)低12位:
CCR[11:0]:快速/标准模式下的时钟控制分频系数(主模式),该分频系数用于设置主模式下的SCL时钟。
在I2C标准模式或SMBus模式下:
Thigh=CCR×TPCLK1
Tlow=CCR×TPCLK1
时钟周期为T=Thigh+Tlow;例如:在标准模式下,FREQR=36即36Mhz,产生200kHz的SCL的频率
时钟控制分频系数=Freqr/2/ff为想得到的频率
配置好时钟,还需要配置本机地址,I2C支持7位地址和10位地址,这里用的是7位地址:
自身地址寄存器1(I2C_OAR1)[7:1]:接口地址,地址的7~1位。
其他相关操作参见代码,有详细注释:(system.h和stm32f10x_it.h等相关代码参照stm32直接操作寄存器开发环境配置)
User/main.c
01 | #include<stm32f10x_lib.h> |
20 | Nvic_Init(1,0,I2C1_EV_IRQChannel,4); //设置抢占优先级为1,响应优先级为0,中断分组为4 |
22 | Nvic_Init(0,0,I2C1_ER_IRQChannel,4); //设置I2C错误中断抢占优先级为0 |
26 | I2c_Init(0x30); //设置I2C1地址为0x30 |
36 | RCC->APB2ENR|=1<<2; //使能PORTA时钟 |
37 | RCC->APB2ENR|=1<<3; //使能PORTB时钟; |
40 | GPIOA->CRL&=0x0000FFFF; //PA0~3设置为浮空输入,PA4~7设置为推挽输出 |
41 | GPIOA->CRL|=0x33334444; |
44 | GPIOB->CRL&=0x00FFFFFF; //PB6I2C1_SCL,PB7I2C1_SDL |
45 | GPIOB->CRL|=0xFF000000; //复用开漏输出 |
49 | GPIOA->CRH&=0xFFFFF00F; //设置USART1的Tx(PA.9)为第二功能推挽,50MHz;Rx(PA.10)为浮空输入
|
50 | GPIOA->CRH|=0x000008B0; |
User/stm32f10x_it.c
001 | #include"stm32f10x_it.h"
|
011 | #defineADDRS_R0xA1//读操作地址
|
012 | #defineADDRS_W0xA0//写操作地址
|
016 | void I2C1_EV_IRQHandler( void ) //I2C1EventInterrupt |
020 | if (I2C1->SR1&1<<0) //已发送起始条件,写数据寄存器的操作将清除该位 |
022 | printf ( "\r\nI2C1Start..\r\n" ); |
027 | I2c_Write(ADDRS_W); //写入从机地址,写指令操作地址 |
031 | I2c_Write(ADDRS_W); //写入从机地址,写指令操作地址 |
035 | I2c_Write(ADDRS_R); //写入从机地址,读数据操作地址 |
042 | if (I2C1->SR1&1<<1) //从机地址已发送 |
044 | printf ( "\r\nI2C1hassendaddress..\r\n" ); |
045 | clear=I2C1->SR2; //读取SR2可以清除该位中断 |
050 | I2c_Write(0x01); //写入待写入的EEPROM单元地址 |
055 | I2c_Write(0x01); //写入待写入的EEPROM单元地址 |
060 | printf ( "\r\nRead0x%XfromAt24c02,Address0x01..\r\n" ,I2c_Read()); |
068 | if (I2C1->SR1&1<<2) //字节发送结束发送地址字节时,不触发此中断 |
071 | //printf("\r\nI2C1sendbytesuccess..\r\n"); |
075 | I2c_Write(0x86); //写入数据 |
076 | printf ( "\r\nWrite0x%XtoAt24c02,Address0x01..\r\n" ,0x86); |
103 | //I2C1->CR2&=~(1<<9);//事件中断关闭
|
106 | void I2C1_ER_IRQHandler( void ) //I2C1ErrorInterrupt |
111 | if (I2C1->SR1&1<<10) //应答失败 |
113 | printf ( "\r\nACKERROR..\r\n" ); |
115 | I2C1->SR1&=~(1<<10); //清除中断 |
118 | if (I2C1->SR1&1<<14) //超时 |
120 | printf ( "\r\nTimeout..\r\n" ); |
122 | I2C1->SR1&=~(1<<14); //清除中断 |
125 | if (I2C1->SR1&1<<11) //过载/欠载 |
127 | printf ( "\r\nOverrun/Underrun..\r\n" ); |
128 | I2C1->SR1&=~(1<<11); //清除中断 |
131 | if (I2C1->SR1&1<<9) //仲裁丢失 |
133 | printf ( "\r\nArbitrationlost..\r\n" ); |
134 | I2C1->SR1&=~(1<<9); //清除中断 |
137 | if (I2C1->SR1&1<<8) //总线出错 |
139 | printf ( "\r\nBuserror..\r\n" ); |
140 | I2C1->SR1&=~(1<<8); //清除中断 |
Library/src/i2c.c
viewsource
print?
06 | RCC->APB1ENR|=1<<21; //打开I2C1时钟 |
07 | //RCC->APB1ENR|=1<<22;//打开I2C2时钟
|
09 | RCC->APB1RSTR|=1<<21; //复位I2C1 |
10 | RCC->APB1RSTR&=~(1<<21); //复位结束I2C1 |
11 | //RCC->APB1RSTR|=1<<22;//复位I2C2
|
13 | //I2C1->CR1|=1<<15;//复位寄存器
|
16 | I2C1->CR2|=36; //000000:禁用000001:禁用000010:2MHz...100100:36MHz
|
19 | I2C1->CCR|=0<<15; //I2C主模式0:标准模式的I2C1:快速模式的I2C |
20 | //I2C1->CCR|=1<<14;//快速模式时的占空比0Tlow/Thigh=21Tlow/Thigh=16/9
|
23 | I2C1->CCR|=90<<0; //时钟控制分频系数=PCLK1/2/ff为想得到的频率 |
26 | I2C1->TRISE|=37; //最大允许SCL上升时间为1000ns,故TRISE[5:0]中必须写入(1us/(1/36)us=36+1)。
|
28 | I2C1->CR1|=1<<10; //打开ACK应答,在接收到一个字节后返回一个应答 |
29 | I2C1->CR1|=1<<6; //广播呼叫使能 |
31 | I2C1->OAR1|=0<<15; //寻址模式1响应10位地址0响应7位地址 |
33 | I2C1->OAR1|=1<<14; //必须始终由软件保持为1 |
35 | I2C1->OAR1|=Addr<<1; //设置接口地址的7~1位 |
37 | //I2C1->OAR1|=0;//设置10位地址模式时地址第0位
|
38 | //I2C1->OAR1|=0<<8;//设置10位地址模式时地址第9~8位
|
40 | //I2C1->CR2|=1<<10;//缓冲器中断使能
|
41 | I2C1->CR2|=1<<9; //事件中断使能 |
42 | I2C1->CR2|=1<<8; //出错中断使能 |
44 | I2C1->CR1|=1<<0; //开启I2C1 |
51 | I2C1->CR1|=1<<8; //I2C1产生起始条件 |
56 | I2C1->CR1|=1<<9; //I2C1产生停止条件 |
67 | while (!(I2C1->SR1&1<<6)); //接收到数据标志位 |
Library/inc/i2c.h
1 | #include<stm32f10x_lib.h>
|
串口接收数据如下:
I2C1Start..
I2C1hassendaddress..
Write0x86toAt24c02,Address0x01..
I2C1Start..
I2C1hassendaddress..
I2C1Start..
I2C1hassendaddress..
Read0x86fromAt24c02,Address0x01..
库函数操作
main.c
006 | void RCC_Configuration( void ); |
007 | void GPIO_Configuration( void ); |
008 | void USART_Configuration( void ); |
009 | void I2C_Configuration( void ); |
010 | void NVIC_Configuration( void ); |
013 | u8I2C1_ADDRESS=0x30; //7位I2C地址
|
018 | vu8I2C1_Buffer_Tx[Size]={1,2,3,4}; |
019 | vu8I2C2_Buffer_Rx[Size]={0};
|
027 | USART_Configuration(); |
031 | I2C_GenerateSTART(I2C1,ENABLE); |
036 | void I2C_Configuration( void ) |
038 | I2C_InitTypeDefI2C_InitStructure; |
040 | I2C_InitStructure.I2C_Mode=I2C_Mode_I2C; |
041 | I2C_InitStructure.I2C_DutyCycle=I2C_DutyCycle_2; |
042 | I2C_InitStructure.I2C_OwnAddress1=I2C1_ADDRESS; |
043 | I2C_InitStructure.I2C_Ack=I2C_Ack_Enable; |
044 | I2C_InitStructure.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit; |
045 | I2C_InitStructure.I2C_ClockSpeed=200000; |
046 | I2C_Init(I2C1,&I2C_InitStructure); |
048 | I2C_InitStructure.I2C_Mode=I2C_Mode_I2C; |
049 | I2C_InitStructure.I2C_DutyCycle=I2C_DutyCycle_2; |
050 | I2C_InitStructure.I2C_OwnAddress1=I2C2_ADDRESS; |
051 | I2C_InitStructure.I2C_Ack=I2C_Ack_Enable; |
052 | I2C_InitStructure.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit; |
053 | I2C_InitStructure.I2C_ClockSpeed=200000; |
054 | I2C_Init(I2C2,&I2C_InitStructure); |
057 | I2C_ITConfig(I2C1,I2C_IT_EVT|I2C_IT_BUF,ENABLE); |
058 | I2C_ITConfig(I2C2,I2C_IT_EVT|I2C_IT_BUF,ENABLE); |
064 | void NVIC_Configuration( void ) |
066 | NVIC_InitTypeDefNVIC_InitStructure; |
068 | NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); |
070 | NVIC_InitStructure.NVIC_IRQChannel=I2C1_EV_IRQn; |
071 | NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1; |
072 | NVIC_InitStructure.NVIC_IRQChannelSubPriority=0; |
073 | NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; |
074 | NVIC_Init(&NVIC_InitStructure); |
076 | NVIC_InitStructure.NVIC_IRQChannel=I2C2_EV_IRQn; |
077 | NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0; |
078 | NVIC_InitStructure.NVIC_IRQChannelSubPriority=0; |
079 | NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; |
080 | NVIC_Init(&NVIC_InitStructure); |
083 | void GPIO_Configuration( void ) |
085 | GPIO_InitTypeDefGPIO_InitStructure; |
087 | GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; |
089 | GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7; |
090 | GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_OD; |
091 | GPIO_Init(GPIOB,&GPIO_InitStructure); |
093 | GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10|GPIO_Pin_11; |
094 | GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; |
095 | GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_OD; |
096 | GPIO_Init(GPIOB,&GPIO_InitStructure); |
099 | GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9; |
100 | GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; |
101 | GPIO_Init(GPIOA,&GPIO_InitStructure); |
103 | GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10; |
104 | GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; |
105 | GPIO_Init(GPIOA,&GPIO_InitStructure); |
108 | void RCC_Configuration( void ) |
110 | /*定义枚举类型变量HSEStartUpStatus*/ |
111 | ErrorStatusHSEStartUpStatus; |
116 | RCC_HSEConfig(RCC_HSE_ON); |
118 | HSEStartUpStatus=RCC_WaitForHSEStartUp(); |
119 | /*判断HSE起是否振成功,是则进入if()内部*/ |
120 | if (HSEStartUpStatus==SUCCESS) |
122 | /*选择HCLK(AHB)时钟源为SYSCLK1分频*/ |
123 | RCC_HCLKConfig(RCC_SYSCLK_Div1); |
124 | /*选择PCLK2时钟源为HCLK(AHB)1分频*/ |
125 | RCC_PCLK2Config(RCC_HCLK_Div1); |
126 | /*选择PCLK1时钟源为HCLK(AHB)2分频*/ |
127 | RCC_PCLK1Config(RCC_HCLK_Div2); |
129 | FLASH_SetLatency(FLASH_Latency_2); |
131 | FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); |
132 | /*选择锁相环(PLL)时钟源为HSE1分频,倍频数为9,则PLL输出频率为8MHz*9=72MHz*/ |
133 | RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9); |
137 | while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY)==RESET); |
139 | RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); |
141 | while (RCC_GetSYSCLKSource()!=0x08); |
144 | RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_USART1,ENABLE); |
146 | //RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE); |
148 | RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1|RCC_APB1Periph_I2C2,ENABLE); |
149 | //RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR|RCC_APB1Periph_BKP|RCC_APB1Periph_WWDG|RCC_APB1Periph_SPI2,ENABLE); |
154 | void USART_Configuration( void ) |
156 | USART_InitTypeDefUSART_InitStructure; |
157 | USART_ClockInitTypeDefUSART_ClockInitStructure; |
159 | USART_ClockInitStructure.USART_Clock=USART_Clock_Disable; |
160 | USART_ClockInitStructure.USART_CPOL=USART_CPOL_Low; |
161 | USART_ClockInitStructure.USART_CPHA=USART_CPHA_2Edge; |
162 | USART_ClockInitStructure.USART_LastBit=USART_LastBit_Disable; |
163 | USART_ClockInit(USART1,&USART_ClockInitStructure); |
165 | USART_InitStructure.USART_BaudRate=9600; |
166 | USART_InitStructure.USART_WordLength=USART_WordLength_8b; |
167 | USART_InitStructure.USART_StopBits=USART_StopBits_1; |
168 | USART_InitStructure.USART_Parity=USART_Parity_No; |
169 | USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None; |
170 | USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx; |
171 | USART_Init(USART1,&USART_InitStructure); |
173 | USART_Cmd(USART1,ENABLE); |
178 | int fputc ( int
ch, FILE *f)
|
180 | USART_SendData(USART1,(u8)ch); |
181 | while (USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET); |
stm32f10x_it.c
viewsource
print?
01 | #include"stm32f10x_it.h"
|
08 | extern vu8I2C1_Buffer_Tx[];
|
09 | extern vu8I2C2_Buffer_Rx[];
|
13 | void I2C1_EV_IRQHandler( void ) |
15 | switch (I2C_GetLastEvent(I2C1)) |
17 | case
I2C_EVENT_MASTER_MODE_SELECT: //已发送启始条件 |
19 | I2C_Send7bitAddress(I2C1,I2C2_ADDRESS,I2C_Direction_Transmitter); |
22 | case
I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED: //已发送从机地址 |
24 | printf ( "\r\nTheI2C1hassenddata%d\r\n" ,I2C1_Buffer_Tx[Rx_Counter]); |
25 | I2C_SendData(I2C1,I2C1_Buffer_Tx[Tx_Counter++]); |
28 | case
I2C_EVENT_MASTER_BYTE_TRANSMITTED: //第一个数据已发送 |
30 | if (Tx_Counter<BufferSize) |
32 | printf ( "\r\nTheI2C1hassenddata%d\r\n" ,I2C1_Buffer_Tx[Rx_Counter]); |
33 | I2C_SendData(I2C1,I2C1_Buffer_Tx[Tx_Counter++]); |
36 | I2C_GenerateSTOP(I2C1,ENABLE); |
37 | I2C_ITConfig(I2C1,I2C_IT_EVT|I2C_IT_BUF,DISABLE); //计数发送的个数 |
47 | void I2C2_EV_IRQHandler( void ) |
49 | switch (I2C_GetLastEvent(I2C2)) |
51 | case
I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED: //收到匹配的地址数据 |
55 | case
I2C_EVENT_SLAVE_BYTE_RECEIVED: //收到数据 |
57 | if (Rx_Counter<BufferSize) |
59 | I2C2_Buffer_Rx[Rx_Counter]=I2C_ReceiveData(I2C2); |
60 | printf ( "\r\nTheI2C2hasreceiveddata%d\r\n" ,I2C2_Buffer_Rx[Rx_Counter++]); //计数收到的个数 |
64 | case
I2C_EVENT_SLAVE_STOP_DETECTED: //收到结束条件 |
66 | I2C_ClearFlag(I2C2,I2C_FLAG_STOPF); |
67 | I2C_ITConfig(I2C1,I2C_IT_EVT|I2C_IT_BUF,DISABLE); |
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理