STM32单片机(六). 传感器的使用 |
您所在的位置:网站首页 › MPS技术FESTO八个单元 › STM32单片机(六). 传感器的使用 |
传感器是单片机外围电路中最常见的模块,在搭配了各种形式的传感器电路后,就可以采集到的更多的环境信息。在本章节中,主要介绍呼吸灯、温度传感器、RTC实时时钟以及红外遥控模块的控制使用。 1、PWM实现呼吸灯的效果 1.1 PWM脉冲宽度调制PWM是利用单片机的数字输出来对模拟电路进行控制的技术,其应用包含电机控制、通信、开关电源等等。PWM是一种对模拟信号进行数字编码的方法,其本质上还是数字信号,也就是在任意时刻端口输出要么是高电平要么就是低电平,电压或者电流是以一种通或端的重复脉冲序列被加到模拟负载上。因此只要带宽足够,理论上任何的模拟信号都可以使用PWM技术进行数字编码,如下图中所示。 TIM_OCMode:比较输出模式选择,8种模式,常用PWM1、PWM2; TIM_OutputState:比较输出使能,使能PWM输出到IO口; TIM_OCPolarity:输出极性,设定输出通道电平的极性,(高/低); 将TIM3的通道CH2配置为PWM2模式,输出极性高低电平,并使能PWM输出的过程可配置为: TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enablel; TIM_OCInit(TIM3, &TIM_OCInitStructure); 开启定时器; 调用函数:void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);修改TIMx_CCRx的值控制占空比; 控制占空比需要通过修改TIMx_CCR1的值实现:void TIM_SetComparel(TIM_TypeDef* TIMx, uint32_t Comparel);使能TIMx在CCRx上的预装载寄存器; 调用函数:void TIM_OCxPreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload) TIMx为选定的定时器;TIM_OCPreload取值为TIM_OCPreloda_Enable或TIM_OCPreload_Disable。使能TIMx在ARR上的预装载寄存器允许位。 调用函数:void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState); 1.3 应用示例-呼吸灯通过TIM3定时器的CH1通道输出一个PWM信号,控制一颗LED由“灭->暗->亮->暗->灭”,不断重复该过程。在项目中需要添加定时器库文件stm32f10x_tim.c以及stm32f10x_tim.h。 pwm.h #ifndef _pwm_H #define _pwm_H #include "system.h" void TIM3_CH1_PWM_Init(u16 pre,u16 psc); #endifpwm.c #include "pwm.h" void TIM3_CH1_PWM_Init(u16 pre,u16 psc) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOC,&GPIO_InitStructure); GPIO_PinRemapConfig(GPIO_FullRemap_TIM3,ENABLE); //管脚映射 TIM_TimeBaseInitStructure.TIM_Period=pre; //周期 TIM_TimeBaseInitStructure.TIM_Prescaler=psc; TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure); TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low; TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable; TIM_OC1Init(TIM3,&TIM_OCInitStructure); TIM_OC1PreloadConfig(TIM3,TIM_OCPreload_Enable); TIM_ARRPreloadConfig(TIM3,ENABLE); TIM_Cmd(TIM3,ENABLE); }main.c #include "system.h" #include "led.h" #include "SysTick.h" #include "time.h" #include "pwm.h" int main() { u16 i=0; u8 fx=0; SysTick_Init(72); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); LED_Init(); TIM3_CH1_PWM_Init(500,72-1); while(1) { if(fx==0) { i++; if(i%300==0) { fx=1; } } else { i--; if(i==0) { fx=0; } } TIM_SetCompare1(TIM3,i); //调整占空比 delay_ms(10); } } 2、内部温度传感器对于传感器采集到的信息一般都是通过电压的变化来表示,而在实际中我们需要直观的看到这个数据信息,那么就需要一个转换过程,可以将模拟信息转换为数字信息。在STM32单片机中,存在着3个ADC(模数转换器)外设,可以独立地使用,将模拟信号转换为数字信号。STM32中的ADC是一个12位逐次逼近型的模拟数学转换器,其具有18个复用通道,可测量16个外部信号源、2个内部信号源。通道的A/D转换可以以单次、连续、扫描或间断模式执行,结果可以以左对齐或右对齐的方式存储在16位数据寄存器中,外设ADC的内部结构如下图中所示: 对于ADC的库函数配置版如下所示: 使能端口时钟以及ADC时钟,设置引脚模式为模拟输入; ADCx_INC0~ADCx_IN5为外部通道,分别对应于芯片的一个引脚,一次需要使能GPIOX以及ADCx时钟(挂载在APB2总线上),调用函数:RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOX|RCC_APB2Periph_ADCx, ENABLE); 然后将对应的引脚设置为模拟输入模式(用于采集电压信号):设置ADC的分频因子; 分频因子要保证ADC的时钟(ADCCLK)小于14MHz,ADC的时钟有72/分频因子计算的出,可调用函数:RCC_ADCCLKConfig(RCC_PCLK2_Divx);初始化ADC参数; 初始化ADC时,需要配置ADC的转换模式、触发方式、数据对齐方式等参数,可调用函数:void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct); ADCx用来选择使用哪一个ADC;结构体ADC_InitTypeDef的成员变量如下: typedef struct { uint32_t ADC_Mode; //工作模式选择 FunctionalState ADC_ScanConvMode; //扫描模式选择 FunctionalState ADC_ContinuousConvMode; //转换模式 uint32_t ADC_ExternalTrigConv; //触发信号选择 uint32_t ADC_DataAlign; //数据对齐方式 uint8_t ADC_NbrOfChannel; //采集通道数 }ADC_Mode:模式选择,独立模式、双重模式; ADC_ScanConvMode:扫描模式选择,DISABLE(单通道)或ENABLE(多通道); ADC_ContinuousConvMode:连续转换模式选择,DISABLE(单次)或ENABLE(连续); ADC_ExternalTrigConv:一般默认软件自动触发,不进行配置; ADC_DataAlign:数据对齐方式,ADC_DataAlign_Right(右对齐)或ADC_DataAlign_Left(左对齐); ADC_NbrOfChannel:AD转换通道数目,根据需要选择; 使能ADC并开启校准; 开启ADC并复位校准后,其才能够正常工作,开启ADC时调用函数:void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState); 执行复位校准:ADC_ResetCalibration(ADCx); 执行ADC校准:ADC_StartCalibration(ADCx); 校准后需要等待校准结束,可通过校验位进行判断:while(ADC_ResetCalibration(ADCx)); while(ADC_StartCalibration(ADCx));读取ADC转换值;在读取ADC转换值之前还需要设置规则序列中的通道、采样顺序以及采样周期,然后才启动ADC转换; 调用函数:void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime); 开启ADC转换时调用函数(软件触发):void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState); 读取ADC转换值时调用函数:uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx); 判断ADC的转换是否结束:while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); 2.1 传感器介绍在STM32F1单片机内部有一个温度传感器,可以被用来测量CPU及周围的温度,它支持的测温范围为-40 oC ~125oC,精度为±1.5 oC。其内部的连接结构如下图中所示: 对于温度传感器的配置,需要用到ADC相关的库文件(stm32f10x_adc.h和stm32f10x_adc.c),具体步骤如下: 初始化ADC参数,开启内部温度控制器,调用函数:ADC_TempSensorVrefintCmd(ENABLE);读取ADC采集到的AD值,将其转换为对应的温度。 2.3 应用示例通过STM32芯片内部的温度传感器读取温度值,具体的代码如下所示: adc.h #ifndef _adc_temp_H #define _adc_temp_H #include "system.h" void ADC_Temp_Init(void); int Get_Temperture(void); #endifadc.c #include "adc_temp.h" #include "SysTick.h" #include "usart.h" void ADC_Temp_Init(void) { ADC_InitTypeDef ADC_InitStructure; //注意时钟的开启顺序 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE); ADC_TempSensorVrefintCmd(ENABLE); //开启内部温度传感器通道 RCC_ADCCLKConfig(RCC_PCLK2_Div6); ADC_InitStructure.ADC_Mode=ADC_Mode_Independent; /独立工作模式 ADC_InitStructure.ADC_ScanConvMode=DISABLE; //单通道采样 ADC_InitStructure.ADC_ContinuousConvMode=DISABLE; //单次扫描 ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//不使用外部触发 ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right; //数据右对齐方式 ADC_InitStructure.ADC_NbrOfChannel=1; //通道数量 ADC_Init(ADC1,&ADC_InitStructure); ADC_Cmd(ADC1,ENABLE); //使能ADC ADC_ResetCalibration(ADC1); //复位校准 while(ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); //开启校准 while(ADC_GetCalibrationStatus(ADC1)); ADC_SoftwareStartConvCmd(ADC1, ENABLE); //软件启动ADC } u16 Get_Temp_Value(u8 ch,u8 times) //获取ADC通道采集回来的数据 { u8 t; u32 temp_val=0; ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5); for(t=0;t u16 temp; double tempreture; int T; temp=Get_Temp_Value(ADC_Channel_16,10); printf("%d\r\n",temp); tempreture=(float)temp*(3.3/4096); tempreture=(1.43-tempreture)/0.0043+25; T=tempreture*100; printf("%d\r\n",T); return T; }main.c #include "system.h" #include "led.h" #include "SysTick.h" #include "usart.h" #include "adc_temp.h" int main() { u8 i=0; int tempreture; SysTick_Init(72); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级分组 LED_Init(); USART1_Init(9600); ADC_Temp_Init(); while(1) { i++; if(i%20==0) { led1=!led1; } if(i%50==0) { tempreture=Get_Temperture(); if(tempreture printf("节点温度为:+%.2f C\r\n",(float) tempreture/100-1); } } delay_ms(10); } } 3、RTC实时时钟 3.1 传感器介绍在STM32中RTC(实时时钟)是一个独立的定时器,其具有一组连续计数的计数器,可提供时钟日历的功能。RTC模块拥有一个后备电源—纽扣电池,因此当主控掉电时,只要纽扣电池有电,RTC就会正常的工作。RTC是一个32位的计数器,采取向上计数模式,其时钟源来自于高速外部时钟的128分频、低速内部时钟LSI以及外部时钟LSE三种。在使用HSE分频时钟或LSI时,主电源掉电时,时钟会受到影响,导致RTC无法正常工作。因此一般情况下,RTC使用LSE时钟。RTC的内部结构图如下所示: 对于RTC的配置,需要用到RTC相关的库文件(stm32f10x_rtc.h和stm32f10x_rtc.c),具体步骤如下: 使能电源时钟和后备域时钟,开启RTC后备寄存器写访问; 调用库函数:RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); //使能电源时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE); //使能后备域时钟 PWR_BackupAccessCmd(ENALE); //使能后备寄存 复位备份区域,开启LSE时钟; 在开启后备寄存器写访问后,首先要对该区域进行复位操作,之后使能LSE时钟,调用函数:BKP_DeInit(); //复位备份区域 RCC_LSEConfig(RCC_LSE_ON); //开启LSE时钟 使能RTC时钟; 选择LSE作为RTC时钟源,之后使能RTC时钟,调用函数:RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); RCC_RTCCLKCmd(ENABLE); 设置RTC分频系数,配置RTC参数; 首先要打开RTC允许配置位:RTC_EnterConfigMode(); 设置RTC时钟分频系数:void RTC_SetPrescaler(uint32_t PrescalerValue); 设置秒中断允许:void RTC_ITConfig(uint16_t RTC_IT, FunctionalState NewState); 设置RTC计数值:void RTC_Setounter(uint32_t CounterValue); 更新配置,设置RTC中断分组; 调用配置更新函数:RTC_ExitConfigMode(); 在备份区域写用户数据:void BKP_WriteBackupRegister(uint16_t BKP_DR, uint16_t Data); 读取备份区域内容:uint16_t BKP_ReadBackupRegister(uint16_t BKP_DR); 配置中断分组:NVIC_Init 编写RTC中断服务函数。 在STM32中RTC中断函数为:RTC_IRQHandler; 读取RTC状态标志位:FlagStatus RTC_GetFlagStatus(uint32_t RTC_FLAG); 清除RTC秒中断标志:RTC_ClearITPendingBit(RTC_IT_SEC); 3.3 应用示例设置STM32的RTC时间初始值,进行计时,详细的配置代码如下: rtc.h #ifndef _rtc_H #define _rtc_H #include "system.h" typedef struct{ u8 hour; u8 min; u8 sec; }_calendar; extern _calendar calendar; void RTC_Get(void); u8 RTC_Init(void); #endifrtc.c #include "rtc.h" #include "SysTick.h" #include "usart.h" _calendar calendar; void RTC_NVIC_Config() { NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel=RTC_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2; NVIC_InitStructure.NVIC_IRQChannelSubPriority=3; NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStructure); } void RTC_Get(void) { u32 time=0; time=RTC_GetCounter(); calendar.hour=time/3600; calendar.min=time%3600/60; calendar.sec=time%60; } u8 RTC_Init(void) { u8 temp=0; RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR|RCC_APB1Periph_BKP,ENABLE);//使能电源和后备域时钟 PWR_BackupAccessCmd(ENABLE);//开启RTC后备寄存器访问 if(BKP_ReadBackupRegister(BKP_DR1)!=0xA0A0)//第一次进入RTC { BKP_DeInit();/后备域寄存器初始化 RCC_LSEConfig(RCC_LSE_ON);//开启LSE外部晶振 while(RCC_GetFlagStatus(RCC_FLAG_LSERDY)==RESET&&temp return 1; } RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); RCC_RTCCLKCmd(ENABLE); RTC_WaitForLastTask(); RTC_WaitForSynchro(); RTC_ITConfig(RTC_IT_SEC,ENABLE); RTC_WaitForLastTask(); RTC_EnterConfigMode(); RTC_SetPrescaler(32767); RTC_WaitForLastTask(); RTC_SetCounter(0x11DB4); RTC_ExitConfigMode(); BKP_WriteBackupRegister(BKP_DR1,0xA0A); } else { RTC_WaitForSynchro(); RTC_ITConfig(RTC_IT_SEC,ENABLE); RTC_WaitForLastTask(); } RTC_NVIC_Config(); RTC_Get(); return 0; } void RTC_IRQHandler(void) { if(RTC_GetITStatus(RTC_IT_SEC)!=RESET) { RTC_Get(); printf("RTC Time:%d:%d:%d\t\n",calendar.hour,calendar.min,calendar.sec); } RTC_ClearITPendingBit(RTC_IT_SEC); }main.c #include "system.h" #include "led.h" #include "SysTick.h" #include "usart.h" #include "rtc.h" int main() { u8 i=0; SysTick_Init(72); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//ÖжÏÓÅÏȼ¶·Ö×é LED_Init(); USART1_Init(9600); RTC_Init(); while(1) { i++; if(i%20==0) { led1=!led1; } delay_ms(10); } } 4、红外遥控 4.1 传感器介绍红外遥控是一种利用波长为0.76~1.5μm之间的近红外线来传输控制信号的方法,它是一种无线控制技术,具备较强的抗干扰性、功耗低、成本低以及容易实现的特点。由于红外线无法穿透障碍物,因此对于同类产品的红外线遥控器接收器可以有相同的遥控频率或编码,大大方便了控制系统设计并且易于调试。红外遥控通信系统由红外线发射装置以及红外线接收设备两大部分组成。 4.2 配置步骤根据红外遥控通信控制系统的组成,对于红外遥控的配置分为如下两部分: 红外发射装置 红外发射装置比较常见的就是遥控器,由键盘电路、红外编码电路、电源电路以及红外发射电路组成。目前被大量使用的遥控器发出的红外线波长在940nm上下,可普通的发光二极管的形状相同,但是红外发光二极管发出光属于不可见光。典型的红外遥控器与红外二极管如下图中所示.
红外接收设备由红外接收电路、红外解码、电源以及应用电路组成。红外接收器的作用是将遥控器发射来的红外光信号转换成电信号,再经过放大、限幅、检波以及整形的步骤形成遥控指令脉冲信号,输送至单片机。一个红外接收头的实物如下图所示: 利用STM32的中断功能解码红外接收端受到的遥控指令,详细的代码如下: hw.h #ifndef _hw_H #define _hw_H #include "system.h" void HW_Init(void); u8 hw_getHigh(void); extern u32 hw_jsm; extern u8 hw_jsbz; #endifhw.c #include "hw.h" #include "SysTick.h" u32 hw_jsm; //存储接收码 u8 hw_jsbz; //接收标志 void HW_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG|RCC_APB2Periph_AFIO,ENABLE); GPIO_InitStructure.GPIO_Pin=GPIO_Pin_15; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU; GPIO_Init(GPIOG,&GPIO_InitStructure); GPIO_EXTILineConfig(GPIO_PortSourceGPIOG,GPIO_PinSource15); EXTI_ClearITPendingBit(EXTI_Line15); EXTI_InitStructure.EXTI_Line=EXTI_Line15; EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;//ϽµÑØ´¥·¢ EXTI_InitStructure.EXTI_LineCmd=ENABLE; EXTI_Init(&EXTI_InitStructure); NVIC_InitStructure.NVIC_IRQChannel=EXTI15_10_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0; NVIC_InitStructure.NVIC_IRQChannelSubPriority=1; NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStructure); } u8 hw_getHigh() //得到高电平持续的时间 { u8 t=0; while(GPIO_ReadInputDataBit(GPIOG,GPIO_Pin_15)==1) { t++; delay_us(20); if(t>=250) return t; } return t; } void EXTI15_10_IRQHandler(void) { u8 Tim=0,Ok=0,Data,Num=0; while(1) { if(GPIO_ReadInputDataBit(GPIOG,GPIO_Pin_15)==1) { Tim=hw_getHigh(); if(Tim>=250) //无效信号 break; if(Tim>=200&&Tim Data=1; /数据1 } else if(Tim>=10&&Tim hw_jsm u8 i=0; SysTick_Init(72); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); LED_Init(); USART1_Init(9600); HW_Init(); while(1) { if(hw_jsbz==1) { hw_jsbz=0; printf("红外接收码 %0.8X\r\n", hw_jsm); hw_jsm=0; } i++; if(i%20==0) { led1=!led1; } delay_ms(10); } } |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |