本文实现RTC实时时钟,以点阵屏形式显示。
关于RTC实时时钟,SDK中有完整的例子,可以不用修改就直接使用:
LibSamples_MM32F0120_V1.13.4\Samples\LibSamples\RTC\RTC_Calendar
在此基础上加上显示部分就可以完成本实验。
1、RTC配置
RTC_Configure完成:
RTC模块的初始化配置(使用LSE作为时钟源)
通过备份寄存器判断是否需要重新配置RTC
配置RTC秒中断
设置中断优先级和使能
加载默认RTC值
void RTC_Configure(void)
{
NVIC_InitTypeDef NVIC_InitStruct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_RTC | RCC_APB1Periph_BKP, ENABLE);
PWR_BackupAccessCmd(ENABLE);
BKP_DeInit();
if (BKP_ReadBackupRegister(BKP_DR1) != 0x5B5B)
{
RCC_LSEConfig(RCC_LSE_ON);
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)
{
}
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
RCC_RTCCLKCmd(ENABLE);
RTC_WaitForSynchro();
RTC_WaitForLastTask();
RTC_ITConfig(RTC_IT_SEC, ENABLE);
RTC_WaitForLastTask();
RTC_SetPrescaler(32767);
RTC_WaitForLastTask();
printf("\r\n%s", __FUNCTION__);
BKP_WriteBackupRegister(BKP_DR1, 0x5B5B);
}
else
{
printf("\r\nNeed't to configure RTC.");
RTC_WaitForSynchro();
RTC_ITConfig(RTC_IT_SEC, ENABLE);
RTC_WaitForLastTask();
}
NVIC_InitStruct.NVIC_IRQChannel = RTC_BKP_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
RTC_LoadDefault();
}
2、RTC_BKP_IRQHandler中断处理
RTC(实时时钟)和 BKP(备份寄存器)共用的中断源
实际上包含多个可能的中断事件,主要分:
秒中断(RTC_IT_SEC)
闹钟中断(RTC_IT_ALR)
溢出中断(RTC_IT_OW)
本例中,用于更新时间显示RTC_UpdateCalendar
打印时间RTC_PrintDateTime
void RTC_BKP_IRQHandler(void)
{
if (RESET != RTC_GetITStatus(RTC_IT_SEC))
{
RTC_UpdateCalendar();
RTC_PrintDateTime();
RTC_ClearITPendingBit(RTC_IT_SEC);
RTC_WaitForLastTask();
}
}
3、RTC_UpdateCalendar()更新时间
void RTC_UpdateCalendar(void)
{
static uint32_t PreTotalDay = 0;
uint32_t TotalSecond = 0;
uint32_t TotalDay = 0;
uint16_t Year = 1970;
uint8_t Month = 0;
TotalSecond = RTC_GetCounter();
TotalDay = TotalSecond / 86400;
if (PreTotalDay != TotalDay)
{
PreTotalDay = TotalDay;
while (TotalDay >= 365)
{
if (RTC_LeapYear(Year) == 1)
{
if (TotalDay >= 366)
{
TotalDay -= 366;
}
else
{
break;
}
}
else
{
TotalDay -= 365;
}
Year++;
}
RTC_Calendar.year = Year;
while (TotalDay >= 28)
{
if ((Month == 1) && (RTC_LeapYear(RTC_Calendar.year) == 1))
{
if (TotalDay >= 29)
{
TotalDay -= 29;
}
else
{
break;
}
}
else
{
if (TotalDay >= RTC_DayOfMonth[Month])
{
TotalDay -= RTC_DayOfMonth[Month];
}
else
{
break;
}
}
Month++;
}
RTC_Calendar.month = Month + 1;
RTC_Calendar.day = TotalDay + 1;
RTC_Calendar.week = RTC_GetWeek(RTC_Calendar.year, RTC_Calendar.month, RTC_Calendar.day);
}
RTC_Calendar.hour = (TotalSecond % 86400) / 3600;
RTC_Calendar.minute = ((TotalSecond % 86400) % 3600) / 60;
RTC_Calendar.second = ((TotalSecond % 86400) % 3600) % 60;
}
4、RTC_PrintDateTime打印时间
void RTC_PrintDateTime(void)
{
printf("\r\n%04d-%02d-%02d", RTC_Calendar.year, RTC_Calendar.month, RTC_Calendar.day);
switch (RTC_Calendar.week)
{
case 0:
printf(" SUN ");
break;
case 1:
printf(" MON ");
break;
case 2:
printf(" TUE ");
break;
case 3:
printf(" WED ");
break;
case 4:
printf(" THU ");
break;
case 5:
printf(" FRI ");
break;
case 6:
printf(" SAT ");
break;
default:
break;
}
printf("%02d:%02d:%02d\r\n", RTC_Calendar.hour, RTC_Calendar.minute, RTC_Calendar.second);
}
5、增加自己的时间显示
因为用到74hc595驱动点阵屏,74hc595没有输出锁定功能,需要将相关的点阵屏驱动放在while(1)中不断刷新。
void RTC_Calendar_Sample(void)
{
printf("\r\nTest %s", __FUNCTION__);
//初始化LED点阵屏
config_led_matrix_gpio();
//配置RTC
RTC_Configure();
while (1)
{
//PLATFORM_LED_Toggle(LED1);
//PLATFORM_DelayMS(100);
//在LED点阵屏上显示
clear_screen();
vs_clear_screen();
//填充小时
fill_num_to_buffer(0,0,RTC_Calendar.hour/10);
fill_num_to_buffer(4,0,RTC_Calendar.hour%10);
//填充:
fill_num_to_buffer(8,0,10);
//填充小时
fill_num_to_buffer(12,0,RTC_Calendar.minute/10);
fill_num_to_buffer(16,0,RTC_Calendar.minute%10);
//填充:
fill_num_to_buffer(20,0,10);
//填充秒钟
fill_num_to_buffer(24,0,RTC_Calendar.second/10);
fill_num_to_buffer(28,0,RTC_Calendar.second%10);
vs_render_screen();
render_screen_to_matrix8x24();
}
}
6、点阵屏的驱动
#include "hc595.h"
#include "hal_conf.h"
//位置,595扫描的行/列
//COM
const uint8_t pos[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
//显示缓存 7*11*4=7*44,使用8*48缓存
//SEG,实际屏幕,节约空间
uint8_t matrix3[48]={0x00};
// 虚拟屏幕
uint8_t virtual_screen[SCREEN_WIDTH][SCREEN_HEIGHT]={0};
unsigned char bigNUM[]=
{
/* 3*7数字字模,上下顶到头,空左边一列*/
0x07,0x05,0x05,0x05,0x05,0x05,0x07, /*0*/
0x02,0x02,0x02,0x02,0x02,0x02,0x02, /*1*/
0x07,0x01,0x01,0x07,0x04,0x04,0x07, /*2*/
0x07,0x01,0x01,0x07,0x01,0x01,0x07, /*3*/
0x05,0x05,0x05,0x07,0x01,0x01,0x01, /*4*/
0x07,0x04,0x04,0x07,0x01,0x01,0x07, /*5*/
0x07,0x04,0x04,0x07,0x05,0x05,0x07, /*6*/
0x07,0x01,0x01,0x01,0x01,0x01,0x01, /*7*/
0x07,0x05,0x05,0x07,0x05,0x05,0x07, /*8*/
0x07,0x05,0x05,0x07,0x01,0x01,0x07, /*9*/
0x00,0x02,0x00,0x00,0x00,0x02,0x00, /*:*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*空,补位*/
};
unsigned char smallNUM[]=
{
/* 4*7数字字模,上下各空一行,空左边一列*/
0x00,0x07,0x05,0x05,0x05,0x07,0x00, /*0*/
0x00,0x02,0x02,0x02,0x02,0x02,0x00, /*1*/
0x00,0x07,0x01,0x07,0x04,0x07,0x00, /*2*/
0x00,0x07,0x01,0x07,0x01,0x07,0x00, /*3*/
0x00,0x05,0x05,0x07,0x01,0x01,0x00, /*4*/
0x00,0x07,0x04,0x07,0x01,0x07,0x00, /*5*/
0x00,0x07,0x04,0x07,0x05,0x07,0x00, /*6*/
0x00,0x07,0x01,0x01,0x01,0x01,0x00, /*7*/
0x00,0x07,0x05,0x07,0x05,0x07,0x00, /*8*/
0x00,0x07,0x05,0x07,0x01,0x07,0x00, /*9*/
0x00,0x00,0x02,0x00,0x02,0x00,0x00, /*:*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*空,补位*/
};
void config_led_matrix_gpio(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOD, ENABLE);
GPIO_StructInit(&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_11 | GPIO_Pin_15;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_StructInit(&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOD, &GPIO_InitStruct);
LED_OE_CLR;
}
/*
* PN:反显
* */
void HC595(uint8_t c,uint8_t PN)
{
uint8_t i;
for(i=0;i<8;i++){
if(PN==0){ //正常显示
if((c>>i) & 0x01) LED_SER_SET;
else LED_SER_CLR;
}
else{ //反显
if((c>>i) & 0x01) LED_SER_CLR;
else LED_SER_SET;
}
LED_SRCK_CLR;
LED_SRCK_SET; // 上升沿进行一次数据移入
}
}
void clear_screen(void)
{
uint8_t i;
for ( i = 0; i < 48; i++) {
matrix3[i]=0x00;
}
}
//紧凑缓存画点
void draw_pixel(uint8_t x,uint8_t y)
{
uint8_t mx,my;
if (x>21) x=x+2;
mx=5-x/8;
my=y+1;
matrix3[mx*8+my]|=1<<(7-x%8);
}
//控制闪烁的措施,如果点灯少就多循环显示,否则少循环显示
uint8_t count_ones(uint8_t i) {
uint8_t count = 0;
while (i != 0) {
i &= (i - 1); // 清除最低位的1
count++;
}
return count;
}
//将紧凑缓存内容显示在LED矩阵上,没有虚拟屏直接显示的函数
//pBuff通常是matrix3,
void render_screen_to_matrix8x24(void)
{
uint8_t *pBuff=matrix3;
uint8_t k;
uint16_t m,n;
for(m=0;m<32*2;m++) //为移动预留
{
for(k=0;k<7;k++) //行扫描
{
for(n=0;n<(count_ones(pBuff[k])+count_ones(pBuff[k+8])+count_ones(pBuff[k+16])+count_ones(pBuff[k+24])
+count_ones(pBuff[k+32])+count_ones(pBuff[k+40]))*2;n++)//控制显示速度,防止闪烁
{
HC595(~pos[k],0);
HC595(pBuff[k],0);
HC595(pBuff[k+8],0);
HC595(pBuff[k+16],0);
HC595(~pos[k],0);
HC595(pBuff[k+24],0);
HC595(pBuff[k+32],0);
HC595(pBuff[k+40],0);
LED_RCK_CLR;
LED_RCK_SET; //并行输出
__nop();__nop();
}
}
}
//传COM -
//传SEG +
}
//虚拟屏清屏
void vs_clear_screen(void)
{
uint8_t x,y;
for ( y = 0; y < SCREEN_HEIGHT; y++) {
for ( x = 0; x < SCREEN_WIDTH; x++) {
virtual_screen[x][y] = 0;
}
}
}
//虚拟屏画点
void vs_drawpixel(uint8_t x,uint8_t y)
{
virtual_screen[x][y]=1;
}
void vs_clearpixel(uint8_t x,uint8_t y)
{
virtual_screen[x][y]=0;
}
//虚拟屏复制到紧凑缓存
void vs_render_screen(void)
{
uint8_t x,y;
//clear_screen();
for ( y = 0; y < SCREEN_HEIGHT; y++) {
for ( x = 0; x < SCREEN_WIDTH; x++) {
if (virtual_screen[x][y]) {
draw_pixel(x, y); // 画点
} else {
// 如果需要清除点,可以调用清屏函数或设置背景色
}
}
}
}
/*
* 填充数字到屏幕位置,写物理和虚拟缓存
* (x,y)是显示位置坐标;NUM是要显示的数字
*/
void fill_num_to_buffer(uint8_t x,uint8_t y,uint8_t num)
{
uint8_t i,j;
for(i=0;i<7;i++)
{
for(j=0;j<4;j++)
{
if(((smallNUM[num*7+i]>>j)&0x01)==0x01)
{
//draw_pixel(x+4-j, y+i);
vs_drawpixel(x+4-j, y+i);
}
else
{
vs_clearpixel(x+4-j, y+i);
}
}
}
}
void hc595_test(void)
{
config_led_matrix_gpio();
while(1)
{
vs_clear_screen();
fill_num_to_buffer(0,0,0);
fill_num_to_buffer(4,0,1);
fill_num_to_buffer(8,0,2);
fill_num_to_buffer(12,0,3);
fill_num_to_buffer(16,0,4);
fill_num_to_buffer(20,0,5);
fill_num_to_buffer(24,0,6);
fill_num_to_buffer(28,0,7);
fill_num_to_buffer(32,0,8);
fill_num_to_buffer(36,0,9);
vs_render_screen();
render_screen_to_matrix8x24();
}
}
6、效果
|
 共1人点赞
|