嵌入式 LED 万年历
嵌入式作业 |
20130080476 孙飞 |
软件工程 1班 |
2016-1-7 |
目录
一作业介绍:... 1
1.1主控单元和按键部分设计... 3
1.2部分引脚设计... 3
二项目实现:... 4
2.1 主函数... 4
2.2 函数RTC_SetTIMEConfig. 7
2.2.1定义属性... 7
2.2.2函数... 8
2.3 RCC_Configuration()10
2.4万年历数据的处理和显示函数... 13
2.4.1函数原型:wanchuliday();13
2.4.2 函数原型:wanxians();14
2.4.3 在LCD12864显示当前时间值... 15
2.4.4显示当前时间值... 16
2.5配置行列式键盘串口引脚... 18
2.5.1 GPIO_Config_key(void)18
2.5.2 keyscan()19
2.6 delay延时模块化程序设计... 21
2.6.1 delay(uint32_t time,uint8_t tm)22
三 项目截图及连线:... 26
一作业介绍:
作业要求:
通过一学期的学习,总结设计出万年历,能在屏幕上显示万年历并进行设置。实现时必须使用到考试用到的中断和定时器。
概要:
本作业使用的12864,通过中断实现设置时间,定时器定时进行更新时间,组成一个万年历,在主板显示屏上显示出来万年历,当前时间。
采用12864作为主控芯片,利用它定期的读取时钟芯片中的时间并显示在LCD上;通过算法得出阴历日期并显示在LCD。
定时单元:使用TIM3定时器,向上技术模式。计数5000次即500MS.通过一个全局变量作为分频后得到一秒的定时中断。
显示单元:采用LCD12864点阵显示,操作简单,成本低廉。这个实例采用了串口的方式实现显示,接口定义为:PB7—RST///PSB—PA4通过程序中的KEY.C文件中修改,更改接口后记得打开时钟。
选择LCD1602液晶显示模块。LCD1602是字符点阵系列液晶模块。它是一类专门用于显示字母、数字、符号等的点阵型液晶显示模块,
分为四位和八位数据传输方式,提供5*7点阵+光标和5*10点阵+
光标的显示模式。
对于程序的解释我在第二部分的代码都做了重要的注释,看起来应该比较简单。
项目的截图以及连线的部分在第三部分。
由于在平时实验以及最后考试中我们全都用过中断和定时器,就不详细介绍,全部通过代码和注释就能明白。
1.1主控单元和按键部分设计
1.2部分引脚设计
输入/输出引脚 P0.0~ P0.7、P1.0~P1.7、P2.0~ P2.7 和P3.0~P3.7
① P0端口(P0.0~ P0.7) P0是一个8位漏极开路型双向I/O端口。作为输出口用时,每位能以吸收电流的方式驱动8个TTL输入,对端口写1时,又可作高阻抗输入端用。
②P1端口(P1.0~ P1.7) P1是一个带有内部上拉电阻的8位双向I/O端口。P1的输出缓冲器可驱动(吸收或输出电流方式)4个TTL输入。对端口写1时,通过内部的上拉电阻把端口拉到高电位,这时可用作输入口。作输入口时,因为有内部的上拉电阻,那些被外部信号拉低的引脚会输出一个电流。
③P2端口 (P2.0~P2.7) P2是一个带有内部上拉电阻的8位双向I/O端口。P2的输出缓冲器可驱动(吸收或输出电流方式)4个TTL输入。对端口写1时,通过内部的上拉电阻把端口拉到高电位,这时可用作输入口。P2作输入口使用时,因为有内部的上拉电阻,那些被外部信号拉低的引脚会输出一个电流。 ④P3端口(P3.0~P3.7) P3口管脚是8个带内部上拉电阻的双向I/O口,可接收输出4个TTL门电流。当P3口写入“1”后,它们被内部上拉为高电平,并用作输入。作为输入,由于外部下拉为低电平,P3口将输出电流
二项目实现:
2.1 主函数
/***********************************************************头文件名:main.c
201300800476 孙飞
使用说明:
将LCD12864的PSB与GND短接,RE--PB.12,RW--PB.13,E--PB.15;
将行列式键盘S1-S8依次与PD.00-PD.07连接好(S1-S4控制行,S5-S8控制列)
***********************************************************/
#include "stm32f10x.h"
#include "delay.h"
#include "rcc.h"
#include "stdio.h"
/*时间结构体*/
struct rtc_time systmtime;
/**
* @brief 主函数
* @param 无
* @retval 无
*/
/*主函数*/
int main(void)
{
lcd12864_init();/*初始化lcd,同中断中的初始化一样,不做详细解释。*/
delay(50,ms);//延时50毫秒
//显示内容
lcd12864_display(1,2,"--万年历系统--");
lcd12864_display(2,1,"启动中......");
lcd12864_display(3,1,"请稍等");
delay(1000,ms);
GPIO_Config_key();/*GPIO的配置,有单独的接口设计,I/O端口置输入输出时有不同的输出、检测等。*/
NVIC_Configuration();//NVIC配置
RTC_CheckAndConfig(&systmtime); /*初始化检查,如果按了复位键或彻底掉电情况,进入设置以及设置完时间后,
进行检查更改错误、实现更新.*/
//主循环
while(1)
{
send_command(0x01); //清屏
delay(50,ms);//延迟
Time_Show(&systmtime);//时间进实现农历转化,最终显示出来
send_command(0x01); //清屏
delay(50,ms);
RTC_SetTIMEConfig(&systmtime);/* 配置RTC输入,用于读取RTC时间的结构体指针*/
}
}
2.2 函数RTC_SetTIMEConfig
* 函数名:RTC_SetTIMEConfig
* 描述 :配置RTC
* 输入 :用于读取RTC时间的结构体指针
* 输出 :无
* 调用 :外部调用
2.2.1定义属性
在函数实现之前先设置变量,使得设置的初始值得到初始化。
static uint32_t FirstDisplay = 1;
uint8_tyejingtable[]={0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x20};
uint8_t wandisplay[12];
uint8_t keyaa=0;
uint8_t keyz=0;
uint8_t *WEEK_STR[] = {"日", "一", "二", "三", "四", "五", "六"};
uint8_t *zodiac_sign[] = {"猪", "鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗"};
2.2.2函数
void RTC_SetTIMEConfig(struct rtc_time*tm)
{
/*RTC 初始化*/
RCC_Configuration();
/*重新配置时间*/
/*通过使用超级终端用户调整时间*/
Time_Adjust(tm);
BKP_WriteBackupRegister(BKP_DR1,0xA5A5);
/*定义了时钟输出宏,则配置校正时钟输出到PC13*/
#ifdef RTCClockOutput_Enable
/*使压水堆和BKP时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR| RCC_APB1Periph_BKP, ENABLE);
/*允许访问BKP域*/
PWR_BackupAccessCmd(ENABLE);
/*禁用篡改引脚*/
BKP_TamperPinCmd(DISABLE);/*输出RTCCLK / 64引脚的tamperfunctionality篡改,必须禁用*/
/*时钟使能输出篡改销*/
BKP_RTCOutputConfig(BKP_RTCOutputSource_CalibClock);
#endif
/*清除重置标志*/
RCC_ClearFlag();
}
2.3 RCC_Configuration()
* 函数原型:RCC_Configuration();
* 功能:RTC实时时钟的配置
void RCC_Configuration(void)
{
/*使压水堆和BKP时钟*/
//使能APB1外设PWR and BKP 的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR| RCC_APB1Periph_BKP,ENABLE);
/*允许访问BKP域*/
//允许RTC和后备寄存器的访问
PWR_BackupAccessCmd(ENABLE);
//PWR_CR的DBP位= 1
/*复位备份域*/
//将外设BKP的全部 寄存器重设为缺省值
BKP_DeInit(); //将外设 BKP 的全部寄存器重设为缺省值
/*使LSE有效 */ //设置外部低速时钟
RCC_LSEConfig(RCC_LSE_ON); //打开外部低速晶体振荡器
/*等待LSE准备好 */
//等待外部低速时钟准备好
while(RCC_GetFlagStatus(RCC_FLAG_LSERDY)== RESET); //等待起振
/*选择LSE作为时钟源*/
//设置LSE为RTC时钟
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //设置LSE为RTC时钟源
/*使RTC Clock有效 */
//使能RT时钟
RCC_RTCCLKCmd(ENABLE);
/*等待RTC寄存器同步*/
/*等待RTC寄存器(RTC_CNT,RTC_ALR and RTC_PRL)与RTC同步*/
RTC_WaitForSynchro();
//等待最近一次对RTC的写操作完成
RTC_WaitForLastTask(); //等待上一次对RTC的写操作完成
//使能RTC的秒中断
RTC_ITConfig(RTC_IT_SEC,ENABLE);
RTC_ITConfig(RTC_IT_ALR,ENABLE);
//等待最近一次对RTC的写操作完成
RTC_WaitForLastTask();
//设置RTC预分频的值 (32.768khz/(32767+1))=1hz
RTC_SetPrescaler(32767);
//等待最近一次对RTC的写操作完成
RTC_WaitForLastTask();
}
2.4万年历数据的处理和显示函数
因为输入数据并非ASCII码,而12864显示要求为ASCII码,所以需要进行转换。
2.4.1函数原型:wanchuliday();
功能: 万年历的数据处理函数,实现数据的同步。
void wanchuli(uint32_t year,uint32_tmoon,uint32_t day,uint32_t hour,uint32_t min,uint32_t sec)
{
year = year%100;
wandisplay[0]=year/10; //年显示数据处理
wandisplay[1]=year%10;
wandisplay[2]=moon/10; //月显示数据处理
wandisplay[3]=moon%10;
wandisplay[4]=day/10; //天显示数据处理
wandisplay[5]=day%10;
wandisplay[6]=hour/10;//时显示数据处理
wandisplay[7]=hour%10;
wandisplay[8]=min/10;//分显示数据处理
wandisplay[9]=min%10;
wandisplay[10]=sec/10;//秒显示数据处理/
wandisplay[11]=sec%10;
}
2.4.2 函数原型:wanxians();
功能: 万年历的数据显示函数,支持12864对数据显示的支持。
void wanxians(void)
{
send_command(0x91);
send_data(yejingtable[wandisplay[0]]);//年(高位)
send_data(yejingtable[wandisplay[1]]);//年(低位)
send_command(0x94);
send_data(yejingtable[wandisplay[2]]);//月
send_data(yejingtable[wandisplay[3]]);//月
send_command(0x96);
send_data(yejingtable[wandisplay[4]]);//日
send_data(yejingtable[wandisplay[5]]);//日
send_command(0x8b);
send_data(yejingtable[wandisplay[6]]);
send_data(yejingtable[wandisplay[7]]);
send_command(0x8d);
send_data(yejingtable[wandisplay[8]]);
send_data(yejingtable[wandisplay[9]]);
send_command(0x8f);
send_data(yejingtable[wandisplay[10]]);
send_data(yejingtable[wandisplay[11]]);
}
2.4.3 在LCD12864显示当前时间值
void Time_Show(struct rtc_time *tm)
{
u8 i;
u8 a=1;
/* 设置循环*/
while (a)
{
/* 每过1s*/
if (TimeDisplay == 1)
{
Time_Display(RTC_GetCounter(),tm);//时间实现农历转化,最终显示出来
TimeDisplay = 0;
}
i=keyscan();
if(i==0x84)
{
a=0;
FirstDisplay= 1;
}
}
}//函数结束
2.4.4显示当前时间值
被调用函数
void Time_Display(uint32_tTimeVar,struct rtc_time *tm)
{
uint32_t BJ_TimeVar;
uint8_t str[15]; // 字符串暂存
uint8_t str0[4];
/* 把标准时间转换为北京时间*/
BJ_TimeVar =TimeVar + 8*60*60;
to_tm(BJ_TimeVar, tm);/*把定时器的值转换为北京时间*/
if((!tm->tm_hour && !tm->tm_min&& !tm->tm_sec) ||(FirstDisplay))
{
GetChinaCalendar((u16)tm->tm_year,(u8)tm->tm_mon, (u8)tm->tm_mday, str);
GetChinaCalendarStr((u16)tm->tm_year,(u8)tm->tm_mon ,(u8)tm->tm_mday,str);
lcd12864_display(1,1,str);
if(GetJieQiStr((u16)tm->tm_year,(u8)tm->tm_mon, (u8)tm->tm_mday, str0))
lcd12864_display(3,1,str0);
FirstDisplay = 0;
}
wanchuli(tm->tm_year,tm->tm_mon,tm->tm_mday,tm->t m_hour,tm->tm_min,tm->tm_sec);
wanxians();
lcd12864_display(2,1,"20"); lcd12864_display(2,3,zodiac_sign[(tm->tm_year-3)%12]);//显示年的属相
lcd12864_display(2,4,"年");
lcd12864_display(2,6,"月");
lcd12864_display(2,8,"日");
lcd12864_display(3,5,":");
lcd12864_display(3,7,":");
lcd12864_display(4,1,"星期");
lcd12864_display(4,3,WEEK_STR[tm->tm_wday]);
lcd12864_display(4,5,"B:设定");
}
2.5配置行列式键盘串口引脚
2.5.1 GPIO_Config_key(void)
void GPIO_Config_key(void)
{
GPIO_InitTypeDef GPIO_InitStruct;//定义一个结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);//时钟使能
GPIO_InitStruct.GPIO_Pin=0x0F;//引脚选择,选择D0-D3设置为行线
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;//输出速率
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;//输出
GPIO_Init(GPIOC,&GPIO_InitStruct); //初始化
GPIO_InitStruct.GPIO_Pin=0xF0;//引脚选择,选择D4-D7设置为列线
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;//输入
GPIO_Init(GPIOC,&GPIO_InitStruct); //初始化
}
2.5.2 keyscan()
功能:键盘扫描函数,函数返回值即键值。
uint8_t keyscan(void)
{
uint8_tLIE,HANG,k,i=0;
GPIO_Write(GPIOC,0xF0);//D0-D3拉低,D4-D7拉高
if((GPIO_ReadInputData(GPIOC)&0xF0)!=0xF0)//有按键按下
{
delay(40,ms);//消抖
if((GPIO_ReadInputData(GPIOC)&0xF0)!=0xF0)//再次判断是否按下
{
LIE=GPIO_ReadInputData(GPIOC);//读取按键按下后得到的代码
HANG=LIE;//将代码复制给行
LIE=~LIE;//将键码取反,例如:按下某个键得到0111 0000,取反后得到1000 1111;
LIE=LIE&0XF0;//得到列1000 1111&1111 0000得到1000 0000,得到列数
for(i=0;i<4&&((HANG&0xF0)!=0xF0);i++)//逐次将行拉高,判断列数中原来变低的位是否变高
{
//读到之前检测到为低的列变高则退出
GPIO_Write(GPIOC, (HANG&0xF0)|(0x01<<i));
//进行行扫描,逐次将行口线拉高,列保持为按下时的状态
HANG=GPIO_ReadInputData(GPIOC);//读取IO口,用以判断是否扫描到行坐标
}
HANG&=0x0F;//将行值取出
k=LIE|HANG;//行列相加则得到键码
GPIO_Write(GPIOC,0xF0);//D0-D3拉低,D4-D7拉高,此处用来将行列状态初始化为未按下时的状态
while((GPIO_ReadInputData(GPIOC)&0xF0)!=0xF0)//判释放
{
delay(40,ms);//后沿消抖,时间需长一点,小按键消抖时间可以短一点,大按键抖动严重消抖需长一点
}
return k; //返回键码
}
}
return(0); //无键按下,返回0
}
2.6 delay延时模块化程序设计
2.6.1 delay(uint32_t time,uint8_t tm)
void delay(uint32_t time,uint8_t tm)
{
uint32_tfac_us,temp;
uint32_tfac_ms;
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);//SysTick 时钟源为 AHB 时钟除以 8
fac_us=SystemCoreClock/8000000;
//延时 1us 计数基值 72M/8M=9 9/9M=1us
fac_ms= (uint16_t)fac_us * 1000;
//延时 1ms 计数基值
if(tm== us)
{
SysTick->LOAD= time * fac_us;
//装载重装寄存器
SysTick->VAL = 0x00;
//清空计数器
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk;
//使能开始倒计时
}
if(tm==ms)
{
SysTick->LOAD =(uint32_t)time*fac_ms;
//装载重装寄存器
SysTick->VAL = 0X00;
//清空计数器
SysTick->CTRL|= SysTick_CTRL_ENABLE_Msk;
//使能开始倒计时
}
do
{
temp = SysTick->CTRL;
}
while(temp&0x01&&!(temp&(1<<16))); //等待时间到达
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;
//关闭定时器
SysTick->VAL=0X00; //清空计数器
}
2.7 日历详细设计
代码有点多,就举个例子然后主要说下这个函数的具体内容
u8 GetJieQiStr(u16 year,u8 month,u8day,u8 *str)
{
u8JQdate,JQ,MaxDay;
u8KK[1]={' '};
/***********定义一个为空格字符(' ')的数组************/
if(GetJieQi(year,month,day,&JQdate)==0) return 0;
JQ= (month-1) *2 ; //获得节气顺序标号(0~23
if(day>= 15) JQ++; //判断是否是上半月
if(day==JQdate) //今天正是一个节气日
{
StrCopy(str,(u8*)JieQiStr[JQ],5);
return1;
}
//今天不是一个节气日
StrCopy(str,(u8*) KK[0],4);
/************将空格符传送四个进 str 数组,实现在公历日期没有对应农历节气的时候不显示字符************/
if(day<JQdate) //如果今天日期小于本月的节气日期
{
//StrCopy(&str[2],(u8*)JieQiStr[JQ],4);
day=JQdate-day;
}
else //如果今天日期大于本月的节气日期
{
if((JQ+1) >23) return 0;
//StrCopy(&str[2],(u8*)JieQiStr[JQ+1],4);
if(day< 15)
{
GetJieQi(year,month,15,&JQdate);
day=JQdate-day;
}
else //翻月
{
MaxDay=MonthDayMax[month-1];
if(month==2) //润月问题
{
if((year%4==0)&&((year%100!=0)||(year%400==0)))MaxDay++;
}
if(++month==13) month=1;
GetJieQi(year,month,1,&JQdate);
day=MaxDay-day+JQdate;
}
}
str[10]=day/10+'0';
str[11]=day%10+'0';
return1;
}
通过设置年份对应的键码,使按键设置成立。
const uint8_t year_code[597]=
{
0x04,0xAe,0x53,//1901 0
0x0A,0x57,0x48,//1902 3
0x55,0x26,0xBd,//1903 6
。。。。。。
}//举一部分例子,后边的设置一样,只是按键不同,是通过键盘从左到右的顺序进行设置的。
三项目截图及连线:
这里连线就不做表了,直接按这图连或者在代码里的说明进行连线,非常简单,程序有个别小bug,不影响运行。
嵌入式 LED 万年历相关推荐
- 嵌入式LED驱动程序
<span style="font-size:18px;">#include <linux/config.h>//配置头文件 #include <li ...
- arm嵌入式led灯闪烁实验报告_led闪烁实验报告.doc
led闪烁实验报告 篇一:单片机实验--LED灯闪烁实验 实 验 报 告 课程名称 实验项目 2016年 3 月 13 日 实验目的 1. 掌握51单片机开发板的使用步骤: 2. 掌握51单片机开发板 ...
- arm嵌入式led灯闪烁实验报告_嵌入式led灯亮灭实验报告
实验名称 Led 灯的亮灭 姓名 XXX 学号 XXX 一. 实验目的 通过在 ARM7TDI 实验机上,使 D7~D8 两个灯如下进行亮灭:开始时 D7 灯亮, D8 灯灭:而后 D7 灯灭, D8 ...
- 基于arm的嵌入式QT开发(课程设计)
一. 项目要求 配置QT5.7基于x86及arm 等两种CPU架构的调试及开发环境: 移植arm编译后的QT5.7及屏幕校准工具tslib1.4至CORTEX ARM9实验平台: 开发基于QT5.7的 ...
- 万豪酒店品牌进驻江苏盐城,持续华东区拓展
万豪旅享家(Marriott Bonvoy™)旗下30个卓越酒店品牌之一的旗舰品牌万豪酒店宣布盐城万豪酒店正式开业,标志着万豪国际集团在中国苏北地区以及长三角经济圈的持续深入布局.作为盐城首家服务设施 ...
- 小米的逆转密码:MIX2与新零售
MIX2的发布,让小米又一次扮演引领者的角色,给业内"带节奏"."全屏手机"的概念,如火如荼,小米MIX2之外,如三星.VIVO.金立.荣耀.ZUK等,也在准备 ...
- 嵌入式linux驱动开发之点亮led(驱动编程思想之初体验)
这节我们就开始开始进行实战啦!这里顺便说一下啊,出来做开发的基础很重要啊,基础不好,迟早是要恶补的.个人深刻觉得像这种嵌入式的开发对C语言和微机接口与原理是非常依赖的,必须要有深厚的基础才能hold的 ...
- 嵌入式Linux LED,键盘,AD驱动程序开发
LED,键盘,AD驱动程序开发 原文: http://blog.sina.com.cn/s/blog_4083b2d70100bnlf.html 一:硬件平台及系统平台 C ...
- linux cached释放_正点原子Linux第四十一章嵌入式Linux LED驱动开发实验
1)资料下载:点击资料即可下载 2)对正点原子Linux感兴趣的同学可以加群讨论:935446741 3)关注正点原子公众号,获取最新资料更新 第四十一章嵌入式Linux LED驱动开发实验 上一章我 ...
最新文章
- python调用perl_在Perl、Shell和Python中传参与输出帮助文档
- oracle服务器不识别tc服务,记一次ORACLE无法启动登陆事故
- Oracle 12c应用连接VIP轮训负载均衡?
- 第二次想上传demo到github
- [数据库字典]简单的3个SQL视图搞定所有SqlServer数据库字典
- php源码 乱码 通达oa_php中文乱码问题的终极解决方案汇总
- Android学习之路-----倒计时欢迎界面(更有条理)
- Win11使用CAD卡顿或者致命错误怎么办?Win11无法正常使用CAD
- ai面试的优缺点_如果你的面试官是机器人?那么你该如何表现?
- GNOME 3 使用技巧
- 安装 OpenCC 简繁体中文转换
- 树莓派:GPIO/引脚/Pin 介绍
- 编译 /home/nzm/dvsdk_3_00_02_44/codec_engine_2_24/examples/ti/sdo/ce/examples/apps/video_copy 【part2】
- WEBRTC需要,配置自己的 TURN/STUN 服务
- 【FTP】——文件传输协议
- 3D视觉检测风挡玻璃智能涂胶工作站
- [Java]使用jConsole导出java程序的heap dump文件
- Flash 全屏代码
- mac顶部右边的区域叫什么
- 由于找不到opencv_world430d.dll,无法继续执行代码的解决办法
热门文章
- 高端简洁响应式电子商务网站源码
- 面向对象设计与开发原则
- 如何学习调用股票量化交易API接口的方法?
- 安全透明方法“WebMatrix.WebData.PreApplicationStartCode.Start()”
- 水准助手V2.0软件使用说明
- 德国公民信用相关数据_作为公民数据科学家,没有任何事
- 基于多目标粒子群优化算法的冷热电联供型综合能源系统运行优化(Matlab代码实现)
- 二、IDEA设置、快捷键和代码模板
- 惠普m180n故障码04_HP打印机错误代码及解决方法
- serialize()和serializeArray()方法