打印
[其他]

【HC32L196PCTA测评】6.LCD+WDT+RTC+FLASH测试

[复制链接]
1032|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 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();
        }  
    }
}

运行效果


使用特权

评论回复
沙发
yang377156216| | 2023-8-8 18:19 | 只看该作者
段码LCD 的型号应该是:大连佳显电子的 GDC04212 吧。

使用特权

评论回复
板凳
yuyy1989|  楼主 | 2023-8-8 18:25 | 只看该作者
yang377156216 发表于 2023-8-8 18:19
段码LCD 的型号应该是:大连佳显电子的 GDC04212 吧。

对,原理图上有标

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

149

主题

708

帖子

7

粉丝