stm32i2c通信[操作寄存器+库函数]
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相关操作详见。
库函数实现使⽤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等相关代码参照)
Ur/main.c
#include
#include"system.h"
#include"usart.h"
#include"i2c.h"
#defineLED1PAout(4)
#defineLED2PAout(5)
#defineLED3PAout(6)
#defineLED4PAout(7)
voidGpio_Init(void);
intmain(void)
{
Rcc_Init(9);//系统时钟设置
Usart1_Init(72,9600);
Nvic_Init(1,0,I2C1_EV_IRQChannel,4);//设置抢占优先级为1,响应优先级为0,中断分组为4
Nvic_Init(0,0,I2C1_ER_IRQChannel,4);//设置I2C错误中断抢占优先级为0
Gpio_Init();
I2c_Init(0x30);//设置I2C1地址为0x30
I2c_Start();
while(1);
}
voidGpio_Init(void)
{
RCC->APB2ENR|=1<<2;//使能PORTA时钟
RCC->APB2ENR|=1<<3;//使能PORTB时钟;
GPIOA->CRL&=0x0000FFFF;//PA0~3设置为浮空输⼊,PA4~7设置为推挽输出
GPIOA->CRL|=0x33334444;
GPIOB->CRL&=0x00FFFFFF;//PB6I2C1_SCL,PB7I2C1_SDL
GPIOB->CRL|=0xFF000000;//复⽤开漏输出
//USART1串⼝I/O设置
GPIOA->CRH&=0xFFFFF00F;//设置USART1的Tx(PA.9)为第⼆功能推挽,50MHz;Rx(PA.10)为浮空输⼊
GPIOA->CRH|=0x000008B0;
}
Ur/stm32f10x_it.c
#include"stm32f10x_it.h"
#include"system.h"
#include"stdio.h"
#include"i2c.h"
#defineLED1PAout(4)
#defineLED2PAout(5)
#defineLED3PAout(6)
#defineLED4PAout(7)
#defineLED4PAout(7)
#defineADDRS_R0xA1//读操作地址
#defineADDRS_W0xA0//写操作地址
u8go=0;//操作步骤标记
voidI2C1_EV_IRQHandler(void)//I2C1EventInterrupt
{
u16clear=0;
if(I2C1->SR1&1<<0)//已发送起始条件,写数据寄存器的操作将清除该位
{
printf("rnI2C1Start..rn");
switch(go)
{
ca0:{
I2c_Write(ADDRS_W);//写⼊从机地址,写指令操作地址
break;
}
ca1:{
I2c_Write(ADDRS_W);//写⼊从机地址,写指令操作地址
break;
}
ca2:{
I2c_Write(ADDRS_R);//写⼊从机地址,读数据操作地址
break;
}
}
}
if(I2C1->SR1&1<<1)//从机地址已发送
{
printf("rnI2C1hasndaddress..rn");
clear=I2C1->SR2;//读取SR2可以清除该位中断
switch(go)
{
ca0:{
I2c_Write(0x01);//写⼊待写⼊的EEPROM单元地址
break;
}
ca1:{
I2c_Write(0x01);//写⼊待写⼊的EEPROM单元地址
break;
}
ca2:{
delay(100000);
printf("rnRead0x%XfromAt24c02,Address0x01..rn",I2c_Read());
I2c_Stop();
break;
}
}
}
if(I2C1->SR1&1<<2)//字节发送结束发送地址字节时,不触发此中断
{
//printf("rnI2C1ndbytesuccess..rn");
switch(go)
{
ca0:{
ca0:{
I2c_Write(0x86);//写⼊数据
printf("rnWrite0x%XtoAt24c02,Address0x01..rn",0x86);
//I2c_Stop();
delay(10000);
go=1;
I2c_Start();
break;
}
ca1:{
delay(10000);
go=2;
I2c_Start();
break;
}
ca2:{
break;
}
}
}
delay(100000);
LED3=1;
//I2C1->CR2&=~(1<<9);//事件中断关闭
}
voidI2C1_ER_IRQHandler(void)//I2C1ErrorInterrupt
{
delay(100000);
LED4=1;
if(I2C1->SR1&1<<10)//应答失败
{
printf("rnACKERROR..rn");
I2C1->SR1&=~(1<<10);//清除中断
}
if(I2C1->SR1&1<<14)//超时
{
printf("rnTimeout..rn");
I2C1->SR1&=~(1<<14);//清除中断
}
if(I2C1->SR1&1<<11)//过载/⽋载
{
printf("rnOverrun/Underrun..rn");
I2C1->SR1&=~(1<<11);//清除中断
}
if(I2C1->SR1&1<<9)//仲裁丢失
{
printf("rnArbitrationlost..rn");
I2C1->SR1&=~(1<<9);//清除中断
}
if(I2C1->SR1&1<<8)//总线出错
{
printf("rnBurror..rn");
printf("rnBurror..rn");
I2C1->SR1&=~(1<<8);//清除中断
}
}
Library/src/i2c.c
#include"i2c.h"
voidI2c_Init(u16Addr)
{
RCC->APB1ENR|=1<<21;//打开I2C1时钟
//RCC->APB1ENR|=1<<22;//打开I2C2时钟
RCC->APB1RSTR|=1<<21;//复位I2C1
RCC->APB1RSTR&=~(1<<21);//复位结束I2C1
//RCC->APB1RSTR|=1<<22;//复位I2C2
//I2C1->CR1|=1<<15;//复位寄存器
//I2C模块时钟频率,2~36MHz之间
I2C1->CR2|=36;//000000:禁⽤000001:禁⽤000010:2MHz...100100:36MHz
I2C1->CCR|=0<<15;//I2C主模式0:标准模式的I2C1:快速模式的I2C
//I2C1->CCR|=1<<14;//快速模式时的占空⽐0Tlow/Thigh=21Tlow/Thigh=16/9
//得到200kHz频率
I2C1->CCR|=90<<0;//时钟控制分频系数=PCLK1/2/ff为想得到的频率
//主模式最⼤上升时间
I2C1->TRISE|=37;//最⼤允许SCL上升时间为1000ns,故TRISE[5:0]中必须写⼊(1us/(1/36)us=36+1)。
I2C1->CR1|=1<<10;//打开ACK应答,在接收到⼀个字节后返回⼀个应答
I2C1->CR1|=1<<6;//⼴播呼叫使能
I2C1->OAR1|=0<<15;//寻址模式1响应10位地址0响应7位地址
I2C1->OAR1|=1<<14;//必须始终由软件保持为1
I2C1->OAR1|=Addr<<1;//设置接⼝地址的7~1位
//I2C1->OAR1|=0;//设置10位地址模式时地址第0位
//I2C1->OAR1|=0<<8;//设置10位地址模式时地址第9~8位
//I2C1->CR2|=1<<10;//缓冲器中断使能
I2C1->CR2|=1<<9;//事件中断使能
I2C1->CR2|=1<<8;//出错中断使能
I2C1->CR1|=1<<0;//开启I2C1
}
voidI2c_Start()
{
I2C1->CR1|=1<<8;//I2C1产⽣起始条件
}
voidI2c_Stop()
{
I2C1->CR1|=1<<9;//I2C1产⽣停⽌条件
}
voidI2c_Write(u8data)
{
I2C1->DR=data;
}
u8I2c_Read()
{
while(!(I2C1->SR1&1<<6));//接收到数据标志位
returnI2C1->DR;
}
voidI2c_End()//关闭I2C
{
I2C1->CR1&=~(1<<0);
}
Library/inc/i2c.h
#include
voidI2c_Init(u16Addr);
voidI2c_Start(void);
voidI2c_Stop(void);
voidI2c_Write(u8data);
u8I2c_Read(void);
voidI2c_End(void);
串⼝接收数据如下:
I2C1Start..
I2C1hasndaddress..
Write0x86toAt24c02,Address0x01..
I2C1Start..
I2C1hasndaddress..
I2C1Start..
I2C1hasndaddress..
Read0x86fromAt24c02,Address0x01..
库函数操作
main.c
#include"stm32f10x.h"
#include"stdio.h"
#definePRINTF_ON1
voidRCC_Configuration(void);
voidGPIO_Configuration(void);
voidUSART_Configuration(void);
voidI2C_Configuration(void);
voidNVIC_Configuration(void);
u8I2C1_ADDRESS=0x30;//7位I2C地址
u8I2C2_ADDRESS=0x31;
u8I2C2_ADDRESS=0x31;
#defineSize4
vu8I2C1_Buffer_Tx[Size]={1,2,3,4};
vu8I2C2_Buffer_Rx[Size]={0};
u32BufferSize=Size;
intmain(void)
{
RCC_Configuration();
GPIO_Configuration();
USART_Configuration();
I2C_Configuration();
NVIC_Configuration();
I2C_GenerateSTART(I2C1,ENABLE);
while(1);
}
voidI2C_Configuration(void)
{
I2C_InitTypeDefI2C_InitStructure;
I2C_InitStructure.I2C_Mode=I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle=I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1=I2C1_ADDRESS;
I2C_InitStructure.I2C_Ack=I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed=200000;
I2C_Init(I2C1,&I2C_InitStructure);
I2C_InitStructure.I2C_Mode=I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle=I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1=I2C2_ADDRESS;
I2C_InitStructure.I2C_Ack=I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed=200000;
I2C_Init(I2C2,&I2C_InitStructure);
I2C_ITConfig(I2C1,I2C_IT_EVT|I2C_IT_BUF,ENABLE);
I2C_ITConfig(I2C2,I2C_IT_EVT|I2C_IT_BUF,ENABLE);
I2C_Cmd(I2C1,ENABLE);
I2C_Cmd(I2C2,ENABLE);
}
voidNVIC_Configuration(void)
{
NVIC_InitTypeDefNVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC__IRQChannel=I2C1_EV_IRQn;
NVIC__IRQChannelPreemptionPriority=1;
NVIC__IRQChannelSubPriority=0;
NVIC__IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC__IRQChannel=I2C2_EV_IRQn;
NVIC__IRQChannelPreemptionPriority=0;
NVIC__IRQChannelSubPriority=0;
NVIC__IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
voidGPIO_Configuration(void)
{
GPIO_InitTypeDefGPIO_InitStructure;
GPIO__Speed=GPIO_Speed_50MHz;
GPIO__Pin=GPIO_Pin_6|GPIO_Pin_7;
GPIO__Mode=GPIO_Mode_AF_OD;
GPIO_Init(GPIOB,&GPIO_InitStructure);
GPIO__Pin=GPIO_Pin_10|GPIO_Pin_11;
GPIO__Speed=GPIO_Speed_50MHz;
GPIO__Mode=GPIO_Mode_AF_OD;
GPIO_Init(GPIOB,&GPIO_InitStructure);
GPIO__Pin=GPIO_Pin_9;
GPIO__Mode=GPIO_Mode_AF_PP;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO__Pin=GPIO_Pin_10;
GPIO__Mode=GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA,&GPIO_InitStructure);
}
voidRCC_Configuration(void)
{
/*定义枚举类型变量HSEStartUpStatus*/
ErrorStatusHSEStartUpStatus;
/*复位系统时钟设置*/
RCC_DeInit();
/*开启HSE*/
RCC_HSEConfig(RCC_HSE_ON);
/*等待HSE起振并稳定*/
HSEStartUpStatus=RCC_WaitForHSEStartUp();
/*判断HSE起是否振成功,是则进⼊if()内部*/
if(HSEStartUpStatus==SUCCESS)
{
/*选择HCLK(AHB)时钟源为SYSCLK1分频*/
RCC_HCLKConfig(RCC_SYSCLK_Div1);
/*选择PCLK2时钟源为HCLK(AHB)1分频*/
RCC_PCLK2Config(RCC_HCLK_Div1);
/*选择PCLK1时钟源为HCLK(AHB)2分频*/
RCC_PCLK1Config(RCC_HCLK_Div2);
/*设置FLASH延时周期数为2*/
FLASH_SetLatency(FLASH_Latency_2);
/*使能FLASH预取缓存*/
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
/*选择锁相环(PLL)时钟源为HSE1分频,倍频数为9,则PLL输出频率为8MHz*9=72MHz*/
RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9);
/*使能PLL*/
RCC_PLLCmd(ENABLE);
/*等待PLL输出稳定*/
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY)==RESET);
/*选择SYSCLK时钟源为PLL*/
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
/*等待PLL成为SYSCLK时钟源*/
while(RCC_GetSYSCLKSource()!=0x08);
}
/*打开APB2总线上的GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_USART1,ENABLE);
//RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1|RCC_APB1Periph_I2C2,ENABLE);
//RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR|RCC_APB1Periph_BKP|RCC_APB1Periph_WWDG|RCC_APB1Periph_SPI2,ENABLE);
}
voidUSART_Configuration(void)
{
USART_InitTypeDefUSART_InitStructure;
USART_ClockInitTypeDefUSART_ClockInitStructure;
USART__Clock=USART_Clock_Disable;
USART__CPOL=USART_CPOL_Low;
USART__CPHA=USART_CPHA_2Edge;
USART__LastBit=USART_LastBit_Disable;
USART_ClockInit(USART1,&USART_ClockInitStructure);
USART__BaudRate=9600;
USART__WordLength=USART_WordLength_8b;
USART__StopBits=USART_StopBits_1;
USART__Parity=USART_Parity_No;
USART__HardwareFlowControl=USART_HardwareFlowControl_None;
USART__Mode=USART_Mode_Rx|USART_Mode_Tx;
USART_Init(USART1,&USART_InitStructure);
USART_Cmd(USART1,ENABLE);
}
#ifPRINTF_ON
intfputc(intch,FILE*f)
{
USART_SendData(USART1,(u8)ch);
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET);
returnch;
}
#endif
stm32f10x_it.c
#include"stm32f10x_it.h"
#include"stdio.h"
externu32BufferSize;
externu8I2C1_ADDRESS;
externu8I2C2_ADDRESS;
externvu8I2C1_Buffer_Tx[];
externvu8I2C2_Buffer_Rx[];
vu32Tx_Counter=0;
vu32Rx_Counter=0;
voidI2C1_EV_IRQHandler(void)
{
switch(I2C_GetLastEvent(I2C1))
{
caI2C_EVENT_MASTER_MODE_SELECT://已发送启始条件
{
I2C_Send7bitAddress(I2C1,I2C2_ADDRESS,I2C_Direction_Transmitter);
break;
}
caI2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED://已发送从机地址
{
printf("rnTheI2C1hasnddata%drn",I2C1_Buffer_Tx[Rx_Counter]);
I2C_SendData(I2C1,I2C1_Buffer_Tx[Tx_Counter++]);
break;
}
caI2C_EVENT_MASTER_BYTE_TRANSMITTED://第⼀个数据已发送
{
if(Tx_Counter
{
printf("rnTheI2C1hasnddata%drn",I2C1_Buffer_Tx[Rx_Counter]);
I2C_SendData(I2C1,I2C1_Buffer_Tx[Tx_Counter++]);
}el{
I2C_GenerateSTOP(I2C1,ENABLE);
I2C_ITConfig(I2C1,I2C_IT_EVT|I2C_IT_BUF,DISABLE);//计数发送的个数
}
break;
}
default:{break;}
}
}
voidI2C2_EV_IRQHandler(void)
{
switch(I2C_GetLastEvent(I2C2))
{
caI2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED://收到匹配的地址数据
{
break;
}
caI2C_EVENT_SLAVE_BYTE_RECEIVED://收到数据
{
if(Rx_Counter
{
I2C2_Buffer_Rx[Rx_Counter]=I2C_ReceiveData(I2C2);
printf("rnTheI2C2hasreceiveddata%drn",I2C2_Buffer_Rx[Rx_Counter++]);//计数收到的个数
}
break;
}
caI2C_EVENT_SLAVE_STOP_DETECTED://收到结束条件
{
I2C_ClearFlag(I2C2,I2C_FLAG_STOPF);
I2C_ITConfig(I2C1,I2C_IT_EVT|I2C_IT_BUF,DISABLE);
break;
}
default:{break;}
}
}
相关⽂章
本文发布于:2022-12-28 04:58:51,感谢您对本站的认可!
本文链接:http://www.wtabcd.cn/fanwen/fan/90/44765.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |