STM32学习之I2C

最新更新时间:2019-04-02来源: eefocus关键字:STM32  I2C 手机看文章 扫描二维码
随时随地手机看文章

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总线的时序、协议、仲裁和定时功能,支持标准和快速传输两种模式,同时与SMBus 2.0兼容。


 

    本实验直接操作寄存器实现对I2C总线结构的EEPROM AT24c02的写入和读取。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/f    f 为想得到的频率 

 

配置好时钟,还需要配置本机地址,I2C支持7位地址和10位地址,这里用的是7位地址:

自身地址寄存器1(I2C_OAR1)[7:1]:接口地址,地址的7~1位。

 

其他相关操作参见代码,有详细注释:(system.h 和 stm32f10x_it.h 等相关代码参照 stm32 直接操作寄存器开发环境配置)

 

User/main.c

01#include
02#include "system.h"
03#include "usart.h" 
04#include "i2c.h"
05
06#define LED1 PAout(4)
07#define LED2 PAout(5)
08#define LED3 PAout(6)
09#define LED4 PAout(7)
10
11void Gpio_Init(void);
12
13int main(void)
14{             
15
16    Rcc_Init(9);                          //系统时钟设置
17
18    Usart1_Init(72,9600);
19
20    Nvic_Init(1,0,I2C1_EV_IRQChannel,4);      //设置抢占优先级为1,响应优先级为0,中断分组为4
21
22    Nvic_Init(0,0,I2C1_ER_IRQChannel,4);      //设置I2C错误中断抢占优先级为0
23
24    Gpio_Init();
25
26    I2c_Init(0x30);                           //设置I2C1地址为0x30                    
27
28    I2c_Start();
29
30    while(1);       
31}
32
33
34void Gpio_Init(void)
35{
36    RCC->APB2ENR |= 1<<2;          //使能PORTA时钟     
37    RCC->APB2ENR |= 1<<3;          //使能PORTB时钟;    
38
39
40    GPIOA->CRL &= 0x0000FFFF;        // PA0~3设置为浮空输入,PA4~7设置为推挽输出
41    GPIOA->CRL |= 0x33334444; 
42
43
44    GPIOB->CRL &= 0x00FFFFFF;        //PB6 I2C1_SCL ,PB7  I2C1_SDL
45    GPIOB->CRL |= 0xFF000000;        //复用开漏输出
46
47    //USART1 串口I/O设置
48
49    GPIOA -> CRH &= 0xFFFFF00F;      //设置USART1 的Tx(PA.9)为第二功能推挽,50MHz;Rx(PA.10)为浮空输入
50    GPIOA -> CRH |= 0x000008B0;    
51
52}

 

User/stm32f10x_it.c

001#include "stm32f10x_it.h"
002#include "system.h"
003#include "stdio.h"
004#include "i2c.h"
005
006#define LED1 PAout(4)
007#define LED2 PAout(5)
008#define LED3 PAout(6)
009#define LED4 PAout(7)
010
011#define  ADDRS_R  0xA1    //读操作地址
012#define  ADDRS_W  0xA0    //写操作地址
013
014u8  go = 0;               //操作步骤标记
015
016void I2C1_EV_IRQHandler(void)     //I2C1 Event Interrupt 
017{
018    u16 clear = 0;
019
020    if(I2C1 -> SR1 & 1<<0 )          //已发送起始条件,写数据寄存器的操作将清除该位
021    {
022        printf("rn I2C1 Start .. rn");
023
024        switch(go)
025        {
026            case 0:{ 
027                I2c_Write(ADDRS_W);        //写入从机地址,写指令操作地址
028                break;
029            }
030            case 1:{
031                I2c_Write(ADDRS_W);        //写入从机地址,写指令操作地址
032                break;
033            }
034            case 2:{
035                I2c_Write(ADDRS_R);        //写入从机地址,读数据操作地址
036                break;
037           }
038        }
039
040    }
041
042    if(I2C1 -> SR1 & 1<<1 )        //从机地址已发送
043    {
044        printf("rn I2C1 has send address .. rn");
045        clear = I2C1 -> SR2; //读取SR2可以清除该位中断
046
047        switch(go)
048        {
049            case 0:{ 
050                I2c_Write(0x01);    //写入待写入的EEPROM单元地址
051                break;
052            }
053
054            case 1:{
055                I2c_Write(0x01);    //写入待写入的EEPROM单元地址
056                break;
057            }
058            case 2:{
059                delay(100000);
060                printf("rn Read 0x%X from At24c02 ,Address 0x01 ..  rn",I2c_Read());
061                I2c_Stop();
062                break;
063           }
064        }
065
066    }
067
068    if(I2C1 -> SR1 & 1<<2 )        //字节发送结束  发送地址字节时,不触发此中断
069    {
070
071        //printf("rn I2C1 send byte success .. rn");
072        switch(go)
073        {
074            case 0:{ 
075                I2c_Write(0x86);            //写入数据
076                printf("rn Write 0x%X to At24c02 ,Address 0x01 ..  rn",0x86);           
077                //I2c_Stop();
078
079                delay(10000);
080                go = 1;
081                I2c_Start(); 
082                break;
083            }
084
085            case 1:{
086
087                delay(10000);
088                go = 2;
089                I2c_Start();
090                break;
091            }
092            case 2:{
093
094                break;
095           }
096        }
097
098    }
099
100    delay(100000);
101    LED3 = 1;
102
103    //I2C1 -> CR2 &= ~(1<<9);          //事件中断关闭
104}
105
106void I2C1_ER_IRQHandler(void)       //I2C1 Error Interrupt 
107{
108    delay(100000);
109    LED4 = 1;   
110
111    if(I2C1->SR1 & 1<<10)          //应答失败
112    {
113        printf("rn ACK ERROR .. rn");
114
115        I2C1->SR1 &=~(1<<10);      //清除中断
116    }
117
118    if(I2C1->SR1 & 1<<14)          //超时
119    {
120        printf("rn Timeout .. rn");
121
122        I2C1->SR1 &=~(1<<14);      //清除中断
123    }
124
125    if(I2C1->SR1 & 1<<11)          //过载/欠载
126    {
127        printf("rn Overrun/Underrun .. rn");
128        I2C1->SR1 &=~(1<<11);      //清除中断
129    }
130
131    if(I2C1->SR1 & 1<<9)           //仲裁丢失
132    {
133        printf("rn Arbitration lost .. rn");
134        I2C1->SR1 &=~(1<<9);       //清除中断
135    }
136
137    if(I2C1->SR1 & 1<<8)           //总线出错
138    {
139        printf("rn Bus error .. rn");
140        I2C1->SR1 &=~(1<<8);       //清除中断
141    }
142
143
144}

Library/src/i2c.c

view sourceprint?

01#include "i2c.h" 
02
03void I2c_Init(u16 Addr )
04{
05
06    RCC -> APB1ENR |= 1<<21;           //打开I2C1时钟
07    //RCC -> APB1ENR |= 1<<22;         //打开I2C2时钟
08
09    RCC->APB1RSTR  |= 1<<21;           //复位I2C1
10    RCC->APB1RSTR  &= ~(1<<21);            //复位结束I2C1
11    //RCC->APB1RSTR  |= 1<<22;         //复位I2C2
12
13    //I2C1 -> CR1 |=  1<<15;               //复位寄存器
14
15    //I2C模块时钟频率,2~36MHz之间
16    I2C1 -> CR2 |=   36 ;                //000000:禁用 000001:禁用 000010:2MHz ... 100100:36MHz
17
18
19    I2C1 -> CCR |= 0<<15;              //I2C主模式  0:标准模式的I2C    1:快速模式的I2C
20    //I2C1 -> CCR |= 1<<14;                //快速模式时的占空比 0 Tlow/Thigh = 2    1   Tlow/Thigh = 16/9
21
22    //得到200kHz频率
23    I2C1 -> CCR |= 90<<0;              //时钟控制分频系数  = PCLK1 /2/f    f 为想得到的频率
24
25    //主模式最大上升时间
26    I2C1 -> TRISE |= 37;             //最大允许SCL上升时间为1000ns,故TRISE[5:0]中必须写入(1us/(1/36)us = 36+1)。
27
28    I2C1 -> CR1 |=  1<<10;             //打开ACK应答,在接收到一个字节后返回一个应答
29    I2C1 -> CR1 |= 1<<6;               //广播呼叫使能
30
31    I2C1 -> OAR1 |= 0<<15;             //寻址模式   1 响应10位地址  0  响应7位地址   
32
33    I2C1 -> OAR1 |= 1<<14;             //必须始终由软件保持为 1
34
35    I2C1 -> OAR1 |=  Addr <<1 ;            //设置接口地址的 7~1位
36
37    //I2C1 -> OAR1 |=  0 ;           //设置10位地址模式时地址第0位 
38    //I2C1 -> OAR1 |= 0<<8;                //设置10位地址模式时地址第9~8位
39
40    //I2C1 -> CR2 |=  1<<10;               //缓冲器中断使能
41    I2C1 -> CR2 |=  1<<9;              //事件中断使能
42    I2C1 -> CR2 |=  1<<8;              //出错中断使能
43
44    I2C1 -> CR1 |=   1<<0;             //开启I2C1
45}
46
47
48void  I2c_Start()
49{
50
51    I2C1 -> CR1 |=   1<<8;             //I2C1产生起始条件
52}
53
54void  I2c_Stop()
55{
56    I2C1 -> CR1 |=   1<<9;             //I2C1产生停止条件
57}
58
59
60void  I2c_Write(u8 data)
61{
62    I2C1 -> DR = data;
63}
64
65u8  I2c_Read()
66{
67    while(!(I2C1 -> SR1 & 1<<6));      //接收到数据标志位
68
69    return I2C1 -> DR;
70}
71
72void  I2c_End()                         //关闭I2C
73{
74    I2C1 -> CR1 &=   ~(1<<0);      
75}

 

Library/inc/i2c.h

 

1#include
2
3void I2c_Init(u16 Addr );   
4
5void  I2c_Start(void);
6void  I2c_Stop(void);
7void  I2c_Write(u8 data);
8u8    I2c_Read(void);
9void  I2c_End(void);

串口接收数据如下:

 

 I2C1 Start .. 

 I2C1 has send address .. 

 Write 0x86 to At24c02 ,Address 0x01 ..  

 I2C1 Start .. 

 I2C1 has send address .. 

 I2C1 Start .. 

 I2C1 has send address .. 

 Read 0x86 from At24c02 ,Address 0x01 ..  

 

 

库函数操作

 

main.c

001#include "stm32f10x.h"
002#include "stdio.h"
003
004#define  PRINTF_ON  1
005
006void RCC_Configuration(void);
007void GPIO_Configuration(void);
008void USART_Configuration(void);
009void I2C_Configuration(void);
010void NVIC_Configuration(void);
011
012
013u8 I2C1_ADDRESS = 0x30;   //7位 I2C 地址
014u8 I2C2_ADDRESS = 0x31;
015
016#define Size 4
017
018vu8 I2C1_Buffer_Tx[Size] = {1,2,3,4};
019vu8 I2C2_Buffer_Rx[Size] = {0};
020
021u32 BufferSize = Size ;
022
023int main(void)
024{
025    RCC_Configuration();
026    GPIO_Configuration();
027    USART_Configuration();
028    I2C_Configuration();
029    NVIC_Configuration();
030
031    I2C_GenerateSTART(I2C1,ENABLE);
032
033    while(1);   
034}
035
036void I2C_Configuration(void)
037{
038    I2C_InitTypeDef I2C_InitStructure;
039
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);
047
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);
055
056
057    I2C_ITConfig(I2C1,I2C_IT_EVT|I2C_IT_BUF,ENABLE);
058    I2C_ITConfig(I2C2,I2C_IT_EVT|I2C_IT_BUF,ENABLE);
059
060    I2C_Cmd(I2C1,ENABLE);
061    I2C_Cmd(I2C2,ENABLE);
062}
063
064void NVIC_Configuration(void)
065{
066    NVIC_InitTypeDef NVIC_InitStructure;
067
068    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
069
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);
075
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);
081}
082
083void GPIO_Configuration(void)
084{
085    GPIO_InitTypeDef    GPIO_InitStructure;
086
087    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
088
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); 
092
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); 
097
098
099    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
100    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;         
101    GPIO_Init(GPIOA , &GPIO_InitStructure); 
102
103    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
104    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;           
105    GPIO_Init(GPIOA , &GPIO_InitStructure); 
106}
107
108void RCC_Configuration(void)
109{
110    /* 定义枚举类型变量 HSEStartUpStatus */
111    ErrorStatus HSEStartUpStatus;
112
113    /* 复位系统时钟设置*/
114    RCC_DeInit();
115    /* 开启HSE*/
116    RCC_HSEConfig(RCC_HSE_ON);
117    /* 等待HSE起振并稳定*/
118    HSEStartUpStatus = RCC_WaitForHSEStartUp();
119    /* 判断HSE起是否振成功,是则进入if()内部 */
120    if(HSEStartUpStatus == SUCCESS)
121    {
122        /* 选择HCLK(AHB)时钟源为SYSCLK 1分频 */
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);
128        /* 设置FLASH延时周期数为2 */
129        FLASH_SetLatency(FLASH_Latency_2);
130        /* 使能FLASH预取缓存 */
131        FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
132        /* 选择锁相环(PLL)时钟源为HSE 1分频,倍频数为9,则PLL输出频率为 8MHz * 9 = 72MHz */
133        RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
134        /* 使能PLL */ 
135        RCC_PLLCmd(ENABLE);
136        /* 等待PLL输出稳定 */
137        while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
138        /* 选择SYSCLK时钟源为PLL */
139        RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
140        /* 等待PLL成为SYSCLK时钟源 */
141        while(RCC_GetSYSCLKSource() != 0x08);
142    } 
143    /* 打开APB2总线上的GPIOA时钟*/
144    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_USART1, ENABLE);
145
146    //RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
147
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);
150
151}
152
153
154void USART_Configuration(void)
155{
156    USART_InitTypeDef USART_InitStructure;
157    USART_ClockInitTypeDef USART_ClockInitStructure;
158
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);
164
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);
172
173    USART_Cmd(USART1,ENABLE);
174}
175
176#if  PRINTF_ON
177
178int fputc(int ch,FILE *f)
179{
180    USART_SendData(USART1,(u8) ch);
181    while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);
182    return ch;
183}
184
185#endif

stm32f10x_it.c


01#include "stm32f10x_it.h"
02#include "stdio.h"
03
04extern u32 BufferSize ;
05extern u8 I2C1_ADDRESS ;
06extern u8 I2C2_ADDRESS ;
07
08extern vu8 I2C1_Buffer_Tx[];
09extern vu8 I2C2_Buffer_Rx[];
10vu32 Tx_Counter = 0;
11vu32 Rx_Counter = 0;
12
13void I2C1_EV_IRQHandler(void)
14{
15    switch(I2C_GetLastEvent(I2C1))
16    {
17        case I2C_EVENT_MASTER_MODE_SELECT: //已发送启始条件
18        {
19            I2C_Send7bitAddress(I2C1,I2C2_ADDRESS,I2C_Direction_Transmitter);   
20            break;
21        }
22        case I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED: //已发送从机地址
23        {
24            printf("rn The I2C1 has send data %d rn",I2C1_Buffer_Tx[Rx_Counter]);
25            I2C_SendData(I2C1,I2C1_Buffer_Tx[Tx_Counter++]);
26            break;
27        }
28        case I2C_EVENT_MASTER_BYTE_TRANSMITTED: //第一个数据已发送
29        {
30            if(Tx_Counter
31            {
32                printf("rn The I2C1 has send data %d rn",I2C1_Buffer_Tx[Rx_Counter]);
33                I2C_SendData(I2C1,I2C1_Buffer_Tx[Tx_Counter++]);                
34
35            }else{
36                I2C_GenerateSTOP(I2C1,ENABLE);
37                I2C_ITConfig(I2C1,I2C_IT_EVT|I2C_IT_BUF,DISABLE);  //计数发送的个数
38            }
39
40            break;
41        }
42        default: {break;}
43    }
44}
45
46
47void I2C2_EV_IRQHandler(void)
48{
49    switch(I2C_GetLastEvent(I2C2))
50    {
51        case I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED: //收到匹配的地址数据
52        {
53            break;
54        }
55        case I2C_EVENT_SLAVE_BYTE_RECEIVED: //收到数据
56        {   
57            if(Rx_Counter < BufferSize )
58            {
59                I2C2_Buffer_Rx[Rx_Counter] = I2C_ReceiveData(I2C2);
60                printf("rn The I2C2 has received data %d rn",I2C2_Buffer_Rx[Rx_Counter++]); //计数收到的个数               
61            }
62            break;
63        }
64        case I2C_EVENT_SLAVE_STOP_DETECTED: //收到结束条件
65        {
66            I2C_ClearFlag(I2C2,I2C_FLAG_STOPF);
67            I2C_ITConfig(I2C1,I2C_IT_EVT|I2C_IT_BUF,DISABLE);
68
69            break;
70        }
71        default: {break;}
72    }
73}


关键字:STM32  I2C 编辑:什么鱼 引用地址:STM32学习之I2C

上一篇:STM32 IIC 详解 之 stm32 IIC 从机模式
下一篇:【STM32CUBEMX】 I2C Slave 实现

推荐阅读最新更新时间:2023-10-12 16:31

STM32输出调试信息-printf重定向到串口
在STM32调试过程中常常需要将调试信息输出到串口,然后通过串口助手查看输出的调试信息。一般来说,串口输出的是指定长度的十六进制字节,对于想打印的调试信息来 说,略显灵活性不足。这时候如果可以将printf重定向到串口输出,则能很好的解决这个问题。 关于printf重定向的方法有很多种,这里只讨论一种我认为相对简单实用的方法。其主要方法步骤如下: 1、配置STM32的串口,确保STM32能输出数据到串口调试助手。 void USART5_Init(void) { USART_InitTypeDef USART_InitStructure; GPIO_InitTypeDef GPIO_InitStructure;
[单片机]
基于STM32单片机对流水灯的控制
1.跟着例程自己创建自己的工程时,出现了GPIO库文件里有undefined等等,在我们刚开始学习新建工程时要注意C/c++里面的Defined,与开发板匹配! 2.在启动文件中选择与开发板匹配的文件,比如hd,ld,md等等只要加入一种,多了会出现错误。 3.终于成功了! 4.出现Flash download failed cortex M3 注意Flashdownload里面关于你芯片Flash匹配。 5.今天终于借助于库开发点亮第一个LED灯,哈哈。 http://blog.csdn.net/hantangsongming/article/details/8139793 http://blog.sina.co
[单片机]
基于<font color='red'>STM32</font>单片机对流水灯的控制
STM32 PWM捕获 两种方法详解
前言: STM32 的TIM的捕获PWM波,是为了频率和占空比,这两种数据结果!它 的最基本的原理就是(打个比方):例如一个高电平上升沿过来,捕获的数值就是上升为的高电平的TIM的计数值(TIM定时器,CNT不断增加,再重新装载),这点是最基本的内容!后面就是通过前后的数值计算可以获得占空比 频率。 第一种方法: 官方的正统方法 IC1和IC2为一组通道,IC3和IC4为一组通道,以上图为例可以得到上升沿是IC1的值为600(假设),此时TIMx_CCR2不变,计数器复位为0,然后在下降沿的时候,IC2读取计数值 即为高电平时间300(假设),当道下一个周期的上升沿时,IC1读取为600,计数器复位为0;
[单片机]
<font color='red'>STM32</font> PWM捕获 两种方法详解
基于STM32的触摸屏学习笔记
本文共有三个内容:一、电阻触摸屏的原理;二、XPT2046的控制字与数字接口;三、程序源码讲解(参考正点原子的代码) 一、电阻触摸屏的原理,上图: 图上的文字介绍了触摸的原理,下面总结一下触摸的原理: 触摸屏工作主要是两个电阻屏(上下两层)在工作,如上图,当某一层电级加上电压时,会在该网络上形成电压梯度。如果有外力使得上下两层在某一点接触,则在未加电压的那一层可以测得接触点的电压,从而得出接触点的坐标(X或Y)。举个例子:当我们在上层的电极间(Y+和Y-)加上电压,则会在上层形成电压梯度(这里读者可以想想AD转换的原理),当有外力使得上下两层在某一点接触时,在底层X层就可以测得接触点处的电压(每个点电压都不同),再根据测得电
[单片机]
基于<font color='red'>STM32</font>的触摸屏学习笔记
STM32 堆和栈的学习(二)
1、首先来看:栈(STACK)的问题 函数的局部变量,都是存放在”栈”里面,栈的英文是:STACK. 所以,如果一个函数的局部变量过多,程序崩溃是很容易的事情,这时候,一般你会进入到hardfault…. 对于栈区,一般栈顶,也就是MSP,在程序刚运行的时候,指向程序所占用内存的最高地址 图中,MSP就是:0X2000 0940. 程序运行后,MSP就是从这个地址开始,往下给函数的局部变量分配地址. 这里的Code,RO-data,RW-data,ZI-data分别代表如下: Code为程序代码部分 RO-data 表示 程序定义的常量const temp; RW-data 表示 已初始化的局部变量 ZI-data
[单片机]
<font color='red'>STM32</font> 堆和栈的学习(二)
STM32关于外部时钟晶振为什么选用8MHz
芯片的主晶振频率范围一般来说在数据手册(Datasheet)和技术参考手册(Technical Reference Manual)中都有介绍。 你提到的时钟先分频再倍频,这个需要深入到STM32的内部去一探究竟了, 在其技术参考手册的第7.2节Clocks的一开始有一个表格,时钟树(Figure 8. Clock tree),它完全地列出来STM32这个芯片内部各个模块的时钟来源以及相关的从属关系。 在这个图的正中央,有一个核心时钟:SYSCLK,它最大频率是72MHz,它的时钟信号通过选择器SW可以由PLLCLK提供(也可由HIS、HSE提供)。 PLL,毫无疑问,它是用来倍频的,可以*2,*3,*4……*16,它
[单片机]
<font color='red'>STM32</font>关于外部时钟晶振为什么选用8MHz
STM32串口使用IDLE中断接收不定长数据原理与源程序
今天说一下STM32单片机的接收不定长度字节数据的方法。由于STM32单片机带IDLE中断,所以利用这个中断,可以接收不定长字节的数据,由于STM32属于ARM单片机,所以这篇文章的方法也适合其他的ARM单片机。 IDLE中断什么时候发生? IDLE就是串口收到一帧数据后,发生的中断。什么是一帧数据呢?比如说给单片机一次发来1个字节,或者一次发来8个字节,这些一次发来的数据,就称为一帧数据,也可以叫做一包数据。 如何判断一帧数据结束,就是我们今天讨论的问题。因为很多项目中都要用到这个,因为只有接收到一帧数据以后,你才可以判断这次收了几个字节和每个字节的内容是否符合协议要求。 看了前面IDLE中断的定义,你就会明白了
[单片机]
<font color='red'>STM32</font>串口使用IDLE中断接收不定长数据原理与源程序
如何配置stm32中断的优先级
写作原由:因为之前有对stm32 优先级做过研究,但是没时间把整理的东西发表,最近项目需要2个串口,但是不是两个串口同时使用,只是随机使用其中一个,程序对2个串口的优先级需要配置; 此文思路:“中断优先级”思维导图--》关键要点---》结合图和要点相关程序应用例程讲解; 我们先来看ST公司的一张图: 我自己依据此图理解,应用思维导图画了一张方便理解:(如果看不清可通过ctrl+鼠标滑轮 放大看;) 前提条件1:组别优先顺序(第0组优先级最强,第4组优先级最弱):NVIC_PriorityGroup_0》NVIC_PriorityGroup_1》NVIC_PriorityGroup_2》NVIC_PriorityGrou
[单片机]
如何配置<font color='red'>stm32</font>中断的优先级
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2023 EEWORLD.com.cn, Inc. All rights reserved