傳感器是單片機外圍電路中最常見的模塊,在搭配了各種形式的傳感器電路后,就可以采集到的更多的環境資訊,在本章節中,主要介紹呼吸燈、溫度傳感器、RTC實時時鐘以及紅外遙控模塊的控制使用,
1、PWM實作呼吸燈的效果
1.1 PWM脈沖寬度調制
PWM是利用單片機的數字輸出來對模擬電路進行控制的技術,其應用包含電機控制、通信、開關電源等等,PWM是一種對模擬信號進行數字編碼的方法,其本質上還是數字信號,也就是在任意時刻埠輸出要么是高電平要么就是低電平,電壓或者電流是以一種通或端的重復脈沖序列被加到模擬負載上,因此只要帶寬足夠,理論上任何的模擬信號都可以使用PWM技術進行數字編碼,如下圖中所示,

在STM32F1中除了基本定時器TIM6與TIM7外,其它定時器都具備PWM輸出的功能,輸出PWM就是對外輸出脈寬可調的方波信號,該方波信號的頻率由自動重裝暫存器ARR決定,占空比由比較暫存器CCR決定,因此改變CCR的值就會使PWM輸出信號占空比發生改變(占空比=周期內高電平時間/周期總時間),PWM最常用的輸出模式是PWM1和PWM2,其區別如下表中所示,按照PWM計數器CNT的計數方式,可分為邊沿對齊模式;中心對齊模式,
| 模式 | 計數器CNT計數方式 | 詳細 |
|---|---|---|
| PWM1 | 遞增 | CNT<CCR,通道CH為有效,否則為無效 |
| PWM1 | 遞減 | CNT>CCR,通道CH為無效,否則為有效 |
| PWM2 | 遞增 | CNT<CCR,通道CH為無效,否則為有效 |
| PWM2 | 遞減 | CNT>CCR,通道CH為有效,否則為無效 |
1.2 PWM輸出配置步驟
- 使能定時器及埠時鐘,并設定引腳復用映射;
呼叫函式:RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//使能 TIM3 時鐘;
TIM3的CH1通道為引腳PA6,在這里需要將這個通道映射到其它IO口上(視硬體電路而定),使用到了GPIO的復用功能,對于TIM3定時器的復用功能如下表中所示,
開啟重映射功能需要啟動AFIO時鐘:RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
呼叫復用映射功能函式:GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE);
配置所映射的管腳模式為復用推挽輸出:GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
| 復用功能 | TIM3_REMAP[1:0]=00,沒有重映像 | TIM3_REMAP[1:0]=10,部分重映像 | TIM3_REMAP[1:0]=11,完全重映像 |
|---|---|---|---|
| TIM_CH1 | PA6 | PB4 | PC6 |
| TIM_CH2 | PA7 | PB5 | PC7 |
| TIM_CH3 | PB0 | PB0 | PC8 |
| TIM_CH4 | PB1 | PB1 | PC9 |
- 初始化定時器引數;
呼叫函式:void TIM_TimeBaseInit(TIM_TypeDef*TIMx,TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
配置細節參見前一章節, - 初始化PWM輸出引數;
呼叫函式:oid TIM_OCxInit(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
其中TIMx表示的是PWM輸出通道可取值為1/2/3/4;結構體TIM_OCInitTypeDef的成員變數為:
typedef struct
{
uint16_t TIM_OCMode; //比較輸出模式
uint16_t TIM_OutputState; //比較輸出使能
uint16_t TIM_OutputNState; //比較互補輸出使能,高級定時器
uint16_t TIM_Pluse; //脈沖寬度
uint16_t TIM_OCPolarity; //輸出極性
uint16_t TIM_OCNPolarity; //互補比較輸出極性,高級定時器
uint16_t TIM_OCIdleState; //空閑狀態下輸出狀態,高級定時器
uint16_t TIM_OCNIdleState; //空閑狀態下比較輸出狀態,高級定時器
}
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);
#endif
pwm.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執行一次轉換,通過ADC_CR2暫存器的SWSTART位啟動,也可以通過外部觸發啟動,一旦所選定的通道轉換完成,轉換結果將被保存在ADC_DR暫存器中,ADC停止,直到下一次被啟動,
- 連續轉換模式下,ADC結束一次轉換后繼續開始下一次的轉換,CONT位為1時,通過將ADC_CR2暫存器的SWSTRT位置1或外部觸發的方式啟動該模式,
對于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轉換的使用,在這里溫度傳感器的輸出信號就是一個電壓值的變化,需要通過A/D模數轉換成數字信號,對于溫度傳感器的設定需要考慮到:
- 激活ADC內部通道,置ADC_CCR的TSVREFE為1;
- 讀取輸入埠的電壓信號,通過公式將其轉換為溫度:
T(oC) = ( (V25 - Vsense) / Avg_slope ) + 25;
式中,V25 = Vsense在25 oC時的電壓數(1.43V);Avg_slope=溫度與Vsense曲線的平均斜率(4.3mV/ oC)
2.2 配置步驟
對于溫度傳感器的配置,需要用到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);
#endif
adc.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<times;t++)
{
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //軟體啟動ADC
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
temp_val+=ADC_GetConversionValue(ADC1);
delay_ms(5);
}
return temp_val/times;
}
int Get_Temperture(void)
{
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<0)
{
tempreture=-tempreture;
printf("節點溫度為:-%.2f C\r\n",(float) tempreture/100-1);
}
else
{
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的內部結構圖如下所示:

3.2 配置步驟
對于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);
#endif
rtc.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<250)//2.5秒為開啟即退出
{
temp++;
delay_ms(10);
}
if(temp>=250)/初始化失敗,晶振不穩定
{
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);//?D??ó??è??·?×é
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上下,可普通的發光二極管的形狀相同,但是紅外發光二極管發出光屬于不可見光,典型的紅外遙控器與紅外二極管如下圖中所示.
通常紅外遙控為了提高抗干擾性和降低電源消耗,采用了載波的方式傳輸二進制編碼,載波的頻率一般為38kHZ由發射端的晶振頻率所決定,常用的二進制編碼脈沖為NEC Protocol的PWM碼和Philips RC-5 Protocol的PWM碼,當采用NEC碼時,一個脈沖對應560us的連續載波,傳輸1需要2.25ms(560us的脈沖+1680us的低電平信號),傳輸0需要1.125ms(560us的脈沖+560us的低電平信號),NEC遙控指令碼的資料格式分為引導碼、地址碼、地址反碼、控制碼以及控制反碼,引導碼是一個9ms的低電平和一個4.5ms的高電平組成;地址碼、地址反碼、控制碼以及控制反碼為8位資料格式,按照低位在前、高位在后的順序發送,NEC中的連發碼是有9ms低電平+2.5ms高電平+0.56ms低電平+97.94高電平組成,在按下遙控器的一個按鍵一幀資料發送后,按鍵沒有松開則發送連發碼,通過統計連發碼的次數可以計算出按鍵按下的長短或次數,
- 紅外接收設備
紅外接收設備由紅外接收電路、紅外解碼、電源以及應用電路組成,紅外接收器的作用是將遙控器發射來的紅外光信號轉換成電信號,再經過放大、限幅、檢波以及整形的步驟形成遙控指令脈沖信號,輸送至單片機,一個紅外接收頭的實物如下圖所示:

紅外接收頭在沒有受到紅外脈沖的時候為高電平,收到脈沖時為低電平,因此可以通過外部中斷的下降沿觸發中斷模式在中斷服務函式中計算高電平的時間來判斷接收到的二進制碼時0或1,在STM32的紅外遙控接收端的軟體程式框架大致如下:
- 使能IO口以及AFIO時鐘,復用引腳IO到外部中斷線上,初始化中斷系統;
- 撰寫中斷服務函式,包含紅外解碼部分;
- 撰寫main函式,
4.3 應用示例
利用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;
#endif
hw.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<250)
{
Ok=1; //起始信號
}
else if(Tim>=60&&Tim<90)
{
Data=1; /資料1
}
else if(Tim>=10&&Tim<50)
{
Data=0; //資料0
}
if(Ok==1)
{
hw_jsm<<=1;
hw_jsm+=Data;
if(Num>=32)
{
hw_jsbz=1;
break;
}
}
Num++;
}
}
EXTI_ClearITPendingBit(EXTI_Line15);
}
main.c
#include "system.h"
#include "led.h"
#include "SysTick.h"
#include "usart.h"
#include "hw.h"
int main()
{
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);
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/279665.html
標籤:其他
