這幾天,學習組織了爲期5天的關於基於stm32的仿真的課程設計,最後一天,我們要進行實訓項目的測驗,要獨立完成一個項目,具體有以下要求:
原理圖繪製:
按鍵 6個
LED燈 4盞+1盞
蜂鳴器 1個
溫溼度傳感器 1個
要求:
(1)按下按鍵key1 , 開啓LED跑馬燈(4盞)
(2)按下按鍵key2 , 關閉LED跑馬燈(4盞)
(3)按下按鍵key3 , 控制獨立的LED 每500ms 閃爍一次-----使用定時器 TIM_cmd(TIM2,ENABLE)
(4)按下按鍵key4 , 關閉獨立的LED 閃爍-----使用定時器 TIM_cmd(TIM2,DISENABLE)
(5)按下按鍵key5 , 開啓蜂鳴器
(6)按下按鍵key6 , 關閉蜂鳴器
(7)溫溼度檢測報警,當溫度超過30度或者溼度超過80%時,蜂鳴器報警
加分項1:
添加2個按鍵
按下按鍵7,跑馬燈加速,按下按鍵8,跑馬燈減速
滿分項:
添加顯像管(seg),把溫溼度顯示在顯像管中。
因爲博主並不是主攻嵌入式開發的,所以5天時間對於嵌入式開發的研究還遠遠不夠,因此,對於此次作業項目,並沒有完成最後一個滿分項,但我還是想將此次完成的項目進行記錄,以作紀念。
一、Protues仿真部分
二、keil工程
#include "stm32f4xx.h"
#include "sys.h"
int SIGN = 10000;
void delay(int x)
{
int i,j;
for(i=0;i<x;i++)
for(j=0;j<SIGN;j++);
}
//延時函數--毫秒級別
//VAL計數器是24bit的寄存器,所以最大值是16777215
//所以nms的最大值是 199
void delay_ms(int nms){
uint32_t temp;
//延時nms毫秒需要的計數數值,把它交給重載計數器
SysTick->LOAD = 84000*nms;
//將VAL計數器的數值賦值爲0
SysTick->VAL = 0;
//將控制器的0bit位賦值爲1,開啓計數
SysTick->CTRL |= 0x1;
//循環檢測控制器得到16bit,當它爲1的時候,表示計數器由1減到0了
do{
temp = SysTick->CTRL;
}while( !(temp>>16 & 1) && (temp&0x1));
//temp>>16 & 1 結果爲真的時候,表示16bit數值已經爲1了,要跳出循環
//結果爲假的時候,表示16bit數值還是0,要繼續循環
//但是while的判斷語句是 邏輯真--循環 邏輯假---結束
//所以在最外面加上 ! 取反。
//temp&0x1爲真是要求控制器的0bit位數值爲1,即計數器一直在計數
//將控制器的0bit位賦值爲0,停止計數
SysTick->CTRL &= ~0x1;
//將計數器數值設置爲0
SysTick->VAL = 0;
}
//1微秒是84次脈衝
void delay_us(int nus){
uint32_t temp;
//延時nms毫秒需要的計數數值,把它交給重載計數器---會忽略小數
SysTick->LOAD = 84*nus;
SysTick->VAL = 0;
SysTick->CTRL |= 0x1;
do{
temp = SysTick->CTRL;
}while( !(temp>>16 & 1) && (temp&0x1));
SysTick->CTRL &= ~0x1;
SysTick->VAL = 0;
}
//配置LED引腳
void led(){
//定義GPIO結構體
GPIO_InitTypeDef a;
//使能LED燈GPIO分組的時鐘
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
//GPIO結構體賦值---也就是選擇引腳的配置參數
a.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_9;//選中引腳
a.GPIO_Mode = GPIO_Mode_OUT;//配置輸出模式----是主動設置引腳電平高低
a.GPIO_Speed = GPIO_Speed_100MHz;//選擇高速
a.GPIO_OType = GPIO_OType_PP;//推輓輸出
a.GPIO_PuPd = GPIO_PuPd_NOPULL;//無上下拉電阻
//配置GPIO的引腳函數---將選好的配置放入到GPIOA組裏面
GPIO_Init(GPIOA,&a);
}
//初始化蜂鳴器
void beep_init()
{
//定義GPIO結構體
GPIO_InitTypeDef a;
//使能LED燈的GPIO分組
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
//對GPIO結構體進行賦值
a.GPIO_Pin = GPIO_Pin_8;
a.GPIO_Mode = GPIO_Mode_OUT;//配置輸出模式----是主動設置引腳電平高低
a.GPIO_Speed = GPIO_Speed_100MHz;//選擇高速
a.GPIO_OType = GPIO_OType_PP;//推輓輸出
a.GPIO_PuPd = GPIO_PuPd_NOPULL;//無上下拉電阻
//配置GPIO的引腳函數---將選好的配置放入到GPIOA組裏面
GPIO_Init(GPIOB,&a);
//讓蜂鳴器停下
PEout(8)=0;
}
//蜂鳴器響
void beep_up(){
int i = 0;
for(i=0;i<2;i++)
delay_ms(100);
PBout(8)=1;
for(i=0;i<2;i++)
delay_ms(100);
PBout(8)=0;
}
//配置開關引腳
void btns(){
GPIO_InitTypeDef k;//定義GPIO結構體
EXTI_InitTypeDef b;//定義外部中斷控制器結構體
NVIC_InitTypeDef c;//定義中斷配置結構體
//使能KEY按鍵GPIO分組的時鐘
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
//使能EXTI的時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
//GPIO結構體賦值---也就是選擇引腳的配置參數
k.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
k.GPIO_Mode =GPIO_Mode_IN;//配置輸入模式---是檢測引腳電平高低
k.GPIO_Speed=GPIO_Speed_100MHz;//選擇高速
k.GPIO_PuPd = GPIO_PuPd_NOPULL;//無上下拉電阻
GPIO_Init(GPIOE,&k);
//發生外部中斷時的引腳
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource0);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource1);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource2);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource3);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource4);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource5);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource6);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource7);
//對EXTI結構體進行賦值
b.EXTI_Line = EXTI_Line0 | EXTI_Line1 | EXTI_Line2 | EXTI_Line3 | EXTI_Line4 | EXTI_Line5 | EXTI_Line6 | EXTI_Line7;//選擇輸入線0,1,2,3,4,5,6,7
b.EXTI_Mode = EXTI_Mode_Interrupt;//選擇中斷
b.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿觸發
b.EXTI_LineCmd = ENABLE;//使能
EXTI_Init(&b);//根據配置好的EXTI參數初始化外部中斷控制器
//開啓跑馬燈
c.NVIC_IRQChannel = EXTI0_IRQn;//通道爲EXTI0_IRQHandler
c.NVIC_IRQChannelCmd = ENABLE;//使能
c.NVIC_IRQChannelPreemptionPriority = 3;//搶佔優先級
c.NVIC_IRQChannelSubPriority = 0;//響應優先級
//根據配置好的NVIC參數初始化中斷服務
NVIC_Init(&c);
//關閉跑馬燈
c.NVIC_IRQChannel = EXTI1_IRQn;//通道爲EXTI1_IRQHandler
c.NVIC_IRQChannelCmd = ENABLE;//使能
c.NVIC_IRQChannelPreemptionPriority = 0;
c.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&c);
//開啓獨立燈
c.NVIC_IRQChannel = EXTI2_IRQn;//通道爲EXTI2_IRQHandler
c.NVIC_IRQChannelCmd = ENABLE;//使能
c.NVIC_IRQChannelPreemptionPriority = 1;//搶佔優先級
c.NVIC_IRQChannelSubPriority = 0;//響應優先級
//根據配置好的NVIC參數初始化中斷服務
NVIC_Init(&c);
//關閉獨立燈
c.NVIC_IRQChannel = EXTI3_IRQn;//通道爲EXTI3_IRQHandler
c.NVIC_IRQChannelCmd = ENABLE;//使能
c.NVIC_IRQChannelPreemptionPriority = 0;
c.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&c);
//開啓蜂鳴器
c.NVIC_IRQChannel = EXTI4_IRQn;//通道爲EXTI4_IRQHandler
c.NVIC_IRQChannelCmd = ENABLE;//使能
c.NVIC_IRQChannelPreemptionPriority = 2;
c.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&c);
//關閉蜂鳴器等
c.NVIC_IRQChannel = EXTI9_5_IRQn;
c.NVIC_IRQChannelCmd = ENABLE;//使能
c.NVIC_IRQChannelPreemptionPriority = 1;
c.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&c);
}
//目的:初始化TIM4,讓定時器每1秒發生五次中斷
//當前輸入頻率 84MHz , 可以設置預分頻器 8399 重裝載值 9999
void tim4_init(){
TIM_TimeBaseInitTypeDef a;//定義定時器的結構體
NVIC_InitTypeDef b;//定義中斷結構體
//開啓TIM4的時鐘使能
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
//設置時基單元
a.TIM_Prescaler = 8399;//預分頻器數值
a.TIM_Period = 4999;//重裝載值
a.TIM_CounterMode = TIM_CounterMode_Up;//計數器遞增模式
//a.TIM_CounterMode = 0;//定時的時候用不到濾波
//a.TIM_RepetitionCounter;//重複計數器僅限高級定時器使用
TIM_TimeBaseInit(TIM4, &a);
//初始化中斷配置
b.NVIC_IRQChannel = TIM4_IRQn;//中斷服務通道
b.NVIC_IRQChannelCmd = ENABLE;//中斷使能
b.NVIC_IRQChannelPreemptionPriority = 2;//搶佔優先級
b.NVIC_IRQChannelSubPriority = 0;//響應優先級
NVIC_Init(&b);
}
void TIM4_IRQHandler()
{
//判斷中斷是否真的發生----檢查中斷標誌位是否爲1
if( TIM_GetITStatus(TIM4,TIM_IT_Update) == SET )
{
//LED燈閃爍
PAout(9) ^=1;
//清除中斷標誌位
TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
}
}
//中斷服務函數
void EXTI0_IRQHandler()
{
SIGN=10000;
//判斷中斷是否真的發生
while(EXTI_GetITStatus(EXTI_Line0)==SET)//確實發生了中斷
{
PAout(0)=1;
delay(200);
PAout(0)=0;
PAout(1)=1;
delay(200);
PAout(1)=0;
PAout(2)=1;
delay(200);
PAout(2)=0;
PAout(3)=1;
delay(200);
PAout(3)=0;
}
}
void EXTI1_IRQHandler()
{
//判斷中斷是否真的發生
if(EXTI_GetITStatus(EXTI_Line1)==SET)//確實發生了中斷
{
//清除中斷標誌位,防止CPU認爲中斷在不停的發生
EXTI_ClearITPendingBit(EXTI_Line0);
//清除中斷標誌位,防止CPU認爲中斷在不停的發生
EXTI_ClearITPendingBit(EXTI_Line1);
}
}
void EXTI2_IRQHandler()
{
//判斷中斷是否真的發生
if(EXTI_GetITStatus(EXTI_Line2)==SET)//確實發生了中斷
{
//開啓定時器中斷
TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE);
//開啓定時器
TIM_Cmd(TIM4,ENABLE);
//清除中斷標誌位,防止CPU認爲中斷在不停的發生
EXTI_ClearITPendingBit(EXTI_Line2);
}
}
void EXTI3_IRQHandler()
{
//判斷中斷是否真的發生
if(EXTI_GetITStatus(EXTI_Line3)==SET)//確實發生了中斷
{
//關閉定時器中斷
TIM_ITConfig(TIM4,TIM_IT_Update,DISABLE);
//關閉定時器
TIM_Cmd(TIM4,DISABLE);
PAout(9)=0;
//清除中斷標誌位,防止CPU認爲中斷在不停的發生
EXTI_ClearITPendingBit(EXTI_Line3);
}
}
void EXTI4_IRQHandler()
{
//判斷中斷是否真的發生
while(EXTI_GetITStatus(EXTI_Line4)==SET)//確實發生了中斷
{
//開啓蜂鳴器
beep_up();
}
PBout(8)=0;
}
void EXTI9_5_IRQHandler()
{
//判斷中斷是否真的發生
if(EXTI_GetITStatus(EXTI_Line5)==SET)//確實發生了中斷
{
//清除中斷標誌位,防止CPU認爲中斷在不停的發生
EXTI_ClearITPendingBit(EXTI_Line4);
//清除中斷標誌位,防止CPU認爲中斷在不停的發生
EXTI_ClearITPendingBit(EXTI_Line5);
}
//判斷中斷是否真的發生
if(EXTI_GetITStatus(EXTI_Line0)==SET && EXTI_GetITStatus(EXTI_Line6)==SET)//確實發生了中斷
{
SIGN = 2000;
//清除中斷標誌位,防止CPU認爲中斷在不停的發生
EXTI_ClearITPendingBit(EXTI_Line6);
}
//判斷中斷是否真的發生
if(EXTI_GetITStatus(EXTI_Line0)==SET && EXTI_GetITStatus(EXTI_Line7)==SET)//確實發生了中斷
{
SIGN=20000;
//清除中斷標誌位,防止CPU認爲中斷在不停的發生
EXTI_ClearITPendingBit(EXTI_Line7);
}
}
//空閒狀態
void idle(){
//定義GPIO結構體
GPIO_InitTypeDef a;
//使能按鍵GPIO分組的時鐘
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
//GPIO結構體賦值---也就是選擇引腳的配置參數
a.GPIO_Pin = GPIO_Pin_0;//選中0引腳
a.GPIO_Mode = GPIO_Mode_OUT;//配置輸出模式----是主動設置引腳電平高低
a.GPIO_Speed = GPIO_Speed_100MHz;//選擇高速
a.GPIO_OType = GPIO_OType_PP;//推輓輸出
a.GPIO_PuPd = GPIO_PuPd_NOPULL;//無上下拉電阻
//配置GPIO的引腳函數---將選好的配置放入到GPIOA組裏面
GPIO_Init(GPIOD,&a);
PDout(0)=1;
}
void work(){
//定義GPIO結構體
GPIO_InitTypeDef k;
//使能KEY按鍵GPIO分組的時鐘
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
//GPIO結構體賦值---也就是選擇引腳的配置參數
k.GPIO_Pin = GPIO_Pin_0;
k.GPIO_Mode =GPIO_Mode_IN;//配置輸入模式---是檢測引腳電平高低
k.GPIO_Speed=GPIO_Speed_100MHz;//選擇高速
k.GPIO_PuPd = GPIO_PuPd_NOPULL;//無上下拉電阻
//k.GPIO_OType 不需要設置,因爲電流沒經過輸出控制器
GPIO_Init(GPIOD,&k);
}
//開始啓動
void start(){
idle();
PDout(0)=0;
delay_ms(18);
PDout(0)=1;
delay_us(30);
work();
}
//檢測DHT11是否啓動
int detect(){
int i=0,j=0;
while(PDin(0)==0){
delay_us(10);
i++;
if(i>10){
return -1;
}
}
while(PDin(0)==1){
delay_us(10);
j++;
if(j>10){
return -1;
}
}
return 0;
}
//接收輸入數據
int readdata(){
int a=0,i=0;
for(i=0;i<8;i++){
while(PDin(0)==0);
delay_us(50);
if(PDin(0)==1)
a |=1<<(7-i);
while(PDin(0)==1);
}
return a;
}
//判斷溫溼度
void judge(int T,int H){
if(T>30 || H>80){
//開啓蜂鳴器
beep_up();
}
}
int main()
{
int i,data[5];
int T=0,H=0;
//設置滴答定時器的輸入頻率
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);
//設置中斷優先級分組
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
led();
tim4_init();
btns();
beep_init();
while(1)
{
for(i=0;i<5;i++)
delay_ms(100);
start();
if(detect() != 0)
continue;
for(i=0;i<5;i++)
data[i]=readdata();
T=data[2];
H=data[0];
judge(T,H);
}
}