打印
[MM32软件]

【灵动微电子MM32F5330测评】6.SPI驱动LCD屏幕+RTC时钟测试

[复制链接]
661|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
SPI是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线(CS、SCK、MOSI、MISO),根据实际使用情况可以只用其中的2根或3根。
MISO:主设备输入、从设备输出引脚。传输方向为从设备发送到主设备。
MOSI:主设备输出、从设备输入引脚。传输方向为主设备发送到从设备。
SCK:串口时钟,由主设备产生并提供给从设备。
NSS:从设备选择。

开发板上集成了一颗W25Q80,这是一颗容量为 8M-bit的串行 Flash 存储器

例程里有读写代码这里就不重复实现了,换用SPI驱动LCD显示屏。
驱动这块屏幕需要用到5个IO:CS、RST、A0、SCK、MOSI

初始化SPI,只用发送模式就行,手动控制CS、RST、A0

void APP_SpiInit(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    SPI_InitTypeDef  SPI_InitStruct;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);

    SPI_StructInit(&SPI_InitStruct);
    SPI_InitStruct.SPI_Mode      = SPI_Mode_Master;
    SPI_InitStruct.SPI_DataSize  = SPI_DataSize_8b;
    SPI_InitStruct.SPI_DataWidth = 8;
    SPI_InitStruct.SPI_CPOL      = SPI_CPOL_High;
    SPI_InitStruct.SPI_CPHA      = SPI_CPHA_2Edge;
    SPI_InitStruct.SPI_NSS       = SPI_NSS_Soft;
    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
    SPI_InitStruct.SPI_FirstBit  = SPI_FirstBit_MSB;
    SPI_Init(SPI1, &SPI_InitStruct);

    SPI_BiDirectionalLineConfig(SPI1, SPI_Direction_Tx);

    GPIO_PinAFConfig(GPIOB, GPIO_PinSource3,  GPIO_AF_5);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource5,  GPIO_AF_5);

    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_3 | GPIO_Pin_5;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOB, &GPIO_InitStruct);

    SPI_Cmd(SPI1, ENABLE);
}

void APP_SpiWriteByte(SPI_TypeDef *spix,uint8_t dat)
{
    SPI_SendData(spix, dat);
    while (RESET == SPI_GetFlagStatus(spix, SPI_FLAG_TXEPT));
}

YUYY_HS12864G18B_DEV_Type lcd_dev;
YUYY_GPIO_DEV_Type gpio_cs;
YUYY_GPIO_DEV_Type gpio_rst;
YUYY_GPIO_DEV_Type gpio_a0;
void APP_LcdInit(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_4 | GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOB, &GPIO_InitStruct);
   
    gpio_cs.gpio = GPIOB;
    gpio_cs.pin = GPIO_Pin_6;
    gpio_a0.gpio = GPIOB;
    gpio_a0.pin = GPIO_Pin_4;
    gpio_rst.gpio = GPIOB;
    gpio_rst.pin = GPIO_Pin_7;
   
    lcd_dev.cs_pin = &gpio_cs;
    lcd_dev.a0_pin = &gpio_a0;
    lcd_dev.rst_pin = &gpio_rst;
    lcd_dev.spix = SPI1;
    lcd_dev.spi_sendbyte_func = (YUYY_HS12864G18B_SpiWriteByteFunc_Type)APP_SpiWriteByte;
    lcd_dev.gpio_setlev_func = (YUYY_HS12864G18B_GPIO_SetLevFunc_Type)YUYY_GPIO_SetLev;
    lcd_dev.delayus_func = (YUYY_HS12864G18B_DelayUsFunc_Type)YUYY_DelayUs;
   
    YUYY_HS12864G18B_Init(&lcd_dev);
    YUYY_HS12864G18B_ClearScreen(&lcd_dev);
    YUYY_HS12864G18B_DisplayString8x16(&lcd_dev,0,0,0," MM32F5330 TEST");
    YUYY_HS12864G18B_DisplayString8x16(&lcd_dev,0,2,0,"   LCD use SPI");
    YUYY_HS12864G18B_DisplayString8x16(&lcd_dev,0,4,0,"  bbs.21ic.com");
    YUYY_HS12864G18B_DisplayString8x16(&lcd_dev,0,6,0,"Code by yuyy1989");
    YUYY_HS12864G18B_DisplayFinish(&lcd_dev);
}
运行效果

实时时钟(RTC)是嵌入式系统中的一种重要组件,它提供了精确的时间跟踪功能,即使在系统关机时也能保持准确。RTC通常由一个专用的硬件模块组成,用于跟踪当前的年、月、日、时、分、秒以及星期等时间信息。MM32F5330的RTC模块内部包含一组连续计数的计数器。它作为一个独立的定时器,在相应软件配置下,可提供时钟功能。修改计数器的值可以重新设置系统当前的时间。MM32F5330的RTC提供的是一个32位的计数器,需要自行实现秒到日期时间的转换
enum
{
    YUYY_TIME_WEEK_SUNDAY = 0,
    YUYY_TIME_WEEK_MONDAY,
    YUYY_TIME_WEEK_TUESDAY,
    YUYY_TIME_WEEK_WEDNESDAY,
    YUYY_TIME_WEEK_THURSDAY,
    YUYY_TIME_WEEK_FRIDAY,
    YUYY_TIME_WEEK_SATURDAY,
};
typedef struct
{
    uint8_t month;
    uint8_t day;
    uint8_t week;
    uint8_t hour;
    uint8_t minutes;
    uint8_t seconds;
    uint16_t year;
} YUYY_DateTimeType;
const uint8_t kMonthDays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
bool YUYY_TIME_IsLeapYear(uint16_t year)
{
    if(year%100!=0)
    {
        return (year%4==0);
    }
    else
    {
        return (year%400==0);
    }
}
uint32_t YUYY_TIME_GetSecondFromDateTime(YUYY_DateTimeType *datetime,uint16_t since_year)
{
    uint16_t i = 0,leap_count = 0;
    uint32_t alldays = 0;
    if(datetime->year < since_year)
        return 0;
    i = since_year;
    while (i < datetime->year)
    {
        if(YUYY_TIME_IsLeapYear(i))
            leap_count += 1;
        i+=1;
    }
    alldays = (datetime->year - since_year)*365+leap_count;
    printf("%d \n",leap_count);
    i = 1;
    while (i < datetime->month)
    {
        if(i == 2 && YUYY_TIME_IsLeapYear(datetime->year))
            alldays += 29;
        else
            alldays += kMonthDays[i-1];
        i+=1;
    }
    alldays += datetime->day - 1;
    return alldays*24*3600 + datetime->hour*3600 + datetime->minutes*60 + datetime->seconds;
}
void YUYY_TIME_GetDateTimeFromSecond(uint32_t sec,YUYY_DateTimeType *datetime,uint16_t since_year,uint8_t since_week)
{
    uint16_t i = 0,leap_count = 0;
    uint32_t alldays = 0;
    datetime->seconds = sec%60;
    sec /= 60;
    datetime->minutes = sec%60;
    sec /= 60;
    datetime->hour = sec%24;
    alldays = sec/24;
    i = since_year;
    datetime->week = ((alldays%7)+since_week)%7;
    while (alldays > 365)
    {
        alldays -= 365;
        if(YUYY_TIME_IsLeapYear(i))
        {
            if(alldays>0)
                alldays -= 1;
            else
                break;
        }
        i+=1;
    }
    datetime->year = i;
    i = 0;
    while (alldays >= kMonthDays[i])
    {
        alldays -= kMonthDays[i];
        if(i == 1 && YUYY_TIME_IsLeapYear(datetime->year))
        {
            if(alldays > 0)
                alldays -= 1;
            else
                break;
        }
        i+=1;
    }
    datetime->month = i+1;
    datetime->day = alldays+1;
}

初始化RTC
void APP_RTCInit(void)
{

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWRDBG | 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_WaitForLastTask();
        RTC_SetPrescaler(32767);
        RTC_WaitForLastTask();
        RTC_SetCounter(0);
        RTC_WaitForLastTask();
        BKP_WriteBackupRegister(BKP_DR1, 0x5B5B);
    }
    else
    {
        RTC_WaitForSynchro();
        RTC_WaitForLastTask();
    }
}

按下按键后修改为指定的时间
#define RTC_START_YEAR 2024
#define RTC_START_WEEK YUYY_TIME_WEEK_MONDAY
char *WeekdayStr[7]= {"SUN","MON","TUE","WED","THU","FRI","SAT"};
void APP_SetTestRTC(void)
{
    YUYY_DateTimeType datetime;
    datetime.year = 2024;
    datetime.month = 6;
    datetime.day = 30;
    datetime.hour = 20;
    datetime.minutes = 30;
    datetime.seconds = 30;
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWRDBG | RCC_APB1Periph_BKP, ENABLE);
    PWR_BackupAccessCmd(ENABLE);
    RTC_SetCounter(YUYY_TIME_GetSecondFromDateTime(&datetime,RTC_START_YEAR));
    RTC_WaitForLastTask();
}

打印时间到LCD屏幕
int main(void)
{
    char out[20];
    YUYY_DateTimeType datetime;
    uart_init();
    led_init();
    keys_init();
    exit_init();
    APP_SpiInit();
    APP_LcdInit();
    APP_RTCInit();
    while (1)
    {
        YUYY_DelayMs(1000);
        led_toggle(LED_NO_1);
        YUYY_TIME_GetDateTimeFromSecond(RTC_GetCounter(),&datetime,RTC_START_YEAR,RTC_START_WEEK);
        sprintf(out," %04d-%02d-%02d %s ",datetime.year, datetime.month, datetime.day,WeekdayStr[datetime.week]);
        YUYY_HS12864G18B_DisplayString8x16(&lcd_dev,0,2,0,out);
        sprintf(out,"    %02d:%02d:%02d    ",datetime.hour, datetime.minutes, datetime.seconds);
        YUYY_HS12864G18B_DisplayString8x16(&lcd_dev,0,4,0,out);
    }
}

运行效果


使用特权

评论回复
沙发
地瓜patch| | 2024-7-30 22:36 | 只看该作者
用片内rtc么

使用特权

评论回复
板凳
suncat0504| | 2024-7-31 19:30 | 只看该作者
帖子排版很舒服,楼主用心了。谢谢分享!

使用特权

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

本版积分规则

149

主题

708

帖子

7

粉丝