stm32遥控时钟

设计目的

本次设计为STM32初学成品,基于STM32F103ZET6的红外遥控电子时钟,采用了战舰开发板配套的TFTLCD显示屏,红外遥控器HS0038等硬件,实现了遥控控制日期时间的更改,闹钟的设置,响铃等功能。

设计原理

该设计运用到两个定时器,定时器3用于1s定时,定时器4用于捕获遥控器发出的红外信号。通过TFTLCD显示屏显示当前日期、当前时间以及自定义的闹钟时间,纯人工定位画点(后来才发现有现成的RCT)。TFTLCD由FSMC(flexible static memory controller)驱动,颜色编码格式RGB565,遥控器仅设置三个按键有效,ALIENTEK控制模式切换,UP控制加一操作,DOWN控制减一操作。
以下有几个重要的概念:

  1. RGB565
    RGB565使用16位表示一个像素,这16位中的5位用于R,6位用于G,5位用于B。程序中通常使用一个字(WORD,一个字等于两个字节)来操作一个像素。当读出一个像素后,这个字的各个位意义如下:
    高字节 低字节
    R R R R R G G G G G G B B B B B
  2. FSMC
    FSMC(Flexible Static Memory Controller,可变静态存储控制器)是STM32系列采用的一种新型的存储器扩展技术。在外部存储器扩展方面具有独特的优势,可根据系统的应用需要,方便地进行不同类型大容量静态存储器的扩展。
    FSMC管理1 GB的映射地址空间。该空间划分为4个大小为256 MB的BANK,每个BANK又划分为4个64 MB的子BANK,如表1所列。FSMC的2个控制器管理的映射地址空间不同。NOR Flash控制器管理第1个BANK,NAND/PC Card控制器管理第2~4个BANK。由于两个控制器管理的存储器类型不同,扩展时应根据选用的存储设备类型确定其映射位置。其中,BANK1的4个子BANK拥有独立的片选线和控制寄存器,可分别扩展一个独立的存储设备,而BANK2~BANK4只有一组控制寄存器。
  3. NEC
    NEC协议的特征:8位地址和8位指令长度;地址和命令两次传输;PWM脉冲宽度调制,以发射红外载波的占空比代表“0”和“1”;载波频率为38KHz;位时间为1.125ms和2.25ms。
    NEC码位的定义:一个脉冲对应560us的连续载波,一个逻辑1传输需要2.25ms(560us脉冲+1680us低电平),一个逻辑0的传输需要1.125ms(560us脉冲+560us低电平)。而遥控接收头在收到脉冲时为低电平,在没有收到脉冲时为高电平,因此,我们在接收头端收到的信号为:逻辑1应该是560us低+1680us高,逻辑0应该是560us低+560us高。
    NEC遥控指令的数据格式为:同步码头(引导码)+地址码+地址反码+控制码+控制反码。

硬件电路

stm32遥控时钟_第1张图片
stm32遥控时钟_第2张图片

软件设计

  1. main.c
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "lcd.h"
#include "usart.h"
#include "remote.h"
#include "timer.h"
#include "clock.h"
#include "beep.h"

volatile u32 year=2019;
volatile u32 month=12;
volatile u32 day=12;
volatile u32 hour=23;
volatile u32 minute=59;
volatile u32 second=50;
volatile u32 chour=0;
volatile u32 cminute=0;
volatile u32 csecond=0;

u8 setMode=0;//设置模式

void inc(void)//加1
{
    switch(setMode)
    {
    case 1:
    {
        year++;
        if(year==2100)year=2000;
        break;
    }
    case 2:
    {
        month++;
        if(month==13)month=1;
        break;
    }
    case 3:
    {
        day++;
        if(day==31)day=1;
        break;
    }
    case 4:
    {
        hour++;
        if(hour==24)hour=0;
        break;
    }
    case 5:
    {
        minute++;
        if(minute==60)minute=0;
        break;
    }
    case 6:
    {
        second++;
        if(second==60)second=0;
        break;
    }
    case 7:
    {
        chour++;
        if(chour==24)chour=0;
        break;
    }
    case 8:
    {
        cminute++;
        if(cminute==60)cminute=0;
        break;
    }
    case 9:
    {
        csecond++;
        if(csecond==60)csecond=0;
        break;
    }
    }
}

void dec(void)//减1
{
    switch(setMode)
    {
    case 1:
    {
        year--;
        if(year==1999)year=2100;
        break;
    }
    case 2:
    {
        month--;
        if(month==0)month=12;
        break;
    }
    case 3:
    {
        day--;
        if(day==0)day=30;
        break;
    }
    case 4:
    {
        hour--;
        if(hour<=0)hour=23;
        break;
    }
    case 5:
    {
        minute--;
        if(minute<=0)minute=59;
        break;
    }
    case 6:
    {
        second--;
        if(second<=0)second=59;
        break;
    }
    case 7:
    {
        chour--;
        if(chour<=0)chour=23;
        break;
    }
    case 8:
    {
        cminute--;
        if(cminute<=0)cminute=59;
        break;
    }
    case 9:
    {
        csecond--;
        if(csecond<=0)csecond=59;
        break;
    }
    }
}

int main(void)
{
    u8 key;

    delay_init();	    	 //延时函数初始化
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
    //uart_init(115200);	 	//串口初始化为115200
    LED_Init();			     //LED端口初始化
    BEEP_Init();         	//初始化蜂鸣器端口
    LCD_Init();
    Remote_Init();			//红外接收初始化
    TIM3_Int_Init(9999,7199);//10Khz的计数频率,计数到10000为1s

    while(1)
    {
        CLOCK_Show(year,month,day,hour,minute,second,chour,cminute,csecond);
        if(hour==chour&&minute==cminute&&second==csecond) {//闹钟响
            BEEP=1;
            delay_ms(1000);
            BEEP=0;
        }

        if(Remote_Scan())
        {
            delay_ms(2800);//2000-3000都可
            key=Remote_Scan();

            switch(key)
            {
            case 0://ERROR
                break;
            case 162://POWER
                break;
            case 98://UP
            {
                inc();
                break;
            }
            case 2://PLAY
                break;
            case 226://ALIENTEK
            {
                setMode++;
                if(setMode==10)setMode=0;
                break;
            }
            case 194://RIGHT
                break;
            case 34://LEFT
                break;
            case 224://VOL-
                break;
            case 168://DOWN
            {
                dec();
                break;
            }
            case 144://VOL+
                break;
            case 104://1
                break;
            case 152://2
                break;
            case 176://3
                break;
            case 48://4
                break;
            case 24://5
                break;
            case 122://6
                break;
            case 16://7
                break;
            case 56://8
                break;
            case 90://9
                break;
            case 66://0
                break;
            case 82://DELETE
                break;
            }

        }
        if(setMode)
        {
            LED1=1;
            LED0=0;

        } else
        {
            LED0=1;
            LED1=0;
        }
    }
}
  1. clock.c
#include "clock.h"

void CLOCK_Show(u32 year,u32 month,u32 day,u32 hour,u32 minute,u32 second,u32 chour,u32 cminute,u32 csecond)//初始化日期时间
{

    POINT_COLOR=RED;		//设置字体为红色,默认也是红色
    LCD_ShowString(30,50,200,24,24,"DATE:");
    LCD_ShowString(90,80,1,24,24,"/");//年60 其他30 符号10
    LCD_ShowString(140,80,1,24,24,"/");//

    LCD_ShowString(30,110,200,24,24,"TIME:");
    LCD_ShowString(60,140,1,24,24,":");
    LCD_ShowString(110,140,1,24,24,":");

    LCD_ShowString(30,170,200,24,24,"CLOCK:");
    LCD_ShowString(60,200,1,24,24,":");
    LCD_ShowString(110,200,1,24,24,":");


    POINT_COLOR=BLUE;
    BACK_COLOR=(setMode==1)?GRAY:WHITE;
    LCD_ShowNum(30,80,year,4,24);//行距30
    BACK_COLOR=(setMode==2)?GRAY:WHITE;
    LCD_ShowNum(110,80,month,2,24);
    BACK_COLOR=(setMode==3)?GRAY:WHITE;
    LCD_ShowNum(160,80,day,2,24);

    BACK_COLOR=(setMode==4)?GRAY:WHITE;
    LCD_ShowNum(30,140,hour,2,24);
    BACK_COLOR=(setMode==5)?GRAY:WHITE;
    LCD_ShowNum(80,140,minute,2,24);
    BACK_COLOR=(setMode==6)?GRAY:WHITE;
    LCD_ShowNum(130,140,second,2,24);

    BACK_COLOR=(setMode==7)?GRAY:WHITE;
    LCD_ShowNum(30,200,chour,2,24);
    BACK_COLOR=(setMode==8)?GRAY:WHITE;
    LCD_ShowNum(80,200,cminute,2,24);
    BACK_COLOR=(setMode==9)?GRAY:WHITE;
    LCD_ShowNum(130,200,csecond,2,24);

    BACK_COLOR=WHITE;//字体背景色

}
  1. clock.h
#ifndef __CLOCK_H
#define __CLOCK_H
#include "sys.h"
#include "lcd.h"

extern u8 setMode;

void CLOCK_Show(u32 year,u32 month,u32 day,u32 hour,u32 minute,u32 second,u32 chour,u32 cminute,u32 csecond);//初始化日期,时间,闹钟

#endif
  1. timer.c
#include "timer.h"
#include "delay.h"
#include "led.h"

//通用定时器3中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器3!
void TIM3_Int_Init(u16 arr,u16 psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能

    //定时器TIM3初始化
    TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
    TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位

    TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断

    //中断优先级NVIC设置
    NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
    NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器


    TIM_Cmd(TIM3, ENABLE);  //使能TIMx
}
//定时器3中断服务程序
void TIM3_IRQHandler(void)   //TIM3中断
{

    if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)  //检查TIM3更新中断发生与否
    {
        TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx更新中断标志
        if(!setMode)
        {
            second++;
            if(second==60)
            {
                second=0;
                minute++;
                if(minute==60)
                {
                    minute=0;
                    hour++;
                    if(hour==24)
                    {
                        hour=0;
                        day++;
                        if(day==31)
                        {
                            day=1;
                            month++;
                            if(month==13)
                            {
                                month=1;
                                year++;
                                if(year==3001)
                                {
                                    year=2000;

                                }
                            }
                        }
                    }
                }
            }
        }

    }
}
  1. timer.h
#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"

extern volatile u32 day,month,year;
extern volatile u32 second,minute,hour;
extern u8 setMode;

void TIM3_Int_Init(u16 arr,u16 psc);

#endif

调试结果

stm32遥控时钟_第3张图片stm32遥控时钟_第4张图片
图左为正常工作模式(计时模式),图右为设置模式,灰色背景表示当前设置的位置,蓝色数字都可以通过遥控设置。由上往下分别显示当前日期、当前时间、自定义闹钟,按遥控上的ALIENTEK键进入或退出设置模式。

设计总结

本次课程设计调试过程中,TFTLCD对红外遥控按键的反应是循环检测,支持连按的,一时找不到检测松手的入口,索性直接采用延迟反应的笨办法。2000ms过慢,3000ms又过快,因此选择了2800ms延迟按键反应时间。注意蜂鸣器需要三极管驱动,GPIO口输出高电平放大电流。TFTLCD显示的字体背景颜色改变后要切换回默认状态即白色,以保证设置的时候始终只有一个位置是灰色。LED由GPIO输出低电平点亮,在设置模式下DS0(红灯)亮,正常工作模式下DS1(黄灯)亮 。由于时间关系(一天时间太短了莫得办法),闹钟功能简单的由蜂鸣器响铃一秒完成,可后续扩展成为音乐闹铃。红外遥控键盘部分也可以扩展相应要实现的功能,比如数字键盘设置。

你可能感兴趣的:(STM32嵌入式)