WeDesign第3期---基于NXP i.MX RT1020实现的日历
基于NXP i.MX RT1021实现的日历,采用lvgl作为显示驱动。
标签
LED
RT1021
WeDesign第3期
冷月烟
更新2023-08-09
540

项目介绍

基于NXP i.MX RT1021实现的日历,使用240*240的oled全彩小屏幕显示,采用lvgl作为显示驱动。

 

项目设计思路(含设计框图)

  1. 最简板子:板上无其他多余外设,大部分为最小系统模块,仅留出一个用户LED灯与复位按键。
  2. 最全接口:所有IO都通过排针引出,都可直接使用。
  3. 最易使用:板载DAP Link,支持调试、下载、串口、供电多种功能,一线通。
  4. 最小体积:主要使用0402封装器件,双面布局布线,四层板设计,减小体积。

FjHhNoCqIP8TJvUooRteHJj0zdF_

 

芯片介绍

Ft3sR0C4u1Gjy8vO_Vcszq9DN4lB

i.MX RT1021跨界MCU是EdgeVerse™ 边缘计算平台的一部分,通过低成本LQFP封装提供高性能功能集,进一步为客户简化板设计与布局。i.MX RT1021 MCU基于Arm® Cortex®-M7内核,运行频率500 MHz。

RT1020系列芯片的特征:

  • 高性能Arm Cortex-M7内核
  • 2517 CoreMark/1070 DMIPS @ 500 MHz
  • 实时低延迟响应,低至20 ns
  • 高达256 kB紧耦合存储器(TCM)
  • 通过集成DC-DC转换器实现行业最低的动态功耗
  • 低功耗运行模式下运行频率为24MHz
  • 丰富的外部存储接口选项
    • NAND、eMMC、QuadSPI NOR Flash和Parallel NOR Flash
  • 无线连接接口:
    • Wi-Fi®、蓝牙、低功耗蓝牙、ZigBee®和Thread™
  • 面向低成本PCB设计的144LQFP和100LQFP封装

FpmkWrxwoONxF-PMOiN5jeMP-AVk

 

搜集素材的思路

主要通过NXP官网获取需要资料。

参考官方RT1020板卡原理图,进行相应改动去实验。

 

画原理图、PCB制板过程中遇到的问题,以及解决方法

一切顺利,没碰见啥问题。主要注意以下设计:

1. 电源引脚和上电时序

对于有VSNVS低功耗需求,需要1个单独的LDO+1个DCDC,上电顺序上需满足VSNVS先于VDD_HIGH_IN和DCDC_IN上电。
对于无VSNVS低功耗需求,可以单电源LDO供电,上电顺序要保证VSNVS与VDD_HIGH_IN和DCDC_IN同时上电。

2.代码启动配置引脚

BOOT_MODE[0:1] (需要留出来作为启动模式选择,外加拨码开关或者电阻,如果是作为复用引脚,需要注意外部上下拉的影响可能导致芯片无法启动)

BOOT_CFG1[0:7]和BOOT_CFG2[0:3](这10个Pin和EMC复用,根据启动配置固定上下拉即可)

对于常用的QSPI启动来说,都是采用默认的0即可,契合内部的下拉电阻,只要外部没有特别的上拉,问题不大。在调试阶段,建议使用拨码开关或者Jumper跳针作为启动选择。后期量产直接就可以Fuse来设定启动模式了,这些引脚可以继续作为其它功能。

3.   时钟输入引脚

RT芯片需要外置RTC32K和24M无源时钟,但是如果客户不需要RTC准确的定时,可以省去RTC 32K,处理方式是RTC_XTALI连接到GND,RTC_XTALO直接Floating。

4. LDO和DCDC输出能力选择

RT中的电流需求是DCDC_IN, VDD_High_IN, VSNVS_IN以及外设的电流之和,最大值200mA+GPIO电流.

5.  DQS引脚设定

为最大程度的提高Flexspi执行速度,如果Flexspi没有DQS引脚,建议FLEXSPI_A_DQS(GPIO_SD_B1_05)引脚Floating,否则只能到60M。

 

实现结果展示(调试过程中遇到什么问题)

板卡展示

Fi8sqUfcAHUSEm__B8_mgOavR37m

FkDNMLVSF5Zp8ZwinCkftHr3ydoL

FvlSAC4sTkddWWFfSw2sNi_HWQ7N

日历界面展示

Fr8NlS3hQul5xzTYnVw30Y39nKo2

调试的过程中碰到屏幕无法电亮的问题,用示波器量了一下发现有IO虚焊,补焊后正常。

 

关键代码及说明

主代码

#define LPSPI_MASTER_DMA_BASEADDR     DMA0
#define LPSPI_MASTER_DMA_MUX_BASEADDR DMAMUX
static volatile bool s_lvgl_initialized = false;
#include "gui_guider.h"
/*******************************************************************************
 * Prototypes
 ******************************************************************************/
#if LV_USE_LOG
static void print_cb(const char *buf)
{
    PRINTF("\r%s\n", buf);
}
#endif
extern void setup_scr_screen(lv_ui *ui);
lv_ui ui;
static void AppTask(void *param)
{
#if LV_USE_LOG
    lv_log_register_print_cb(print_cb);
#endif

    PRINTF("lvgl widgets demo started\r\n");

    lv_port_pre_init();
    lv_init();
    lv_port_disp_init();
    lv_port_indev_init();

    s_lvgl_initialized = true;
    setup_ui(&ui);

    for (;;)
    {
        lv_task_handler();
        vTaskDelay(5);
    }
}

/*******************************************************************************
 * Code
 ******************************************************************************/
/*!
 * @brief Main function
 */
int main(void)
{
    BaseType_t stat;
    gpio_pin_config_t gpio_out_config = {kGPIO_DigitalOutput, 0, kGPIO_NoIntmode};

    /* Init board hardware. */
    BOARD_ConfigMPU();
    BOARD_InitPins();
    BOARD_BootClockRUN();
    BOARD_InitDebugConsole();
    
    GPIO_PinInit(BOARD_INITPINS_LCD_RES_GPIO, BOARD_INITPINS_LCD_RES_GPIO_PIN, &gpio_out_config);
	GPIO_PinWrite(BOARD_INITPINS_LCD_RES_GPIO, BOARD_INITPINS_LCD_RES_GPIO_PIN, 1U);
    
    GPIO_PinInit(BOARD_INITPINS_LCD_DC_GPIO, BOARD_INITPINS_LCD_DC_GPIO_PIN, &gpio_out_config);
	GPIO_PinWrite(BOARD_INITPINS_LCD_DC_GPIO, BOARD_INITPINS_LCD_DC_GPIO_PIN, 1U);

    /* DMA Mux init and EDMA init */
    edma_config_t edmaConfig = {0};
    EDMA_GetDefaultConfig(&edmaConfig);
    EDMA_Init(LPSPI_MASTER_DMA_BASEADDR, &edmaConfig);
#if (defined(FSL_FEATURE_SOC_DMAMUX_COUNT) && FSL_FEATURE_SOC_DMAMUX_COUNT)
    DMAMUX_Init(LPSPI_MASTER_DMA_MUX_BASEADDR);
#endif

    stat = xTaskCreate(AppTask, "lvgl", configMINIMAL_STACK_SIZE + 800, NULL, tskIDLE_PRIORITY + 2, NULL);

    if (pdPASS != stat)
    {
        PRINTF("Failed to create lvgl task");
        while (1)
            ;
    }

    vTaskStartScheduler();

    for (;;)
    {
    } /* should never get here */
}

/*!
 * @brief Malloc failed hook.
 */
void vApplicationMallocFailedHook(void)
{
    PRINTF("Malloc failed. Increase the heap size.");

    for (;;)
        ;
}

/*!
 * @brief FreeRTOS tick hook.
 */
void vApplicationTickHook(void)
{
    if (s_lvgl_initialized)
    {
        lv_tick_inc(1);
    }
}

/*!
 * @brief Stack overflow hook.
 */
void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName)
{
    (void)pcTaskName;
    (void)xTask;

    for (;;)
        ;
}

LVGL显示部分

static void screen_calendar_1draw_part_begin_event_cb(lv_event_t * e)
{
	lv_obj_t * obj = lv_event_get_target(e);
	lv_obj_draw_part_dsc_t * dsc = lv_event_get_param(e);
	if(dsc->part == LV_PART_ITEMS) {
		if(dsc->id < 7) {
			dsc->label_dsc->color = lv_color_make(0x0D, 0x30, 0x55);
			dsc->label_dsc->font = &lv_font_montserratMedium_12;
		}	else if (lv_btnmatrix_has_btn_ctrl(obj, dsc->id, LV_BTNMATRIX_CTRL_DISABLED)) {
			dsc->rect_dsc->bg_color = lv_color_make(0x01, 0xa2, 0xb1);
			dsc->rect_dsc->bg_grad.stops_count = 2;
			dsc->rect_dsc->bg_grad.dir = LV_GRAD_DIR_NONE;
			dsc->rect_dsc->bg_grad.stops[0].color = lv_color_make(0x01, 0xa2, 0xb1);
			dsc->rect_dsc->bg_grad.stops[1].color = lv_color_make(0x01, 0xa2, 0xb1);
			dsc->rect_dsc->bg_opa = 0;
			dsc->label_dsc->color = lv_color_make(0xA9, 0xA2, 0xA2);
			dsc->label_dsc->font = &lv_font_montserratMedium_12;
		}		else if(lv_btnmatrix_has_btn_ctrl(obj, dsc->id, LV_BTNMATRIX_CTRL_CUSTOM_1)) {
			dsc->rect_dsc->bg_color = lv_color_make(0x01, 0xa2, 0xb1);
			dsc->rect_dsc->bg_grad.stops_count = 2;
			dsc->rect_dsc->bg_grad.dir = LV_GRAD_DIR_NONE;
			dsc->rect_dsc->bg_grad.stops[0].color = lv_color_make(0x01, 0xa2, 0xb1);
			dsc->rect_dsc->bg_grad.stops[1].color = lv_color_make(0x01, 0xa2, 0xb1);
			dsc->rect_dsc->bg_opa = 255;
			dsc->rect_dsc->border_color = lv_color_make(0xc0, 0xc0, 0xc0);
			dsc->rect_dsc->border_width = 1;
			dsc->rect_dsc->border_opa = 255;
			dsc->label_dsc->color = lv_color_make(0x0D, 0x30, 0x55);
			dsc->label_dsc->font = &lv_font_montserratMedium_12;
		}
		else if(lv_btnmatrix_has_btn_ctrl(obj, dsc->id, LV_BTNMATRIX_CTRL_CUSTOM_2)) {
			dsc->rect_dsc->bg_color = lv_color_make(0xf0, 0x00, 0x00);
			dsc->rect_dsc->bg_grad.stops_count = 2;
			dsc->rect_dsc->bg_grad.dir = LV_GRAD_DIR_NONE;
			dsc->rect_dsc->bg_grad.stops[0].color = lv_color_make(0xf0, 0x00, 0x00);
			dsc->rect_dsc->bg_grad.stops[1].color = lv_color_make(0x21, 0x95, 0xf6);
			dsc->rect_dsc->bg_opa = 0;
			dsc->label_dsc->color = lv_color_make(0x0D, 0x30, 0x55);
			dsc->label_dsc->font = &lv_font_montserratMedium_12;
		}	else {
			dsc->rect_dsc->bg_color = lv_color_make(0x01, 0xa2, 0xb1);
			dsc->rect_dsc->bg_grad.stops_count = 2;
			dsc->rect_dsc->bg_grad.dir = LV_GRAD_DIR_NONE;
			dsc->rect_dsc->bg_grad.stops[0].color = lv_color_make(0x01, 0xa2, 0xb1);
			dsc->rect_dsc->bg_grad.stops[1].color = lv_color_make(0x01, 0xa2, 0xb1);
			dsc->rect_dsc->bg_opa = 0;
			dsc->rect_dsc->border_color = lv_color_make(0xc0, 0xc0, 0xc0);
			dsc->rect_dsc->border_width = 1;
			dsc->rect_dsc->border_opa = 255;
			dsc->label_dsc->color = lv_color_make(0x0D, 0x30, 0x55);
			dsc->label_dsc->font = &lv_font_montserratMedium_12;
		}
	}
}

static lv_calendar_date_t date;
static void screen_calendar_1_event_handler(lv_event_t * e)
{
	lv_event_code_t code = lv_event_get_code(e);
	if (code == LV_EVENT_VALUE_CHANGED) {
		lv_obj_t * obj =lv_event_get_current_target(e);
		lv_calendar_get_pressed_date(obj,&date);
		lv_calendar_set_highlighted_dates(obj, &date, 1);
	}
}
static lv_obj_t * g_kb_screen;
static void kb_screen_event_cb(lv_event_t *e)
{
	lv_event_code_t code = lv_event_get_code(e);
	lv_obj_t *kb = lv_event_get_target(e);
	if(code == LV_EVENT_READY || code == LV_EVENT_CANCEL){
		lv_obj_add_flag(kb, LV_OBJ_FLAG_HIDDEN);
	}
}
__attribute__((unused)) static void ta_screen_event_cb(lv_event_t *e)
{

	lv_event_code_t code = lv_event_get_code(e);
	lv_obj_t *ta = lv_event_get_target(e);
	lv_obj_t *kb = lv_event_get_user_data(e);
	if (code == LV_EVENT_FOCUSED || code == LV_EVENT_CLICKED)
	{
		lv_keyboard_set_textarea(kb, ta);
		lv_obj_move_foreground(kb);
		lv_obj_clear_flag(kb, LV_OBJ_FLAG_HIDDEN);
	}
	if (code == LV_EVENT_CANCEL || code == LV_EVENT_DEFOCUSED)
	{
		lv_keyboard_set_textarea(kb, NULL);
		lv_obj_move_background(kb);
		lv_obj_add_flag(kb, LV_OBJ_FLAG_HIDDEN);
	}
}

void setup_scr_screen(lv_ui *ui){

	//Write codes screen
	ui->screen = lv_obj_create(NULL);

	//Create keyboard on screen
	g_kb_screen = lv_keyboard_create(ui->screen);
	lv_obj_add_event_cb(g_kb_screen, kb_screen_event_cb, LV_EVENT_ALL, NULL);
	lv_obj_add_flag(g_kb_screen, LV_OBJ_FLAG_HIDDEN);
	lv_obj_set_style_text_font(g_kb_screen, &lv_font_simsun_18, LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_scrollbar_mode(ui->screen, LV_SCROLLBAR_MODE_OFF);

	//Set style for screen. Part: LV_PART_MAIN, State: LV_STATE_DEFAULT
	lv_obj_set_style_bg_color(ui->screen, lv_color_make(0xff, 0xff, 0xff), LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_bg_grad_color(ui->screen, lv_color_make(0x21, 0x95, 0xf6), LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_bg_grad_dir(ui->screen, LV_GRAD_DIR_NONE, LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_bg_opa(ui->screen, 0, LV_PART_MAIN|LV_STATE_DEFAULT);

	//Write codes screen_calendar_1
	ui->screen_calendar_1 = lv_calendar_create(ui->screen);
	lv_obj_set_pos(ui->screen_calendar_1, 0, 0);
	lv_obj_set_size(ui->screen_calendar_1, 240, 240);
	lv_obj_set_scrollbar_mode(ui->screen_calendar_1, LV_SCROLLBAR_MODE_OFF);
	today.year = 2023;
	today.month = 7;
	today.day = 23;
	lv_calendar_set_today_date(ui->screen_calendar_1, today.year, today.month, today.day);
	lv_calendar_set_showed_date(ui->screen_calendar_1, today.year, today.month);
	highlihted_days[0].year = 2023;
	highlihted_days[0].month = 7;
	highlihted_days[0].day = 24;
	lv_calendar_set_highlighted_dates(ui->screen_calendar_1, highlihted_days, 1);
	lv_obj_t *screen_calendar_1_header = lv_calendar_header_arrow_create(ui->screen_calendar_1);
	lv_calendar_t * screen_calendar_1 = (lv_calendar_t *)ui->screen_calendar_1;
	lv_obj_add_event_cb(screen_calendar_1->btnm, screen_calendar_1draw_part_begin_event_cb, LV_EVENT_DRAW_PART_BEGIN, NULL);
	lv_obj_add_event_cb(ui->screen_calendar_1, screen_calendar_1_event_handler, LV_EVENT_ALL, NULL);

	//Set style for screen_calendar_1. Part: LV_PART_MAIN, State: LV_STATE_DEFAULT
	lv_obj_set_style_radius(ui->screen_calendar_1, 0, LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_bg_color(ui->screen_calendar_1, lv_color_make(0xff, 0xff, 0xff), LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_bg_grad_color(ui->screen_calendar_1, lv_color_make(0xff, 0xff, 0xff), LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_bg_grad_dir(ui->screen_calendar_1, LV_GRAD_DIR_NONE, LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_bg_opa(ui->screen_calendar_1, 255, LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_shadow_width(ui->screen_calendar_1, 0, LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_shadow_color(ui->screen_calendar_1, lv_color_make(0x21, 0x95, 0xf6), LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_shadow_opa(ui->screen_calendar_1, 255, LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_shadow_spread(ui->screen_calendar_1, 0, LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_shadow_ofs_x(ui->screen_calendar_1, 0, LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_shadow_ofs_y(ui->screen_calendar_1, 0, LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_border_color(ui->screen_calendar_1, lv_color_make(0xc0, 0xc0, 0xc0), LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_border_width(ui->screen_calendar_1, 1, LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_border_opa(ui->screen_calendar_1, 255, LV_PART_MAIN|LV_STATE_DEFAULT);

	//Set style state: LV_STATE_DEFAULT for style_screen_calendar_1_extra_header_main_default
	static lv_style_t style_screen_calendar_1_extra_header_main_default;
	ui_init_style(&style_screen_calendar_1_extra_header_main_default);
	lv_style_set_bg_color(&style_screen_calendar_1_extra_header_main_default, lv_color_make(0x21, 0x95, 0xf6));
	lv_style_set_bg_grad_color(&style_screen_calendar_1_extra_header_main_default, lv_color_make(0x21, 0x95, 0xf6));
	lv_style_set_bg_grad_dir(&style_screen_calendar_1_extra_header_main_default, LV_GRAD_DIR_NONE);
	lv_style_set_bg_opa(&style_screen_calendar_1_extra_header_main_default, 255);
	lv_style_set_text_color(&style_screen_calendar_1_extra_header_main_default, lv_color_make(0xff, 0xff, 0xff));
	lv_style_set_text_font(&style_screen_calendar_1_extra_header_main_default, &lv_font_montserratMedium_12);
	lv_obj_add_style(screen_calendar_1_header, &style_screen_calendar_1_extra_header_main_default, LV_PART_MAIN|LV_STATE_DEFAULT);
}

OLED驱动

看似代码是FT9341,其实是ST7789,懒得改了。

void FT9341_Init(ili9341_send_byte_t _writeData, ili9341_send_byte_t _writeCommand)
{
    GPIO_PinWrite(BOARD_INITPINS_LCD_RES_GPIO, BOARD_INITPINS_LCD_RES_GPIO_PIN, 0U);
    SDK_DelayAtLeastUs(300 * 1000U, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
    GPIO_PinWrite(BOARD_INITPINS_LCD_RES_GPIO, BOARD_INITPINS_LCD_RES_GPIO_PIN, 1U);
    SDK_DelayAtLeastUs(300 * 1000U, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
    
    _writeCommand(0x36); 
    _writeData(0x00);

    _writeCommand(0x3A); 
    _writeData(0x05);

    _writeCommand(0xB2);
    _writeData(0x0C);
    _writeData(0x0C);
    _writeData(0x00);
    _writeData(0x33);
    _writeData(0x33);

    _writeCommand(0xB7); 
    _writeData(0x35);  

    _writeCommand(0xBB);
    _writeData(0x19);

    _writeCommand(0xC0);
    _writeData(0x2C);

    _writeCommand(0xC2);
    _writeData(0x01);

    _writeCommand(0xC3);
    _writeData(0x12);   

    _writeCommand(0xC4);
    _writeData(0x20);  

    _writeCommand(0xC6); 
    _writeData(0x0F);    

    _writeCommand(0xD0); 
    _writeData(0xA4);
    _writeData(0xA1);

    _writeCommand(0xE0);
    _writeData(0xD0);
    _writeData(0x04);
    _writeData(0x0D);
    _writeData(0x11);
    _writeData(0x13);
    _writeData(0x2B);
    _writeData(0x3F);
    _writeData(0x54);
    _writeData(0x4C);
    _writeData(0x18);
    _writeData(0x0D);
    _writeData(0x0B);
    _writeData(0x1F);
    _writeData(0x23);

    _writeCommand(0xE1);
    _writeData(0xD0);
    _writeData(0x04);
    _writeData(0x0C);
    _writeData(0x11);
    _writeData(0x13);
    _writeData(0x2C);
    _writeData(0x3F);
    _writeData(0x44);
    _writeData(0x51);
    _writeData(0x2F);
    _writeData(0x1F);
    _writeData(0x1F);
    _writeData(0x20);
    _writeData(0x23);

    _writeCommand(0x21); 

    _writeCommand(0x11); 

    _writeCommand(0x29); 
}

 

在芯片设计过程中,遇到什么难题以及解决方法,或未来针对这个芯片的扩展项目

没碰见啥问题。

计划后期设计一块扩展板,板载屏幕,按键,摇杆,加速度,温湿度等外设,作为一个综合学习板。

 

芯片的优势与局限

  1. NXP的i.MX RT系列的低价格策略确实很是吸引人,但是由于芯片没有内置flash,应用起来还是自己外接Flash,成本会相应增加,应用方便性也不如内置flash方便。
  2. 电路设计比较复杂,没有片上的FLash,电源需要上电下电时序,要设计启动方式配置等等。
  3. 性能强大。
  4. 可用IO数量少。

 

原理图/PCB图

见附件

 

可编译下载的代码

见附件

 

物料清单
附件下载
RT1021 PCB.zip
lvgl_demo_benchmark.zip
lvgl_demo_widgets.zip
团队介绍
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号