一、I2C协议
*** I2C 通讯协议(Inter-Integrated Circuit)是由 Phiilps 公司开发的,由于它引脚少,硬件实现简单,可扩展性强,不需USART、CAN 等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路(IC)间的通讯。对于通讯协议,我们以分层的方式来理解,最基本的是把它分为物理层和协议层。物理层规定通讯系统中具有机械、电子功能部分的特性,确保原始数据在物理媒体的传输。协议层主要规定通讯逻辑,统一收发双方的数据打包、解包标准。
1.软件I2C
一般来讲,软件I2C较为方便简单,软件I2C一般是使用GPIO管脚,用软件控制SCL,SDA线输出高低电平,模拟i2c协议的时序。
**起始信号 :当SCL高电平期间,SDA的下降沿为起始信号;
**停止信号 :当SCL高电平期间,SDA的上升沿为停止信号;
**应答信号 :在数据传输完成后第九个时钟脉冲(SCL)期间,SDA为低电平,则为应答信号;
**非应答信号: 在数据传输完成后第九个时钟脉冲(SCL)期间,SDA为高电平,则为非应答信号;
2.硬件I2C
1.I2C输出引脚,应配置为复用开漏输出。
控制通信的频率,标准模式100KHZ,快速模式400KHZ,快速模式下可以选择SCL时钟的占空比,STM32的I2C在APB1总线上。
2.数据逻辑控制,数据移位寄存器的数据来源及目标是数据寄存器(DR)、地址寄存器(OAR)、PEC 寄存器以及 SDA 数据线。
3.整体控制逻辑,控制逻辑的工作模式根据我们配置的“控制寄存器(CR1/CR2)”的参数而改变。在外设工作时,控制逻辑会根据外设的工作状态修改“状态寄存器(SR1 和 SR2)”,我们只要读取这些4.寄存器相关的寄存器位,就可以了解 I2C的工作状态。除此之外,控制逻辑还根据要求,负责控制产生 I2C中断信号、DMA请求及各种 I2C的通讯信号(起始、停止、响应信号等)。
主发送器发送流程及事件说明如下:
(1) 控制产生起始信号(S),当发生起始信号后,它产生事件“EV5”,并会对 SR1 寄
存器的“SB”位置 1,表示起始信号已经发送;
(2) 紧接着发送设备地址并等待应答信号,若有从机应答,则产生事件“EV6”及
“EV8”,这时 SR1 寄存器的“ADDR”位及“TXE”位被置 1,ADDR 为 1 表
示地址已经发送,TXE 为 1表示数据寄存器为空;
(3) 以上步骤正常执行并对 ADDR 位清零后,我们往 I2C 的“数据寄存器 DR”写入
要发送的数据,这时 TXE 位会被重置 0,表示数据寄存器非空,I2C 外设通过
SDA 信号线一位位把数据发送出去后,又会产生“EV8”事件,即 TXE 位被置 1,
重复这个过程,就可以发送多个字节数据了;
(4) 当我们发送数据完成后,控制 I2C 设备产生一个停止信号§,这个时候会产生
EV8_2事件,SR1的 TXE 位及 BTF 位都被置 1,表示通讯结束。
主接收器接收流程及事件说明如下:
(1) 同主发送流程,起始信号(S)是由主机端产生的,控制发生起始信号后,它产生事
件“EV5”,并会对 SR1寄存器的“SB”位置 1,表示起始信号已经发送;
(2) 紧接着发送设备地址并等待应答信号,若有从机应答,则产生事件“EV6”这时
SR1寄存器的“ADDR”位被置 1,表示地址已经发送。
(3) 从机端接收到地址后,开始向主机端发送数据。当主机接收到这些数据后,会产
生“EV7”事件,SR1 寄存器的 RXNE 被置 1,表示接收数据寄存器非空,我们
读取该寄存器后,可对数据寄存器清空,以便接收下一次数据。此时我们可以控
制 I2C 发送应答信号(ACK)或非应答信号(NACK),若应答,则重复以上步骤接收
数据,若非应答,则停止传输;
(4) 发送非应答信号后,产生停止信号§,结束传输。
在发送和接收过程中,有的事件不只是标志了我们上面提到的状态位,还可能同时标
志主机状态之类的状态位,而且读了之后还需要清除标志位,比较复杂。我们可使用
STM32标准库函数来直接检测这些事件的复合标志,降低编程难度。
二、编程实现:每隔2秒钟采集一次温湿度数据,并通过串口发送到上位机
1.AHT20基本参数
2.本次实验我们使用野火串口通信的UART4接发模板
接下来添加修改程序
将主函数改为
#include "delay.h"
#include "usart.h"
#include "bsp_i2c.h"
int main(void)
{
delay_init(); //ÑÓʱº¯Êý³õʼ»¯
uart_init(115200); //´®¿Ú³õʼ»¯Îª115200
IIC_Init();
while(1)
{
printf("¿ªÊ¼²âÁ¿£¬ÇëÉԵȣº");
read_AHT20_once();
delay_ms(1500);
}
}
添加一些子函数
urart.c
#include "sys.h"
#include "usart.h"
//STM32F103ºËÐÄ°åÀý³Ì
//¿âº¯Êý°æ±¾Àý³Ì
/********** mcudev.taobao.com ³öÆ· ********/
//
//Èç¹ûʹÓÃucos,Ôò°üÀ¨ÏÂÃæµÄÍ·Îļþ¼´¿É.
#if SYSTEM_SUPPORT_UCOS
#include "includes.h" //ucos ʹÓÃ
#endif
//
//STM32¿ª·¢°å
//´®¿Ú1³õʼ»¯
//
//
//¼ÓÈëÒÔÏ´úÂë,Ö§³Öprintfº¯Êý,¶ø²»ÐèҪѡÔñuse MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//±ê×¼¿âÐèÒªµÄÖ§³Öº¯Êý
struct __FILE
{
int handle;
};
FILE __stdout;
//¶¨Òå_sys_exit()ÒÔ±ÜÃâʹÓðëÖ÷»úģʽ
void _sys_exit(int x)
{
x = x;
}
//Öض¨Òåfputcº¯Êý
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//Ñ»··¢ËÍ,Ö±µ½·¢ËÍÍê±Ï
USART1->DR = (u8) ch;
return ch;
}
#endif
/*ʹÓÃmicroLibµÄ·½·¨*/
/*
int fputc(int ch, FILE *f)
{
USART_SendData(USART1, (uint8_t) ch);
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {}
return ch;
}
int GetKey (void) {
while (!(USART1->SR & USART_FLAG_RXNE));
return ((int)(USART1->DR & 0x1FF));
}
*/
#if EN_USART1_RX //Èç¹ûʹÄÜÁ˽ÓÊÕ
//´®¿Ú1ÖжϷþÎñ³ÌÐò
//×¢Òâ,¶ÁÈ¡USARTx->SRÄܱÜÃâĪÃûÆäÃîµÄ´íÎó
u8 USART_RX_BUF[USART_REC_LEN]; //½ÓÊÕ»º³å,×î´óUSART_REC_LEN¸ö×Ö½Ú.
//½ÓÊÕ״̬
//bit15£¬ ½ÓÊÕÍê³É±êÖ¾
//bit14£¬ ½ÓÊÕµ½0x0d
//bit13~0£¬ ½ÓÊÕµ½µÄÓÐЧ×Ö½ÚÊýÄ¿
u16 USART_RX_STA=0; //½ÓÊÕ״̬±ê¼Ç
void uart_init(u32 bound){
//GPIO¶Ë¿ÚÉèÖÃ
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //ʹÄÜUSART1£¬GPIOAʱÖÓ
//USART1_TX PA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //¸´ÓÃÍÆÍìÊä³ö
GPIO_Init(GPIOA, &GPIO_InitStructure);
//USART1_RX PA.10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//¸¡¿ÕÊäÈë
GPIO_Init(GPIOA, &GPIO_InitStructure);
//Usart1 NVIC ÅäÖÃ
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//ÇÀÕ¼ÓÅÏȼ¶3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //×ÓÓÅÏȼ¶3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQͨµÀʹÄÜ
NVIC_Init(&NVIC_InitStructure); //¸ù¾ÝÖ¸¶¨µÄ²ÎÊý³õʼ»¯VIC¼Ä´æÆ÷
//USART ³õʼ»¯ÉèÖÃ
USART_InitStructure.USART_BaudRate = bound;//Ò»°ãÉèÖÃΪ9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//×Ö³¤Îª8λÊý¾Ý¸ñʽ
USART_InitStructure.USART_StopBits = USART_StopBits_1;//Ò»¸öֹͣλ
USART_InitStructure.USART_Parity = USART_Parity_No;//ÎÞÆæżУÑéλ
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//ÎÞÓ²¼þÊý¾ÝÁ÷¿ØÖÆ
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //ÊÕ·¢Ä£Ê½
USART_Init(USART1, &USART_InitStructure); //³õʼ»¯´®¿Ú
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//¿ªÆôÖжÏ
USART_Cmd(USART1, ENABLE); //ʹÄÜ´®¿Ú
}
void USART1_IRQHandler(void) //´®¿Ú1ÖжϷþÎñ³ÌÐò
{
u8 Res;
#ifdef OS_TICKS_PER_SEC //Èç¹ûʱÖÓ½ÚÅÄÊý¶¨ÒåÁË,˵Ã÷ҪʹÓÃucosIIÁË.
OSIntEnter();
#endif
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //½ÓÊÕÖжÏ(½ÓÊÕµ½µÄÊý¾Ý±ØÐëÊÇ0x0d 0x0a½áβ)
{
Res =USART_ReceiveData(USART1);//(USART1->DR); //¶ÁÈ¡½ÓÊÕµ½µÄÊý¾Ý
if((USART_RX_STA&0x8000)==0)//½ÓÊÕδÍê³É
{
if(USART_RX_STA&0x4000)//½ÓÊÕµ½ÁË0x0d
{
if(Res!=0x0a)USART_RX_STA=0;//½ÓÊÕ´íÎó,ÖØпªÊ¼
else USART_RX_STA|=0x8000; //½ÓÊÕÍê³ÉÁË
}
else //»¹Ã»ÊÕµ½0X0D
{
if(Res==0x0d)USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//½ÓÊÕÊý¾Ý´íÎó,ÖØпªÊ¼½ÓÊÕ
}
}
}
}
#ifdef OS_TICKS_PER_SEC //Èç¹ûʱÖÓ½ÚÅÄÊý¶¨ÒåÁË,˵Ã÷ҪʹÓÃucosIIÁË.
OSIntExit();
#endif
}
#endif
bsp_i2c.c
#include "bsp_i2c.h"
#include "delay.h"
uint8_t ack_status=0;
uint8_t readByte[6];
uint8_t AHT20_status=0;
uint32_t H1=0; //Humility
uint32_t T1=0; //Temperature
uint8_t AHT20_OutData[4];
uint8_t AHT20sendOutData[10] = {
0xFA, 0x06, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF};
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //ÍÆÍìÊä³ö
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,