AM2320是一款具有I2C、单总线通信的温湿度传感器,精度也比较高,适合日常的一些应用场景。对于单总线通信,已经有很多人分享其实现的代码了,但多是使用延时法进行读取,今天给大家带来一个通过单总线定时器中断方式读取。
初始化代码
首先是需要进行IO口初始化,单总线通信时SCL引脚需要接地,并使用SDA引脚作为单总线通信引脚。此处将SCL使用GPIO进行拉低,SDA选择连接在GPIOB,GPIO_Pin_9这个定时器4的CH4上。
Am2320_SdaOutMode 输出模式 用于发起通信起始信号。
Am2320_SdaInMode 输入模式 用于接收总线数据,此处将定时器的基准时间设置为1us,方便捕获中断时长用于计算0/1。并开启定时器更新中断作为通信超时结束控制,开启定时器的捕获中断作为通信时采集。
//AM2320 IO口
#define AM2320_SCL_APBxCLK_CMD RCC_APB2PeriphClockCmd
#define AM2320_SCL_GPIO_CLK RCC_APB2Periph_GPIOB
#define AM2320_SCL_GPIOx GPIOB
#define AM2320_SCL_GPIO_Pin GPIO_Pin_8
#define AM2320_SDA_APBxCLK_CMD RCC_APB2PeriphClockCmd
#define AM2320_SDA_GPIO_CLK RCC_APB2Periph_GPIOB
#define AM2320_SDA_GPIOx GPIOB
#define AM2320_SDA_GPIO_Pin GPIO_Pin_9
#define AM2320_SDA_TIM_APBxCLK_CMD RCC_APB1PeriphClockCmd
#define AM2320_SDA_TIM_CLK RCC_APB1Periph_TIM4
#define AM2320_SDA_TIM_TIMx TIM4
#define AM2320_SDA_TIM_CC TIM_Channel_4
#define AM2320_SDA_TIM_IT_CC TIM_IT_CC4
#define AM2320_SDA_TIM_IRQn TIM4_IRQn
#define AM2320_SDA_TIM_IRQHandler TIM4_IRQHandler
/*
* 函数名 : Am2320_Init
* 描述 : AM2320初始化
* 输入 : 无
* 输出 : 无
* 说明 : 无
*/
int Am2320_Init(void)
{
//IO口配置结构体
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_StructInit(&GPIO_InitStruct);
//SCL
AM2320_SCL_APBxCLK_CMD(AM2320_SCL_GPIO_CLK , ENABLE);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = AM2320_SCL_GPIO_Pin;
GPIO_Init(AM2320_SCL_GPIOx , &GPIO_InitStruct);
//SCL保持低进入单总线传输
GPIO_ResetBits(AM2320_SCL_GPIOx, AM2320_SCL_GPIO_Pin);
//SDA
AM2320_SDA_APBxCLK_CMD(AM2320_SDA_GPIO_CLK , ENABLE);
//输出模式
Am2320_SdaOutMode();
return 0;
}
/*
* 函数名 : Am2320_SdaInMode
* 描述 : AM2320 SDA处于输入模式
* 输入 : 无
* 输出 : 无
* 说明 : 无
*/
void Am2320_SdaInMode(void)
{
//GPIO配置结构体
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_StructInit(&GPIO_InitStruct);
//定时器配置结构体
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseStructInit(&TIM_TimeBaseInitStruct);
//定时器输入配置机构体
TIM_ICInitTypeDef TIM_ICInitStruct;
TIM_ICStructInit(&TIM_ICInitStruct);
//中断配置结构体
NVIC_InitTypeDef NVIC_InitStruct;
//配置为输入
GPIO_InitStruct.GPIO_Pin = AM2320_SDA_GPIO_Pin;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(AM2320_SDA_GPIOx, &GPIO_InitStruct);
//初始化TIM
AM2320_SDA_TIM_APBxCLK_CMD(AM2320_SDA_TIM_CLK, ENABLE);//开启TIM时钟
//配置定时器
TIM_TimeBaseInitStruct.TIM_Prescaler=(SystemCoreClock/1000000 - 1);//定时器分频:设置为1us
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;//向上
TIM_TimeBaseInitStruct.TIM_ClockDivision = 0;
TIM_TimeBaseInitStruct.TIM_Period= 10000 - 1;//10ms溢出
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(AM2320_SDA_TIM_TIMx, &TIM_TimeBaseInitStruct);
//配置为输入模式
TIM_ICInitStruct.TIM_Channel = AM2320_SDA_TIM_CC;// 选择输入端
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_BothEdge;
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置输入分频,不分频
TIM_ICInitStruct.TIM_ICFilter = 0x04;//IC4F=0011 配置输入滤波器 8个定时器时钟周期滤波
TIM_ICInit(AM2320_SDA_TIM_TIMx, &TIM_ICInitStruct);//初始化定时器输入捕获通道
//配置化中断
NVIC_InitStruct.NVIC_IRQChannel = AM2320_SDA_TIM_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; //先占优先级0级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; //从优先级3级
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStruct); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
//清除中断
TIM_ClearITPendingBit(AM2320_SDA_TIM_TIMx, TIM_IT_Update);
TIM_ClearITPendingBit(AM2320_SDA_TIM_TIMx, AM2320_SDA_TIM_IT_CC);
//允许更新中断
TIM_ITConfig(AM2320_SDA_TIM_TIMx, TIM_IT_Update,ENABLE);
//开启捕获中断
TIM_ITConfig(AM2320_SDA_TIM_TIMx, AM2320_SDA_TIM_IT_CC,ENABLE);
//重置定时器
AM2320_SDA_TIM_TIMx->CNT = 0;
//使能定时器
TIM_Cmd(AM2320_SDA_TIM_TIMx, ENABLE);
}
/*
* 函数名 : Am2320_SdaOutMode
* 描述 : AM2320 SDA处于输出模式
* 输入 : 无
* 输出 : 无
* 说明 : 无
*/
void Am2320_SdaOutMode(void)
{
//关闭定时器
TIM_Cmd(AM2320_SDA_TIM_TIMx, DISABLE);
//关闭中断
TIM_ITConfig(AM2320_SDA_TIM_TIMx, TIM_IT_Update, DISABLE);
//关闭捕获中断Bit_RESET
TIM_ITConfig(AM2320_SDA_TIM_TIMx, AM2320_SDA_TIM_IT_CC, DISABLE);
//GPIO配置结构体
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_StructInit(&GPIO_InitStruct);
//配置为输出
GPIO_InitStruct.GPIO_Pin = AM2320_SDA_GPIO_Pin;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(AM2320_SDA_GPIOx, &GPIO_InitStruct);
GPIO_SetBits(AM2320_SDA_GPIOx, AM2320_SDA_GPIO_Pin);
}
数据结构定义
typedef enum
{
AM2320_READ_FREE = 0x00,
AM2320_READ_START ,
AM2320_READ_WAIT_TREL , //起始时80us低电平响应
AM2320_READ_WAIT_TREH , //起始时80us高电平响应
AM2320_READ_WAIT_TDATA , //数据传输开始
AM2320_READ_WAIT_DATA , //数据传输时DATA的响应,上降沿响应
AM2320_READ_END , //读取结束
}AM2320_READ_STEP;
typedef enum
{
AM2320_ERR_NONE = 0x00,
AM2320_ERR_TIM_OVER ,
}AM2320_ERROR;
typedef struct
{
AM2320_ERROR last_err; //异常
AM2320_READ_STEP read_step; //当前的步骤
float humi; //当前的湿度
float temp; //当前的温度
uint8_t data[5]; //临时的数据
uint8_t data_index; //保存当前为读到的第几位
}AM2320;
SDA输出1/0 ,读取输入
#define AM2320_SDA_OUT1 GPIO_SetBits(AM2320_SDA_GPIOx , AM2320_SDA_GPIO_Pin)
#define AM2320_SDA_OUT0 GPIO_ResetBits(AM2320_SDA_GPIOx , AM2320_SDA_GPIO_Pin)
#define AM2320_SDA_IN() GPIO_ReadInputDataBit(AM2320_SDA_GPIOx , AM2320_SDA_GPIO_Pin)
设置定时器超时及捕获极性函数
/*
* 函数名 : Am2320_SetTimOverValue
* 描述 : AM2320设置超时时间
* 输入 : us:微秒
* 输出 : 无
* 说明 : 无
*/
static void Am2320_SetTimOverValue(uint16_t us)
{
//设置超时值
AM2320_SDA_TIM_TIMx->ARR = (us - 1);
}
/*
* 函数名 : Am2320_SetTimPolarity
* 描述 : AM2320设置捕获边沿极性
* 输入 : polarity:极性
* 输出 : 无
* 说明 : 无
*/
static void Am2320_SetTimPolarity(uint16_t polarity)
{
switch(AM2320_SDA_TIM_CC)
{
case TIM_Channel_1: TIM_OC1PolarityConfig(AM2320_SDA_TIM_TIMx, polarity); break;
case TIM_Channel_2: TIM_OC2PolarityConfig(AM2320_SDA_TIM_TIMx, polarity); break;
case TIM_Channel_3: TIM_OC3PolarityConfig(AM2320_SDA_TIM_TIMx, polarity); break;
case TIM_Channel_4: TIM_OC4PolarityConfig(AM2320_SDA_TIM_TIMx, polarity); break;
default:break;
}
}
读取开始函数
/*
* 函数名 : Am2320_ReadByte
* 描述 : AM2320读取开始
* 输入 : p_am2320
* 输出 : 返回读取到的字节数据
* 说明 : 无
*/
static void Am2320_ReadStart(AM2320 *p_am2320)
{
//开始读取
p_am2320->read_step = AM2320_READ_START;
p_am2320->last_err = AM2320_ERR_NONE;
//复位读取到的数据
p_am2320->data_index = 0;
for(uint8_t i = 0; i < 5; i++){
p_am2320->data[i] = 0x00;
}
//拉低总线,延时18ms
Am2320_SdaOutMode();
AM2320_SDA_OUT0;
delay_ms(15);
//总线拉高,延时30us
AM2320_SDA_OUT1;
delay_us(30);
//进入等待总线拉低响应
p_am2320->read_step = AM2320_READ_WAIT_TREH;
//进入读取阶段
Am2320_SdaInMode();
//设置为下降沿响应
Am2320_SetTimPolarity(TIM_ICPolarity_Rising);
//重置定时器
AM2320_SDA_TIM_TIMx->CNT = 0;
//设置超时时间:500us(实际为80us为响应)
Am2320_SetTimOverValue(500);
}
读取结束函数,可在此处获取到数据
/*
* 函数名 : Am2320_ReadEnd
* 描述 : AM2320读取结束
* 输入 : p_am2320
* 输出 : 返回读取到的字节数据
* 说明 : 无
*/
static void Am2320_ReadEnd(AM2320 *p_am2320)
{
static uint8_t err_count = 0;
//开始读取
p_am2320->read_step = AM2320_READ_END;
if(p_am2320->last_err == AM2320_ERR_NONE){
uint8_t humi_int = p_am2320->data[0];
uint8_t humi_deci = p_am2320->data[1];
uint8_t temp_int = p_am2320->data[2];
uint8_t temp_deci = p_am2320->data[3];
uint8_t check_sum = p_am2320->data[4];
//计算结果
uint8_t calc = humi_int + humi_deci + temp_int + temp_deci;
//数据校验
if(calc == check_sum){
p_am2320->humi = (humi_int*256 + humi_deci)*0.1;
p_am2320->temp = (temp_int*256 + temp_deci)*0.1;
//读取完成结束
//此时已将温湿度数据存储在结构体中
}
err_count = 0;
}
//输出模式
Am2320_SdaOutMode();
}
定时器中断处理
/*
* 函数名 : AM2320_SDA_TIM_IRQHandler
* 描述 : 定时器中断
* 输入 : 无
* 输出 : 无
* 说明 : 无
*/
void AM2320_SDA_TIM_IRQHandler(void)
{
AM2320 *p_am2320 = &am2320;
//超时了
if(TIM_GetITStatus(AM2320_SDA_TIM_TIMx, TIM_IT_Update) != RESET){
//清除中断
TIM_ClearITPendingBit(AM2320_SDA_TIM_TIMx, TIM_IT_Update);
p_am2320->last_err = AM2320_ERR_TIM_OVER;
p_am2320->read_step = AM2320_READ_FREE;
//读取结束
Am2320_ReadEnd(p_am2320);
}
//读取到边沿信号
if(TIM_GetITStatus(AM2320_SDA_TIM_TIMx, AM2320_SDA_TIM_IT_CC)!=RESET){
//清除中断
TIM_ClearITPendingBit(AM2320_SDA_TIM_TIMx, AM2320_SDA_TIM_IT_CC);
//重置定时器
AM2320_SDA_TIM_TIMx->CNT = 0;
switch(p_am2320->read_step)
{
case AM2320_READ_WAIT_TREH://80us低电平完成,开始80us高电平
//确认读取到了高电平
if(AM2320_SDA_IN() == Bit_SET){
//进入等待传输开始
p_am2320->read_step = AM2320_READ_WAIT_TDATA;
//设置为上升沿捕获
Am2320_SetTimPolarity(TIM_ICPolarity_Falling);
//设置超时时间:80us: 80us信号低电平时间
Am2320_SetTimOverValue(85+20);//最长85us+20us富余
//读取到数据,指示点亮表示开始读取数据
DigitalLed_SetAnimation(ANIMATION_DOT);
}
break;
case AM2320_READ_WAIT_TDATA://80us高电平完成,开始50us低电平与信号电平时间
//确认读取到了高电平
if(AM2320_SDA_IN() == Bit_RESET){
//开始数据传输
p_am2320->read_step = AM2320_READ_WAIT_DATA;
//设置为上升沿捕获
Am2320_SetTimPolarity(TIM_ICPolarity_Falling);
//设置超时时间:150us: '0'/'1'的高电平时间(75us) + 50us的低电平时间 + 20us富余
Am2320_SetTimOverValue(55+75+20);
}
break;
case AM2320_READ_WAIT_DATA:
//确认读取到了低电平
if(AM2320_SDA_IN() == Bit_RESET){
//获取当前的耗时
uint16_t rm_cnt = 0;
switch(AM2320_SDA_TIM_CC)
{
case TIM_Channel_1: rm_cnt = TIM_GetCapture1(AM2320_SDA_TIM_TIMx); break;
case TIM_Channel_2: rm_cnt = TIM_GetCapture2(AM2320_SDA_TIM_TIMx); break;
case TIM_Channel_3: rm_cnt = TIM_GetCapture3(AM2320_SDA_TIM_TIMx); break;
case TIM_Channel_4: rm_cnt = TIM_GetCapture4(AM2320_SDA_TIM_TIMx); break;
default:break;
}
//当前需要写入的位
uint8_t byte = p_am2320->data_index / 8;
uint8_t bit = p_am2320->data_index % 8;
//判断是否为低电平时间
if(rm_cnt > (50 + 68))//50us+'1'的最小时间68us
p_am2320->data[byte] |= (uint8_t)0x01 << (7-bit);
//判断是否读取到5个字节的数据读取到所有的数据
if(++p_am2320->data_index >= 5*8){
p_am2320->last_err = AM2320_ERR_NONE;
//读取结束
Am2320_ReadEnd(p_am2320);
}
}
break;
default:break;
}
}
}
以上就是所有相关代码了。
使用步骤:
1: Am2320_Init(); //初始化IO
2: Am2320_ReadStart(&am2320); //发起读取
3: 如若一切正常则会将读取到的温湿度数据跟新至 am2320结构体中
版权声明:本文为CSDN博主「毛庭伟」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_63451755/article/details/122695650
AM2320是一款具有I2C、单总线通信的温湿度传感器,精度也比较高,适合日常的一些应用场景。对于单总线通信,已经有很多人分享其实现的代码了,但多是使用延时法进行读取,今天给大家带来一个通过单总线定时器中断方式读取。
初始化代码
首先是需要进行IO口初始化,单总线通信时SCL引脚需要接地,并使用SDA引脚作为单总线通信引脚。此处将SCL使用GPIO进行拉低,SDA选择连接在GPIOB,GPIO_Pin_9这个定时器4的CH4上。
Am2320_SdaOutMode 输出模式 用于发起通信起始信号。
Am2320_SdaInMode 输入模式 用于接收总线数据,此处将定时器的基准时间设置为1us,方便捕获中断时长用于计算0/1。并开启定时器更新中断作为通信超时结束控制,开启定时器的捕获中断作为通信时采集。
//AM2320 IO口
#define AM2320_SCL_APBxCLK_CMD RCC_APB2PeriphClockCmd
#define AM2320_SCL_GPIO_CLK RCC_APB2Periph_GPIOB
#define AM2320_SCL_GPIOx GPIOB
#define AM2320_SCL_GPIO_Pin GPIO_Pin_8
#define AM2320_SDA_APBxCLK_CMD RCC_APB2PeriphClockCmd
#define AM2320_SDA_GPIO_CLK RCC_APB2Periph_GPIOB
#define AM2320_SDA_GPIOx GPIOB
#define AM2320_SDA_GPIO_Pin GPIO_Pin_9
#define AM2320_SDA_TIM_APBxCLK_CMD RCC_APB1PeriphClockCmd
#define AM2320_SDA_TIM_CLK RCC_APB1Periph_TIM4
#define AM2320_SDA_TIM_TIMx TIM4
#define AM2320_SDA_TIM_CC TIM_Channel_4
#define AM2320_SDA_TIM_IT_CC TIM_IT_CC4
#define AM2320_SDA_TIM_IRQn TIM4_IRQn
#define AM2320_SDA_TIM_IRQHandler TIM4_IRQHandler
/*
* 函数名 : Am2320_Init
* 描述 : AM2320初始化
* 输入 : 无
* 输出 : 无
* 说明 : 无
*/
int Am2320_Init(void)
{
//IO口配置结构体
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_StructInit(&GPIO_InitStruct);
//SCL
AM2320_SCL_APBxCLK_CMD(AM2320_SCL_GPIO_CLK , ENABLE);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = AM2320_SCL_GPIO_Pin;
GPIO_Init(AM2320_SCL_GPIOx , &GPIO_InitStruct);
//SCL保持低进入单总线传输
GPIO_ResetBits(AM2320_SCL_GPIOx, AM2320_SCL_GPIO_Pin);
//SDA
AM2320_SDA_APBxCLK_CMD(AM2320_SDA_GPIO_CLK , ENABLE);
//输出模式
Am2320_SdaOutMode();
return 0;
}
/*
* 函数名 : Am2320_SdaInMode
* 描述 : AM2320 SDA处于输入模式
* 输入 : 无
* 输出 : 无
* 说明 : 无
*/
void Am2320_SdaInMode(void)
{
//GPIO配置结构体
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_StructInit(&GPIO_InitStruct);
//定时器配置结构体
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseStructInit(&TIM_TimeBaseInitStruct);
//定时器输入配置机构体
TIM_ICInitTypeDef TIM_ICInitStruct;
TIM_ICStructInit(&TIM_ICInitStruct);
//中断配置结构体
NVIC_InitTypeDef NVIC_InitStruct;
//配置为输入
GPIO_InitStruct.GPIO_Pin = AM2320_SDA_GPIO_Pin;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(AM2320_SDA_GPIOx, &GPIO_InitStruct);
//初始化TIM
AM2320_SDA_TIM_APBxCLK_CMD(AM2320_SDA_TIM_CLK, ENABLE);//开启TIM时钟
//配置定时器
TIM_TimeBaseInitStruct.TIM_Prescaler=(SystemCoreClock/1000000 - 1);//定时器分频:设置为1us
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;//向上
TIM_TimeBaseInitStruct.TIM_ClockDivision = 0;
TIM_TimeBaseInitStruct.TIM_Period= 10000 - 1;//10ms溢出
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(AM2320_SDA_TIM_TIMx, &TIM_TimeBaseInitStruct);
//配置为输入模式
TIM_ICInitStruct.TIM_Channel = AM2320_SDA_TIM_CC;// 选择输入端
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_BothEdge;
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置输入分频,不分频
TIM_ICInitStruct.TIM_ICFilter = 0x04;//IC4F=0011 配置输入滤波器 8个定时器时钟周期滤波
TIM_ICInit(AM2320_SDA_TIM_TIMx, &TIM_ICInitStruct);//初始化定时器输入捕获通道
//配置化中断
NVIC_InitStruct.NVIC_IRQChannel = AM2320_SDA_TIM_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; //先占优先级0级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; //从优先级3级
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStruct); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
//清除中断
TIM_ClearITPendingBit(AM2320_SDA_TIM_TIMx, TIM_IT_Update);
TIM_ClearITPendingBit(AM2320_SDA_TIM_TIMx, AM2320_SDA_TIM_IT_CC);
//允许更新中断
TIM_ITConfig(AM2320_SDA_TIM_TIMx, TIM_IT_Update,ENABLE);
//开启捕获中断
TIM_ITConfig(AM2320_SDA_TIM_TIMx, AM2320_SDA_TIM_IT_CC,ENABLE);
//重置定时器
AM2320_SDA_TIM_TIMx->CNT = 0;
//使能定时器
TIM_Cmd(AM2320_SDA_TIM_TIMx, ENABLE);
}
/*
* 函数名 : Am2320_SdaOutMode
* 描述 : AM2320 SDA处于输出模式
* 输入 : 无
* 输出 : 无
* 说明 : 无
*/
void Am2320_SdaOutMode(void)
{
//关闭定时器
TIM_Cmd(AM2320_SDA_TIM_TIMx, DISABLE);
//关闭中断
TIM_ITConfig(AM2320_SDA_TIM_TIMx, TIM_IT_Update, DISABLE);
//关闭捕获中断Bit_RESET
TIM_ITConfig(AM2320_SDA_TIM_TIMx, AM2320_SDA_TIM_IT_CC, DISABLE);
//GPIO配置结构体
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_StructInit(&GPIO_InitStruct);
//配置为输出
GPIO_InitStruct.GPIO_Pin = AM2320_SDA_GPIO_Pin;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(AM2320_SDA_GPIOx, &GPIO_InitStruct);
GPIO_SetBits(AM2320_SDA_GPIOx, AM2320_SDA_GPIO_Pin);
}
数据结构定义
typedef enum
{
AM2320_READ_FREE = 0x00,
AM2320_READ_START ,
AM2320_READ_WAIT_TREL , //起始时80us低电平响应
AM2320_READ_WAIT_TREH , //起始时80us高电平响应
AM2320_READ_WAIT_TDATA , //数据传输开始
AM2320_READ_WAIT_DATA , //数据传输时DATA的响应,上降沿响应
AM2320_READ_END , //读取结束
}AM2320_READ_STEP;
typedef enum
{
AM2320_ERR_NONE = 0x00,
AM2320_ERR_TIM_OVER ,
}AM2320_ERROR;
typedef struct
{
AM2320_ERROR last_err; //异常
AM2320_READ_STEP read_step; //当前的步骤
float humi; //当前的湿度
float temp; //当前的温度
uint8_t data[5]; //临时的数据
uint8_t data_index; //保存当前为读到的第几位
}AM2320;
SDA输出1/0 ,读取输入
#define AM2320_SDA_OUT1 GPIO_SetBits(AM2320_SDA_GPIOx , AM2320_SDA_GPIO_Pin)
#define AM2320_SDA_OUT0 GPIO_ResetBits(AM2320_SDA_GPIOx , AM2320_SDA_GPIO_Pin)
#define AM2320_SDA_IN() GPIO_ReadInputDataBit(AM2320_SDA_GPIOx , AM2320_SDA_GPIO_Pin)
设置定时器超时及捕获极性函数
/*
* 函数名 : Am2320_SetTimOverValue
* 描述 : AM2320设置超时时间
* 输入 : us:微秒
* 输出 : 无
* 说明 : 无
*/
static void Am2320_SetTimOverValue(uint16_t us)
{
//设置超时值
AM2320_SDA_TIM_TIMx->ARR = (us - 1);
}
/*
* 函数名 : Am2320_SetTimPolarity
* 描述 : AM2320设置捕获边沿极性
* 输入 : polarity:极性
* 输出 : 无
* 说明 : 无
*/
static void Am2320_SetTimPolarity(uint16_t polarity)
{
switch(AM2320_SDA_TIM_CC)
{
case TIM_Channel_1: TIM_OC1PolarityConfig(AM2320_SDA_TIM_TIMx, polarity); break;
case TIM_Channel_2: TIM_OC2PolarityConfig(AM2320_SDA_TIM_TIMx, polarity); break;
case TIM_Channel_3: TIM_OC3PolarityConfig(AM2320_SDA_TIM_TIMx, polarity); break;
case TIM_Channel_4: TIM_OC4PolarityConfig(AM2320_SDA_TIM_TIMx, polarity); break;
default:break;
}
}
读取开始函数
/*
* 函数名 : Am2320_ReadByte
* 描述 : AM2320读取开始
* 输入 : p_am2320
* 输出 : 返回读取到的字节数据
* 说明 : 无
*/
static void Am2320_ReadStart(AM2320 *p_am2320)
{
//开始读取
p_am2320->read_step = AM2320_READ_START;
p_am2320->last_err = AM2320_ERR_NONE;
//复位读取到的数据
p_am2320->data_index = 0;
for(uint8_t i = 0; i < 5; i++){
p_am2320->data[i] = 0x00;
}
//拉低总线,延时18ms
Am2320_SdaOutMode();
AM2320_SDA_OUT0;
delay_ms(15);
//总线拉高,延时30us
AM2320_SDA_OUT1;
delay_us(30);
//进入等待总线拉低响应
p_am2320->read_step = AM2320_READ_WAIT_TREH;
//进入读取阶段
Am2320_SdaInMode();
//设置为下降沿响应
Am2320_SetTimPolarity(TIM_ICPolarity_Rising);
//重置定时器
AM2320_SDA_TIM_TIMx->CNT = 0;
//设置超时时间:500us(实际为80us为响应)
Am2320_SetTimOverValue(500);
}
读取结束函数,可在此处获取到数据
/*
* 函数名 : Am2320_ReadEnd
* 描述 : AM2320读取结束
* 输入 : p_am2320
* 输出 : 返回读取到的字节数据
* 说明 : 无
*/
static void Am2320_ReadEnd(AM2320 *p_am2320)
{
static uint8_t err_count = 0;
//开始读取
p_am2320->read_step = AM2320_READ_END;
if(p_am2320->last_err == AM2320_ERR_NONE){
uint8_t humi_int = p_am2320->data[0];
uint8_t humi_deci = p_am2320->data[1];
uint8_t temp_int = p_am2320->data[2];
uint8_t temp_deci = p_am2320->data[3];
uint8_t check_sum = p_am2320->data[4];
//计算结果
uint8_t calc = humi_int + humi_deci + temp_int + temp_deci;
//数据校验
if(calc == check_sum){
p_am2320->humi = (humi_int*256 + humi_deci)*0.1;
p_am2320->temp = (temp_int*256 + temp_deci)*0.1;
//读取完成结束
//此时已将温湿度数据存储在结构体中
}
err_count = 0;
}
//输出模式
Am2320_SdaOutMode();
}
定时器中断处理
/*
* 函数名 : AM2320_SDA_TIM_IRQHandler
* 描述 : 定时器中断
* 输入 : 无
* 输出 : 无
* 说明 : 无
*/
void AM2320_SDA_TIM_IRQHandler(void)
{
AM2320 *p_am2320 = &am2320;
//超时了
if(TIM_GetITStatus(AM2320_SDA_TIM_TIMx, TIM_IT_Update) != RESET){
//清除中断
TIM_ClearITPendingBit(AM2320_SDA_TIM_TIMx, TIM_IT_Update);
p_am2320->last_err = AM2320_ERR_TIM_OVER;
p_am2320->read_step = AM2320_READ_FREE;
//读取结束
Am2320_ReadEnd(p_am2320);
}
//读取到边沿信号
if(TIM_GetITStatus(AM2320_SDA_TIM_TIMx, AM2320_SDA_TIM_IT_CC)!=RESET){
//清除中断
TIM_ClearITPendingBit(AM2320_SDA_TIM_TIMx, AM2320_SDA_TIM_IT_CC);
//重置定时器
AM2320_SDA_TIM_TIMx->CNT = 0;
switch(p_am2320->read_step)
{
case AM2320_READ_WAIT_TREH://80us低电平完成,开始80us高电平
//确认读取到了高电平
if(AM2320_SDA_IN() == Bit_SET){
//进入等待传输开始
p_am2320->read_step = AM2320_READ_WAIT_TDATA;
//设置为上升沿捕获
Am2320_SetTimPolarity(TIM_ICPolarity_Falling);
//设置超时时间:80us: 80us信号低电平时间
Am2320_SetTimOverValue(85+20);//最长85us+20us富余
//读取到数据,指示点亮表示开始读取数据
DigitalLed_SetAnimation(ANIMATION_DOT);
}
break;
case AM2320_READ_WAIT_TDATA://80us高电平完成,开始50us低电平与信号电平时间
//确认读取到了高电平
if(AM2320_SDA_IN() == Bit_RESET){
//开始数据传输
p_am2320->read_step = AM2320_READ_WAIT_DATA;
//设置为上升沿捕获
Am2320_SetTimPolarity(TIM_ICPolarity_Falling);
//设置超时时间:150us: '0'/'1'的高电平时间(75us) + 50us的低电平时间 + 20us富余
Am2320_SetTimOverValue(55+75+20);
}
break;
case AM2320_READ_WAIT_DATA:
//确认读取到了低电平
if(AM2320_SDA_IN() == Bit_RESET){
//获取当前的耗时
uint16_t rm_cnt = 0;
switch(AM2320_SDA_TIM_CC)
{
case TIM_Channel_1: rm_cnt = TIM_GetCapture1(AM2320_SDA_TIM_TIMx); break;
case TIM_Channel_2: rm_cnt = TIM_GetCapture2(AM2320_SDA_TIM_TIMx); break;
case TIM_Channel_3: rm_cnt = TIM_GetCapture3(AM2320_SDA_TIM_TIMx); break;
case TIM_Channel_4: rm_cnt = TIM_GetCapture4(AM2320_SDA_TIM_TIMx); break;
default:break;
}
//当前需要写入的位
uint8_t byte = p_am2320->data_index / 8;
uint8_t bit = p_am2320->data_index % 8;
//判断是否为低电平时间
if(rm_cnt > (50 + 68))//50us+'1'的最小时间68us
p_am2320->data[byte] |= (uint8_t)0x01 << (7-bit);
//判断是否读取到5个字节的数据读取到所有的数据
if(++p_am2320->data_index >= 5*8){
p_am2320->last_err = AM2320_ERR_NONE;
//读取结束
Am2320_ReadEnd(p_am2320);
}
}
break;
default:break;
}
}
}
以上就是所有相关代码了。
使用步骤:
1: Am2320_Init(); //初始化IO
2: Am2320_ReadStart(&am2320); //发起读取
3: 如若一切正常则会将读取到的温湿度数据跟新至 am2320结构体中
版权声明:本文为CSDN博主「毛庭伟」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_63451755/article/details/122695650
暂无评论