AM2320单总线温湿度传感器常规驱动
AM2302,AM2320以及DHT22常规驱动都是采用IO引脚模拟时序读出的方式实现的。AM2320还可以使用I2C驱动,本文主要介绍单总线方式。
背景
目前的项目中,由于使用了RTOS,致模拟时序的方式会被其他线程或中断打断,导致读取失败,虽然偶尔失败一两次,不影响后续温湿度结果,同时也可以使用禁用调度或中断的方式来保证单总线的读取时序,但项目使用的AT32F437单片机,性能强劲,应该有更好的实现方式。下面将介绍中断及DMA两种驱动的实现方式。
AM2320时序
中断驱动方式
实现思路
通过从机发送数据的下降沿来触发外部中断,中断里面记录当前的SysTick->VAL,一共41次下降沿完成数据传输,第一次下降沿为从机的响应的结束时刻,后续为数据。根据两次中断的间隔计算出脉冲的周期,判断是0或1。
- 主机发送完起始信号后,使用单总线的IO口来触发中断,下降沿一次,进入中断一次,在中断里面记录当前的SysTick->VAL
- 一共需传输41次,根据记录到的数值,计算脉冲周期并还原出温湿度数据。
- 具体实现关键代码如下:
/**
* 函数功能: 使AM2302-DATA引脚变为上拉输入模式
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
static void AM2302_Mode_IPU(void)
{
gpio_init_type gpio_init_struct;
gpio_default_para_init(&gpio_init_struct);
gpio_init_struct.gpio_pins = AM2302_Dout_PIN;
gpio_init_struct.gpio_mode = GPIO_MODE_INPUT;
gpio_init_struct.gpio_pull = GPIO_PULL_UP;
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init(AM2302_Dout_PORT, &gpio_init_struct);
}
/**
* 函数功能: 使AM2302-DATA引脚变为开漏输出模式
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
static void AM2302_Mode_Out_OD(void)
{
gpio_init_type gpio_init_struct;
gpio_default_para_init(&gpio_init_struct);
gpio_init_struct.gpio_pins = AM2302_Dout_PIN;
gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_OPEN_DRAIN;
gpio_init_struct.gpio_pull = GPIO_PULL_UP;
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init(AM2302_Dout_PORT, &gpio_init_struct);
}
// 一共下降沿41次,中断41次
#define FALLING_CNT (41)
__IO static uint8_t isr_nums = 0;
// 读出结果
__IO static uint8_t result[5] = {
0};
// 读出结果
__IO static uint8_t us_result[FALLING_CNT] = {
0};
// 暂存定时器计数值
__IO uint32_t dst_buffer[FALLING_CNT] = {
0};
static void result_reset(void)
{
rt_memset(result, 0, 5);
rt_memset(us_result, 0, FALLING_CNT);
rt_memset(dst_buffer, 0, FALLING_CNT * sizeof(uint32_t));
isr_nums = 0;
}
/**
* 函数功能: AM2302数据引脚中断函数,记录SysTick->VAL
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
static uint8_t AM2302_Read_Isr(void)
{
dst_buffer[isr_nums++] = SysTick->VAL;
}
void printf_bin_8(unsigned char num)
{
int k;
unsigned char *p = (unsigned char *)#
for (int k = 7; k >= 0; k--) // 处理8个位
{
if (*p & (1 << k))
rt_kprintf("1");
else
rt_kprintf("0");
}
rt_kprintf("\r\n");
}
/**
* 函数功能: 一次完整的数据传输为40bit,高位先出
* 输入参数: AM2302_Data:AM2302数据类型
* 返 回 值: ERROR: 读取出错
* SUCCESS:读取成功
* 说 明:8bit 湿度整数 + 8bit 湿度小数 + 8bit 温度整数 + 8bit 温度小数 + 8bit 校验和
*/
uint8_t AM2302_Read_TempAndHumidity(AM2302_Data_TypeDef *AM2302_Data)
{
uint8_t temp;
uint16_t humi_temp;
uint16_t cnt = 0;
uint8_t humi_high8bit; // 原始数据:湿度高8位
uint8_t humi_low8bit; // 原始数据:湿度低8位
uint8_t temp_high8bit; // 原始数据:温度高8位
uint8_t temp_low8bit; // 原始数据:温度高8位
uint8_t check_sum; // 校验和
/*输出模式*/
AM2302_Mode_Out_OD();
/*主机拉低*/
AM2302_Dout_LOW();
/*延时2ms*/
rt_thread_mdelay(2);
/*总线拉高 主机延时30us*/
AM2302_Dout_HIGH();
rt_hw_us_delay(30); // 延时30us
/*主机设为输入 判断从机响应信号*/
AM2302_Mode_IPU();
/*判断从机是否有低电平响应信号 如不响应则跳出,响应则向下运行*/
if (AM2302_Data_IN() == FALSE)
{
result_reset();
// rt_kprintf("%d",get_system_us()-us_tick);
/* 绑定中断,下降沿模式,回调函数名为PowOff_Isr */
rt_pin_attach_irq(AM2302_Data_IN_PIN, PIN_IRQ_MODE_FALLING, AM2302_Read_Isr, RT_NULL);
/* 使能中断 */
rt_pin_irq_enable(AM2302_Data_IN_PIN, PIN_IRQ_ENABLE);
}
else
{
return ERROR;
}
cnt = 0;
do
{
rt_thread_mdelay(2);