AM2320单总线定时器中断方式驱动(stm32)

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

生成海报
点赞 0

毛庭伟

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

AM2320单总线定时器中断方式驱动(stm32)

AM2320是一款具有I2C、单总线通信的温湿度传感器,精度也比较高,适合日常的一些应用场景。对于单总线通信,已经有很多人分享其实现的代码了,但多是使用延时法进行读取,今天给

基于STM32的指纹密码锁

设计简介: 本设计是基于单片机的指纹密码锁,主要实现以下功能: 矩阵按键输入密码,并通过按键显示*号可通过按键或手机开门密码可通过按键进行开门可通过蓝牙模块连接手机进行开门可通过指纹进