本帖最后由 yuyy1989 于 2023-8-10 11:17 编辑
#申请原创# @21小跑堂
6.LCD+WDT+RTC+FLASH测试
6.1LCD测试
HC32L196集成了LCD液晶控制模块,在这块开发板上也有一块4位段码LCD屏,接下来使用这个段码屏结合定时器做个秒表
LCD的相关IO在stkhc32l19x.h中已经被定义好了
初始化IO
void lcd_io_init()
{
Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio,TRUE);
Gpio_SetAnalogMode(STK_LCD_COM0_PORT, STK_LCD_COM0_PIN); //COM0
Gpio_SetAnalogMode(STK_LCD_COM1_PORT, STK_LCD_COM1_PIN); //COM1
Gpio_SetAnalogMode(STK_LCD_COM2_PORT, STK_LCD_COM2_PIN); //COM2
Gpio_SetAnalogMode(STK_LCD_COM3_PORT, STK_LCD_COM3_PIN); //COM3
Gpio_SetAnalogMode(STK_LCD_SEG0_PORT, STK_LCD_SEG0_PIN); //SEG0
Gpio_SetAnalogMode(STK_LCD_SEG1_PORT, STK_LCD_SEG1_PIN); //SEG1
Gpio_SetAnalogMode(STK_LCD_SEG2_PORT, STK_LCD_SEG2_PIN); //SEG2
Gpio_SetAnalogMode(STK_LCD_SEG3_PORT, STK_LCD_SEG3_PIN); //SEG3
Gpio_SetAnalogMode(STK_LCD_SEG4_PORT, STK_LCD_SEG4_PIN); //SEG4
Gpio_SetAnalogMode(STK_LCD_SEG5_PORT, STK_LCD_SEG5_PIN); //SEG5
Gpio_SetAnalogMode(STK_LCD_SEG6_PORT, STK_LCD_SEG6_PIN); //SEG6
Gpio_SetAnalogMode(STK_LCD_SEG7_PORT, STK_LCD_SEG7_PIN); //SEG7
Gpio_SetAnalogMode(GpioPortB, GpioPin3); //VLCDH
Gpio_SetAnalogMode(GpioPortB, GpioPin4); //VLCD3
Gpio_SetAnalogMode(GpioPortB, GpioPin5); //VLCD2
Gpio_SetAnalogMode(GpioPortB, GpioPin6); //VLCD1
}
LCD模块使用低速时钟,因此要开启内部低速时钟或外部低速晶振
初始化LCD模块
#define LCD_MODE 0
void lcd_init()
{
stc_lcd_cfg_t LcdInitStruct;
stc_lcd_segcom_t LcdSegCom;
Sysctrl_ClkSourceEnable(SysctrlClkXTL,TRUE);
Sysctrl_SetPeripheralGate(SysctrlPeripheralLcd,TRUE); ///< 开启LCD时钟
LcdSegCom.u32Seg0_31 = 0xffffff00; ///< 配置LCD_POEN0寄存器 开启SEG0~SEG7
LcdSegCom.stc_seg32_51_com0_8_t.seg32_51_com0_8 = 0xffffffff; ///< 初始化LCD_POEN1寄存器 全部关闭输出端口
LcdSegCom.stc_seg32_51_com0_8_t.segcom_bit.Com0_3 = 0; ///< 使能COM0~COM3
LcdSegCom.stc_seg32_51_com0_8_t.segcom_bit.Mux = 0; ///< Mux=0,Seg32_35=0,BSEL=1表示:选择外部电容工作模式,内部电阻断路
LcdSegCom.stc_seg32_51_com0_8_t.segcom_bit.Seg32_35 = 0;
Lcd_SetSegCom(&LcdSegCom); ///< LCD COMSEG端口配置
LcdInitStruct.LcdBiasSrc = LcdExtCap; ///< 电容分压模式,需要外部电路配合
LcdInitStruct.LcdDuty = LcdDuty4; ///< 1/4duty
LcdInitStruct.LcdBias = LcdBias3; ///< 1/3 BIAS
LcdInitStruct.LcdCpClk = LcdClk2k; ///< 电压泵时钟频率选择2kHz
LcdInitStruct.LcdScanClk = LcdClk128hz; ///< LCD扫描频率选择128Hz
LcdInitStruct.LcdMode = LCD_MODE==0?LcdMode0:LcdMode1;
LcdInitStruct.LcdClkSrc = LcdXTL; ///< LCD时钟选择XTL
LcdInitStruct.LcdEn = LcdEnable; ///< 使能LCD模块
Lcd_Init(&LcdInitStruct);
}
来看看如何显示指定的数字,先找到这块屏的资料
在看看手册里是怎么控制的
模式0
模式1
显示代码
#if(LCD_MODE == 0)
uint32_t lcdrambuffer[2] = {0};
#else
uint8_t lcdrambuffer[4] = {0};
#endif
void lcdshow09num(uint8_t num,uint8_t pos)
{
if(pos > 3)
return;
#if(LCD_MODE == 0)
uint32_t temp;
switch(num)
{
case 0:
temp = 0x0F05;
break;
case 1:
temp = 0x0600;
break;
case 2:
temp = 0x0B06;
break;
case 3:
temp = 0x0F02;
break;
case 4:
temp = 0x0603;
break;
case 5:
temp = 0x0D03;
break;
case 6:
temp = 0x0D07;
break;
case 7:
temp = 0x0700;
break;
case 8:
temp = 0x0F07;
break;
case 9:
temp = 0x0F03;
break;
default:
break;
}
switch(pos)
{
case 0:
lcdrambuffer[0] &= 0xFFFF0008;
lcdrambuffer[0] |= temp;
Lcd_WriteRam(0,lcdrambuffer[0]);
break;
case 1:
lcdrambuffer[0] &= 0x0008FFFF;
lcdrambuffer[0] |= ((temp<<16)&0xFFFF0000);
Lcd_WriteRam(0,lcdrambuffer[0]);
break;
case 2:
lcdrambuffer[1] &= 0xFFFF0008;
lcdrambuffer[1] |= temp;
Lcd_WriteRam(1,lcdrambuffer[1]);
break;
case 3:
lcdrambuffer[1] &= 0x0008FFFF;
lcdrambuffer[1] |= ((temp<<16)&0xFFFF0000);
Lcd_WriteRam(1,lcdrambuffer[1]);
break;
default:
break;
}
#else
uint8_t temp[4] = {0};
switch(num)
{
case 0:
temp[0] = 0x03;
temp[1] = 0x02;
temp[2] = 0x03;
temp[3] = 0x02;
break;
case 1:
temp[0] = 0x00;
temp[1] = 0x02;
temp[2] = 0x02;
temp[3] = 0x00;
break;
case 2:
temp[0] = 0x02;
temp[1] = 0x03;
temp[2] = 0x01;
temp[3] = 0x02;
break;
case 3:
temp[0] = 0x02;
temp[1] = 0x03;
temp[2] = 0x02;
temp[3] = 0x02;
break;
case 4:
temp[0] = 0x01;
temp[1] = 0x03;
temp[2] = 0x02;
temp[3] = 0x00;
break;
case 5:
temp[0] = 0x03;
temp[1] = 0x01;
temp[2] = 0x02;
temp[3] = 0x02;
break;
case 6:
temp[0] = 0x03;
temp[1] = 0x01;
temp[2] = 0x03;
temp[3] = 0x02;
break;
case 7:
temp[0] = 0x02;
temp[1] = 0x02;
temp[2] = 0x02;
temp[3] = 0x00;
break;
case 8:
temp[0] = 0x03;
temp[1] = 0x03;
temp[2] = 0x03;
temp[3] = 0x02;
break;
case 9:
temp[0] = 0x03;
temp[1] = 0x03;
temp[2] = 0x02;
temp[3] = 0x02;
break;
default:
break;
}
lcdrambuffer[0] &= ~(3<<(pos*2));
lcdrambuffer[0] |= (temp[0]<<(pos*2));
lcdrambuffer[1] &= ~(3<<(pos*2));
lcdrambuffer[1] |= (temp[1]<<(pos*2));
lcdrambuffer[2] &= ~(3<<(pos*2));
lcdrambuffer[2] |= (temp[2]<<(pos*2));
lcdrambuffer[3] &= ~(2<<(pos*2));
lcdrambuffer[3] |= (temp[3]<<(pos*2));
Lcd_WriteRam(0,lcdrambuffer[0]);
Lcd_WriteRam(1,lcdrambuffer[1]);
Lcd_WriteRam(2,lcdrambuffer[2]);
Lcd_WriteRam(3,lcdrambuffer[3]);
#endif
}
void lcdshowdot(uint8_t dotpos,uint8_t show)
{
if(dotpos > 3)
return;
#if(LCD_MODE == 0)
switch(dotpos)
{
case 0:
if(show > 0)
lcdrambuffer[0] |= 0x00000008;
else
lcdrambuffer[0] &= 0xFFFFFFF7;
Lcd_WriteRam(0,lcdrambuffer[0]);
break;
case 1:
if(show > 0)
lcdrambuffer[0] |= 0x00080000;
else
lcdrambuffer[0] &= 0xFFF7FFFF;
Lcd_WriteRam(0,lcdrambuffer[0]);
break;
case 2:
if(show > 0)
lcdrambuffer[1] |= 0x00000008;
else
lcdrambuffer[1] &= 0xFFFFFFF7;
Lcd_WriteRam(1,lcdrambuffer[1]);
break;
case 3:
if(show > 0)
lcdrambuffer[1] |= 0x00080000;
else
lcdrambuffer[1] &= 0xFFF7FFFF;
Lcd_WriteRam(1,lcdrambuffer[1]);
break;
default:
break;
}
#else
if(show > 0)
lcdrambuffer[3] |= (1<<(dotpos*2));
else
lcdrambuffer[3] &= ~(1<<(dotpos*2));
Lcd_WriteRam(3,lcdrambuffer[3]);
#endif
}
timer0修改为10ms,中断处理
uint16_t timecount = 0;
uint8_t showseconddot = 1;
uint8_t timerrun = 0;
void Tim0_IRQHandler(void)
{
//Timer0 模式0 溢出中断
if(TRUE == Bt_GetIntFlag(TIM0, BtUevIrq))
{
Bt_ClearIntFlag(TIM0,BtUevIrq); //中断标志清零
if(timerrun == 1)
{
if(timecount < 9999)
timecount += 1;
else
{
timecount = 0;
//Gpio_WriteOutputIO(STK_LED_PORT, STK_LED_PIN,!Gpio_ReadOutputIO(STK_LED_PORT, STK_LED_PIN));
}
lcdshow09num(timecount%10,3);
lcdshow09num((timecount%100)/10,2);
lcdshow09num((timecount%1000)/100,1);
lcdshow09num(timecount/1000,0);
if(timecount%50 == 0)
{
showseconddot = 1-showseconddot;
lcdshowdot(1,showseconddot);
}
}
}
}
按键处理
void PortA_IRQHandler(void)
{
if(TRUE == Gpio_GetIrqStatus(STK_USER_PORT, STK_USER_PIN))
{
if(Gpio_GetInputIO(STK_USER_PORT, STK_USER_PIN) == FALSE)
{
if(timerrun == 0)
{
timecount = 0;
showseconddot = 1;
lcdshowdot(1,1);
timerrun = 1;
}
else if(timerrun == 1)
{
timerrun = 2;
}
else
{
timerrun = 0;
lcdshow09num(0,3);
lcdshow09num(0,2);
lcdshow09num(0,1);
lcdshow09num(0,0);
lcdshowdot(1,0);
}
}
Gpio_ClearIrq(STK_USER_PORT, STK_USER_PIN);
}
}
运行效果
6.2WDT看门狗测试
WDT(Watch Dog Timer)看门狗定时器可用来检测和解决由软件错误引起的故障。当 WDT计数器达到设定的溢出时间后,会触发中断或产生系统复位,WDT由专用的10KHz片内振荡器驱动。
开启看门狗并配置好时间,看门狗复位模式
void wdt_init()
{
Sysctrl_SetPeripheralGate(SysctrlPeripheralWdt,TRUE);
Wdt_Init(WdtResetEn, WdtT1s64);
Wdt_Start();
}
LCD自动计数,在主循环中定时喂狗,按键按下阻塞住,超时后系统会复位,LCD计数从0开始
int32_t main(void)
{
xth_init();
//时钟分频设置
Sysctrl_SetHCLKDiv(SysctrlHclkDiv1);
Sysctrl_SetPCLKDiv(SysctrlPclkDiv1);
led_init();
key_init();
timer0_init();
lcd_io_init();
lcd_init();
Lcd_ClearDisp(); ///< 清屏
lcdshowdot(2,1);
timerrun = 0;
lcdshow09num(0,3);
lcdshow09num(0,2);
lcdshow09num(0,1);
lcdshow09num(0,0);
lcdshowdot(1,0);
wdt_init();
while(1)
{
while(Gpio_GetInputIO(STK_USER_PORT, STK_USER_PIN) == FALSE);
Wdt_Feed();
yuyy_delay_ms(1000);
}
}
运行效果
再试试看门狗中断
void wdt_init()
{
Sysctrl_SetPeripheralGate(SysctrlPeripheralWdt,TRUE);
Wdt_Init(WdtIntEn, WdtT1s64);
EnableNvic(WDT_IRQn, IrqLevel3, TRUE);
Wdt_Start();
}
void Wdt_IRQHandler(void)
{
if(Wdt_GetIrqStatus())
{
Wdt_IrqClr(); ///<清除 wdt 中断标记
Gpio_WriteOutputIO(STK_LED_PORT, STK_LED_PIN,!Gpio_ReadOutputIO(STK_LED_PORT, STK_LED_PIN));
}
}
运行效果
6.3RTC测试
RTC(Real Time Clock)实时时钟提供秒、分、时、日、周、月、年的信息,每月的天数和闰年的天数可自动调整
接下来开启RTC,尝试用串口打印RTC的时间,开启RTC并初始化时间
void rtc_init()
{
stc_rtc_initstruct_t RtcInitStruct;
Sysctrl_SetPeripheralGate(SysctrlPeripheralRtc,TRUE);//RTC模块时钟打开
RtcInitStruct.rtcAmpm = RtcPm; //24小时制
RtcInitStruct.rtcClksrc = RtcClkXth1024; //外部32M晶振
RtcInitStruct.rtcPrdsel.rtcPrdsel = RtcPrdx; //周期中断类型PRDX
RtcInitStruct.rtcPrdsel.rtcPrdx = 1u; //周期中断时间间隔 1秒
RtcInitStruct.rtcTime.u8Second = 0x55; //配置RTC时间
RtcInitStruct.rtcTime.u8Minute = 0x01;
RtcInitStruct.rtcTime.u8Hour = 0x09;
RtcInitStruct.rtcTime.u8Day = 0x08;
RtcInitStruct.rtcTime.u8DayOfWeek = 0x02;
RtcInitStruct.rtcTime.u8Month = 0x08;
RtcInitStruct.rtcTime.u8Year = 0x23;
RtcInitStruct.rtcCompen = RtcCompenEnable; // 使能时钟误差补偿
RtcInitStruct.rtcCompValue = 0; //补偿值 根据实际情况进行补偿
Rtc_Init(&RtcInitStruct);
Rtc_AlmIeCmd(TRUE); //使能闹钟中断
EnableNvic(RTC_IRQn, IrqLevel3, TRUE); //使能RTC中断向量
Rtc_Cmd(TRUE); //使能RTC开始计数
}
RTC中断处理
void print_rtc()
{
stc_rtc_time_t readtime;
printf("RTC:%02X年%02X月%02X日 星期%02X %02X:%02X:%02X",readtime.u8Year,readtime.u8Month,readtime.u8Day,readtime.u8DayOfWeek,readtime.u8Hour,readtime.u8Minute,readtime.u8Second);
}
void Rtc_IRQHandler(void)
{
if(Rtc_GetPridItStatus() == TRUE)
{
Rtc_ClearPrdfItStatus(); //清除中断标志位
print_rtc();
}
}
运行效果
串口修改时间
void set_rtc()
{
if(uart_rxlen > 5)
{
stc_rtc_time_t settime;
settime.u8Year = uart_buffer[0];
settime.u8Month = uart_buffer[1];
settime.u8Day = uart_buffer[2];
settime.u8DayOfWeek = uart_buffer[3];
settime.u8Hour = uart_buffer[4];
settime.u8Minute = uart_buffer[5];
settime.u8Second = uart_buffer[6];
Rtc_SetTime(&settime);
}
uart_rxlen = 0;
uart_rxindex = 0;
}
void uart_rxtimeout()
{
set_rtc();
}
运行效果
用12864LCD做一个带日期显示的电子时钟
void time_chars_init()
{
Spi_SetCS(M0P_SPI0, FALSE);
yuyy_hs12864g18b_display_string_8x16(&hs12864_ctr,0,0,0,(uint8_t *)"HC32L196PCTA RTC");
yuyy_hs12864g18b_display_string_8x16(&hs12864_ctr,0,6,0,(uint8_t *)"Code by yuyy1989");
yuyy_hs12864g18b_display_graphic_16x16(&hs12864_ctr,0,2,16,(uint8_t *)time_chars[7]);
yuyy_hs12864g18b_display_graphic_16x16(&hs12864_ctr,0,2,48,(uint8_t *)time_chars[8]);
yuyy_hs12864g18b_display_graphic_16x16(&hs12864_ctr,0,2,80,(uint8_t *)time_chars[0]);
yuyy_hs12864g18b_display_graphic_16x16(&hs12864_ctr,0,2,98,(uint8_t *)time_chars[9]);
}
void print_rtc()
{
char out[20];
stc_rtc_time_t readtime;
Rtc_ReadDateTime(&readtime);
Spi_SetCS(M0P_SPI0, FALSE);
sprintf(out,"%02X",readtime.u8Year);
yuyy_hs12864g18b_display_string_8x16(&hs12864_ctr,0,2,0,(uint8_t *)out);
sprintf(out,"%02X",readtime.u8Month);
yuyy_hs12864g18b_display_string_8x16(&hs12864_ctr,0,2,32,(uint8_t *)out);
sprintf(out,"%02X",readtime.u8Day);
yuyy_hs12864g18b_display_string_8x16(&hs12864_ctr,0,2,64,(uint8_t *)out);
yuyy_hs12864g18b_display_graphic_16x16(&hs12864_ctr,0,2,114,(uint8_t *)time_chars[readtime.u8DayOfWeek]);
sprintf(out,"%02X:%02X:%02X",readtime.u8Hour,readtime.u8Minute,readtime.u8Second);
yuyy_hs12864g18b_display_string_8x16(&hs12864_ctr,0,4,32,(uint8_t *)out);
Spi_SetCS(M0P_SPI0, TRUE);
}
运行效果
6.4FLASH测试
HC32L196内部集成了256K的flash,每块 FLASH 按照Sector进行划分,每个Sector容量为512字节
做个简单的程序测试一下,按下按键时生成一个0-9999的随机数,用段码LCD显示并存储到FLASH,系统初始化时读出存储的数字并放到LCD上显示
#define FLASH_ADDR 0x1FE00
void flash_init()
{
while(Ok != Flash_Init(8, TRUE))
{
while(1);
}
}
void read_data_from_flash()
{
Flash_LockSet(FlashLock1, 0x80000000);
while(Ok != Flash_OpModeConfig(FlashReadMode));
testdata = *((unsigned short int *)FLASH_ADDR);
Flash_LockAll();
show_testdata();
}
void write_data_to_flash()
{
Flash_LockSet(FlashLock1, 0x80000000);
while(Ok != Flash_OpModeConfig(FlashSectorEraseMode));
if(Ok != Flash_SectorErase(FLASH_ADDR))
{
while(1);
}
///< FLASH 字节写、校验
while(Ok != Flash_OpModeConfig(FlashWriteMode));
if (Ok != Flash_Write16(FLASH_ADDR, &testdata, 1))
{
while(1);
}
Flash_LockAll();
}
按键按下生成随机数并保存void PortA_IRQHandler(void)
{
if(TRUE == Gpio_GetIrqStatus(STK_USER_PORT, STK_USER_PIN))
{
Gpio_ClearIrq(STK_USER_PORT, STK_USER_PIN);
if(Gpio_GetInputIO(STK_USER_PORT, STK_USER_PIN) == FALSE)
{
Gpio_WriteOutputIO(STK_LED_PORT, STK_LED_PIN,!Gpio_ReadOutputIO(STK_LED_PORT, STK_LED_PIN));
Trng_Generate();
testdata = Trng_GetData0()%10000;
show_testdata();
write_data_to_flash();
}
}
}
运行效果
|
此文章已获得独家原创/原创奖标签,著作权归21ic所有,未经允许禁止转载。
|