主頁 >  其他 > STM32單片機(六).傳感器的使用

STM32單片機(六).傳感器的使用

2021-04-24 15:11:40 其他

傳感器是單片機外圍電路中最常見的模塊,在搭配了各種形式的傳感器電路后,就可以采集到的更多的環境資訊,在本章節中,主要介紹呼吸燈、溫度傳感器、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輸出配置步驟

  1. 使能定時器及埠時鐘,并設定引腳復用映射;
    呼叫函式: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_CH1PA6PB4PC6
TIM_CH2PA7PB5PC7
TIM_CH3PB0PB0PC8
TIM_CH4PB1PB1PC9
  1. 初始化定時器引數;
    呼叫函式:void TIM_TimeBaseInit(TIM_TypeDef*TIMx,TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
    配置細節參見前一章節,
  2. 初始化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);
  1. 開啟定時器;
    呼叫函式:void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
  2. 修改TIMx_CCRx的值控制占空比;
    控制占空比需要通過修改TIMx_CCR1的值實作:void TIM_SetComparel(TIM_TypeDef* TIMx, uint32_t Comparel);
  3. 使能TIMx在CCRx上的預裝載暫存器;
    呼叫函式:void TIM_OCxPreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload)
    TIMx為選定的定時器;TIM_OCPreload取值為TIM_OCPreloda_Enable或TIM_OCPreload_Disable,
  4. 使能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轉換的模式有單次轉換和連續轉換,

  1. 單次轉換模式下,ADC執行一次轉換,通過ADC_CR2暫存器的SWSTART位啟動,也可以通過外部觸發啟動,一旦所選定的通道轉換完成,轉換結果將被保存在ADC_DR暫存器中,ADC停止,直到下一次被啟動,
  2. 連續轉換模式下,ADC結束一次轉換后繼續開始下一次的轉換,CONT位為1時,通過將ADC_CR2暫存器的SWSTRT位置1或外部觸發的方式啟動該模式,

對于ADC的庫函式配置版如下所示:

  1. 使能埠時鐘以及ADC時鐘,設定引腳模式為模擬輸入;
    ADCx_INC0~ADCx_IN5為外部通道,分別對應于芯片的一個引腳,一次需要使能GPIOX以及ADCx時鐘(掛載在APB2總線上),呼叫函式:RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOX|RCC_APB2Periph_ADCx, ENABLE);
    然后將對應的引腳設定為模擬輸入模式(用于采集電壓信號):
  2. 設定ADC的分頻因子;
    分頻因子要保證ADC的時鐘(ADCCLK)小于14MHz,ADC的時鐘有72/分頻因子計算的出,可呼叫函式:RCC_ADCCLKConfig(RCC_PCLK2_Divx);
  3. 初始化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轉換通道數目,根據需要選擇;

  1. 使能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));
  2. 讀取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模數轉換成數字信號,對于溫度傳感器的設定需要考慮到:

  1. 激活ADC內部通道,置ADC_CCR的TSVREFE為1;
  2. 讀取輸入埠的電壓信號,通過公式將其轉換為溫度:
    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),具體步驟如下:

  1. 初始化ADC引數,開啟內部溫度控制器,呼叫函式:ADC_TempSensorVrefintCmd(ENABLE);
  2. 讀取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),具體步驟如下:

  1. 使能電源時鐘和后備域時鐘,開啟RTC后備暫存器寫訪問;
    呼叫庫函式:RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); //使能電源時鐘 RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE); //使能后備域時鐘 PWR_BackupAccessCmd(ENALE); //使能后備寄存

  2. 復位備份區域,開啟LSE時鐘;
    在開啟后備暫存器寫訪問后,首先要對該區域進行復位操作,之后使能LSE時鐘,呼叫函式:BKP_DeInit(); //復位備份區域 RCC_LSEConfig(RCC_LSE_ON); //開啟LSE時鐘

  3. 使能RTC時鐘;
    選擇LSE作為RTC時鐘源,之后使能RTC時鐘,呼叫函式:RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); RCC_RTCCLKCmd(ENABLE);

  4. 設定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);

  5. 更新配置,設定RTC中斷分組;
    呼叫配置更新函式:RTC_ExitConfigMode();
    在備份區域寫用戶資料:void BKP_WriteBackupRegister(uint16_t BKP_DR, uint16_t Data);
    讀取備份區域內容:uint16_t BKP_ReadBackupRegister(uint16_t BKP_DR);
    配置中斷分組:NVIC_Init

  6. 撰寫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 配置步驟

根據紅外遙控通信控制系統的組成,對于紅外遙控的配置分為如下兩部分:

  1. 紅外發射裝置
    紅外發射裝置比較常見的就是遙控器,由鍵盤電路、紅外編碼電路、電源電路以及紅外發射電路組成,目前被大量使用的遙控器發出的紅外線波長在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高電平組成,在按下遙控器的一個按鍵一幀資料發送后,按鍵沒有松開則發送連發碼,通過統計連發碼的次數可以計算出按鍵按下的長短或次數,

  1. 紅外接收設備

紅外接收設備由紅外接收電路、紅外解碼、電源以及應用電路組成,紅外接收器的作用是將遙控器發射來的紅外光信號轉換成電信號,再經過放大、限幅、檢波以及整形的步驟形成遙控指令脈沖信號,輸送至單片機,一個紅外接收頭的實物如下圖所示:

紅外接收頭在沒有受到紅外脈沖的時候為高電平,收到脈沖時為低電平,因此可以通過外部中斷的下降沿觸發中斷模式在中斷服務函式中計算高電平的時間來判斷接收到的二進制碼時0或1,在STM32的紅外遙控接收端的軟體程式框架大致如下:

  1. 使能IO口以及AFIO時鐘,復用引腳IO到外部中斷線上,初始化中斷系統;
  2. 撰寫中斷服務函式,包含紅外解碼部分;
  3. 撰寫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

標籤:其他

上一篇:樂鑫代理啟明云端分享|基于ESP32-S2彩色觸摸屏86面板方案

下一篇:STM32-I2C總線驅動程式分析

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more