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结构体中

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值