05-HAL库硬件SPI点亮板载LCD屏幕

原创 小飞哥玩嵌入式 2024-01-17 08:30

关注、星标公众号,直达精彩内容

1、本节内容介绍

  • 1.1、HAL库硬件SPI 在cubemx中的配置及注意事项;
  • 1.2、HAL库SPI详解与结构介绍;
  • 1.3、实现硬件SPI驱动板载ST7789显示屏,240*240像素;

源码地址:https://gitee.com/MR_Wyf/hal-cubemx-rt-thread/tree/hal_rttNano_st7789_menu/

或者关注公众号,后台回复“TFT”,获取本章节源码

2、HAL库SPI在CUBEMX中的配置

2.1 硬件原理图

先来看看LCD 硬件连接方式:

STM32与LCD引脚对应关系:

STM32LCD
PA5SPI-CLK
PA4SPI_CS
PA7SPI_MOSI
PA6SPI_CS
PB2SPI_RST
PE7LEDK

2.2 cubemx中配置

LCD是挂在硬件SPI的SPI1上,CS引脚也挂在硬件SPI上,可以直接配置,不用再操心CS引脚的电平转换了,库函数内部自动完成。

配置硬件SPI1,LCD驱动秩序要MOSI即可,只发送数据,不接收

配置非常简单,以上就是全部,直接生成代码即可。

2.3 SPI配置代码解析

2.3.1 寄存器配置:

2.3.2 硬件引脚配置

整个过程虽然一行代码没写,但是配置过程考验的是你对SPI的理解,还是需要掌握,只是说现在有工具把重复的代码平台化了。

2.3.3 SPI接口解析:

关于SPI的接口和串口差不多,也是非常多的,HAL库每个库文件前面都有详细的使用说明,大家如果可以看下这部分:

接口方式基本上和串口差不多,读写、中断读写、DMA、回调函数等,基本上都是一个调性。

本次我们使用的比较简单,LCD只需要写就可以,所以我们只用发送函数即可:

/**
  * @brief  Transmit an amount of data in blocking mode.
  * @param  hspi pointer to a SPI_HandleTypeDef structure that contains
  *               the configuration information for SPI module.
  * @param  pData pointer to data buffer
  * @param  Size amount of data to be sent
  * @param  Timeout Timeout duration
  * @retval HAL status
  */


HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);

3、LCD驱动编写

3.1 SPI LCD写数据/命令

这块板子带的LCD显示屏的驱动是ST7789,分辨率是240*240的,关于LCD就不多做介绍了,大家可以自行百度。

先来封装几个用到的函数,写法都比较初级,大佬轻喷....

LCD复位:

// ST7789复位
static void lcd_st7789_reset(void)
{
    HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, GPIO_PIN_SET);
    rt_thread_mdelay(1);
    HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, GPIO_PIN_RESET);
    rt_thread_mdelay(10);
    HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, GPIO_PIN_SET);
    rt_thread_mdelay(120);
}

打开LCD背光灯

void lcd_st7789_power_ctrl(uint8_t enable)
{
    if (enable)
        HAL_GPIO_WritePin(LCD_POWER_EN_GPIO_Port, LCD_POWER_EN_Pin, GPIO_PIN_SET);
    else
        HAL_GPIO_WritePin(LCD_POWER_EN_GPIO_Port, LCD_POWER_EN_Pin, GPIO_PIN_RESET);
}

ST7789写数据/命令

// ST7789写函数
static HAL_StatusTypeDef lcd_st7789_write(int is_cmd, uint8_t data)
{
    uint8_t pData[2] = {0};
    assert_param(NULL != hspi_lcd);
    pData[0] = data;
    if (is_cmd)
        HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_RESET);
    else
        HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_SET);

    return HAL_SPI_Transmit(hspi_lcd, pData, 1, HAL_MAX_DELAY);
}
/********************************************************************
 *
 *       LcdWriteReg
 *
 * Function description:
 *   Sets display register
 */

void lcd_st7789_write_reg(uint8_t Data)
{
    HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_RESET);
    HAL_SPI_Transmit(&hspi1, &Data, 110);
}
/********************************************************************
 *
 *       LcdWriteData
 *
 * Function description:
 *   Writes a value to a display register
 */

void lcd_st7789_write_data(uint8_t Data)
{
    HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_SET);
    HAL_SPI_Transmit(&hspi1, &Data, 110);
}

/********************************************************************
 *
 *       lcd_st7789_write_data_multiple
 *
 * Function description:
 *   Writes multiple values to a display register.
 */

void lcd_st7789_write_data_multiple(uint8_t *pData, int NumItems)
{
    HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_SET);
    HAL_SPI_Transmit(&hspi1, pData, NumItems, 10);
}

基本的驱动函数就这些,都是SPI写数据或者写命令的函数,具体可以看源码。

3.2 LCD基本驱动函数

让LCD亮起来,实际上就是操作一个个像素点,以下封装了一些基本函数,只放出了函数接口名,具体的可以公众号后台回复“TFT”获取源码:

/**
@brief   以一种颜色清空LCD屏
@param   color —— 清屏颜色(16bit)
@return  none
*/

void lcd_st7789_clear(uint16_t color);

/**
@brief   以一种颜色清填充LCD屏区域
@param   color —— 清屏颜色(16bit)
@return  none
*/

void lcd_st7789_fill_area(uint16_t xs, uint16_t ys, uint16_t xe, uint16_t ye, uint16_t color);
/**
@brief   以一种颜色划横线
@param   color —— 划线颜色(16bit)
@return  none
*/

void lcd_st7789_draw_x_line(uint16_t xs, uint16_t ys, uint16_t xe, uint16_t ye, uint16_t color);

/**
@brief   以一种颜色划竖线
@param   color —— 划线颜色(16bit)
@return  none
*/

void lcd_st7789_draw_y_line(uint16_t xs, uint16_t ys, uint16_t xe, uint16_t ye, uint16_t color);

/**
@brief   以一种颜色划四边形
@param   color —— 划线颜色(16bit)
@return  none
*/

void lcd_draw_rectangle(uint16_t xs, uint16_t ys, uint16_t xe, uint16_t ye, uint16_t color);

// 往指定区域写一个像素
void lcd_st7789_write_pixel(uint16_t Xpos, uint16_t Ypos, uint16_t data);

/**
@brief 带颜色画任意角度直线,相对长度
@param   xs 起点坐标
@param   xe 终点坐标
@return  none
*/

void lcd_st778_draw_angle_relative_line(uint16_t xs, uint16_t ys, float angle, uint16_t r, uint16_t lens,uint16_t lene, uint16_t color);

/**
@brief  带颜色画任意角度直线,绝对长度
@param
@param
@return  none
*/

void lcd_st778_draw_angle_absolute_line(uint16_t xs, uint16_t ys, float angle,uint16_t len, uint16_t color);

/**
@brief  带颜色画线函数(直线、斜线)
@param   xs,ys 起点坐标
@param   xe,ye 终点坐标
@return  none
*/

void lcd_st778_draw_colorline(uint16_t xs, uint16_t ys, uint16_t xe, uint16_t ye, uint16_t color);

/**
@breif 带颜色画圆函数
@param   x1,x2 —— 圆心坐标
@param r —— 半径
@param color —— 颜色
@retval none
*/

void lcd_st7789_color_circle(uint16_t x, uint16_t y, uint16_t r, uint16_t color);

/**
@brief 显示图片函数
@param   x,y     —— 起点坐标
@param   width —— 图片宽度
@param   height —— 图片高度
@param   p       —— 图片缓存数据起始地址
@return  none
@note Image2Lcd取模方式:C语言数据/水平扫描/16位真彩色(RGB565)/高位在前,其他的不选
*/

void lcd_st7789_show_picture(uint16_t x, uint16_t y, uint16_t width, uint16_t height, const uint8_t *p);

/**
@brief 显示一个ASCII码字符
@param   x,y  显示起始坐标
@param   ch  需要显示的字符
@param   size 字体大小(支持16/24/32号字体)
@return  none
@note 需要font.h字库文件的支持
*/

static void lcd_st7789_show_char(uint16_t x, uint16_t y, uint8_t ch, uint16_t back_color, uint16_t font_color, uint8_t font_size);


4、实战画表

上面介绍了那么多绘图接口,接下来就来实际画个表来玩玩....说实话,画表挺有意思的....就是很费劲....

const time_angle_table_t time_angle_table_second_min[60] = {
    {.time_count = 0,
     .angle = 270},
    {.time_count = 1,
     .angle = 270 + CLOCK_LITTLE_ANGLE},
    {.time_count = 2,
     .angle = 270 + CLOCK_LITTLE_ANGLE * 2},
    {.time_count = 3,
     .angle = 270 + CLOCK_LITTLE_ANGLE * 3},
    {.time_count = 4,
     .angle = 270 + CLOCK_LITTLE_ANGLE * 4},
    {.time_count = 5,
     .angle = 270 + CLOCK_LITTLE_ANGLE * 5},
    {.time_count = 6,
     .angle = 270 + CLOCK_LITTLE_ANGLE * 6},
    {.time_count = 7,
     .angle = 270 + CLOCK_LITTLE_ANGLE * 7},
    {.time_count = 8,
     .angle = 270 + CLOCK_LITTLE_ANGLE * 8},
    {.time_count = 9,
     .angle = 270 + CLOCK_LITTLE_ANGLE * 9},
    {.time_count = 10,
     .angle = 270 + CLOCK_LITTLE_ANGLE * 10},
    {.time_count = 11,
     .angle = 270 + CLOCK_LITTLE_ANGLE * 11},
    {.time_count = 12,
     .angle = 270 + CLOCK_LITTLE_ANGLE * 12},
    {.time_count = 13,
     .angle = 270 + CLOCK_LITTLE_ANGLE * 13},
    {.time_count = 14,
     .angle = 270 + CLOCK_LITTLE_ANGLE * 14},
    {.time_count = 15,
     .angle = 270 + CLOCK_LITTLE_ANGLE * 15},
    {.time_count = 16,
     .angle = 0 + CLOCK_LITTLE_ANGLE * 1},
    {.time_count = 17,
     .angle = CLOCK_LITTLE_ANGLE * 2},
    {.time_count = 18,
     .angle = CLOCK_LITTLE_ANGLE * 3},
    {.time_count = 19,
     .angle = CLOCK_LITTLE_ANGLE * 4},
    {.time_count = 20,
     .angle = CLOCK_LITTLE_ANGLE * 5},
    {.time_count = 21,
     .angle = CLOCK_LITTLE_ANGLE * 6},
    {.time_count = 22,
     .angle = CLOCK_LITTLE_ANGLE * 7},
    {.time_count = 23,
     .angle = CLOCK_LITTLE_ANGLE * 8},
    {.time_count = 24,
     .angle = CLOCK_LITTLE_ANGLE * 9},
    {.time_count = 25,
     .angle = CLOCK_LITTLE_ANGLE * 10},
    {.time_count = 26,
     .angle = CLOCK_LITTLE_ANGLE * 11},
    {.time_count = 27,
     .angle = CLOCK_LITTLE_ANGLE * 12},
    {.time_count = 28,
     .angle = CLOCK_LITTLE_ANGLE * 13},
    {.time_count = 29,
     .angle = CLOCK_LITTLE_ANGLE * 14},
    {.time_count = 30,
     .angle = CLOCK_LITTLE_ANGLE * 15},
    {.time_count = 31,
     .angle = CLOCK_LITTLE_ANGLE * 16},
    {.time_count = 32,
     .angle = CLOCK_LITTLE_ANGLE * 17},
    {.time_count = 33,
     .angle = CLOCK_LITTLE_ANGLE * 18},
    {.time_count = 34,
     .angle = CLOCK_LITTLE_ANGLE * 19},
    {.time_count = 35,
     .angle = CLOCK_LITTLE_ANGLE * 20},
    {.time_count = 36,
     .angle = CLOCK_LITTLE_ANGLE * 21},
    {.time_count = 37,
     .angle = CLOCK_LITTLE_ANGLE * 22},
    {.time_count = 38,
     .angle = CLOCK_LITTLE_ANGLE * 23},
    {.time_count = 39,
     .angle = CLOCK_LITTLE_ANGLE * 24},
    {.time_count = 40,
     .angle = CLOCK_LITTLE_ANGLE * 25},
    {.time_count = 41,
     .angle = CLOCK_LITTLE_ANGLE * 26},
    {.time_count = 42,
     .angle = CLOCK_LITTLE_ANGLE * 27},
    {.time_count = 43,
     .angle = CLOCK_LITTLE_ANGLE * 28},
    {.time_count = 44,
     .angle = CLOCK_LITTLE_ANGLE * 29},
    {.time_count = 45,
     .angle = CLOCK_LITTLE_ANGLE * 30},
    {.time_count = 46,
     .angle = CLOCK_LITTLE_ANGLE * 31},
    {.time_count = 47,
     .angle = CLOCK_LITTLE_ANGLE * 32},
    {.time_count = 48,
     .angle = CLOCK_LITTLE_ANGLE * 33},
    {.time_count = 49,
     .angle = CLOCK_LITTLE_ANGLE * 34},
    {.time_count = 50,
     .angle = CLOCK_LITTLE_ANGLE * 35},
    {.time_count = 51,
     .angle = CLOCK_LITTLE_ANGLE * 36},
    {.time_count = 52,
     .angle = CLOCK_LITTLE_ANGLE * 37},
    {.time_count = 53,
     .angle = CLOCK_LITTLE_ANGLE * 38},
    {.time_count = 54,
     .angle = CLOCK_LITTLE_ANGLE * 39},
    {.time_count = 55,
     .angle = CLOCK_LITTLE_ANGLE * 40},
    {.time_count = 56,
     .angle = CLOCK_LITTLE_ANGLE * 41},
    {.time_count = 57,
     .angle = CLOCK_LITTLE_ANGLE * 42},
    {.time_count = 58,
     .angle = CLOCK_LITTLE_ANGLE * 43},
    {.time_count = 59,
     .angle = CLOCK_LITTLE_ANGLE * 44},
};

const time_angle_table_t time_angle_table_hour[12] = {
    {.time_count = 1,
     .angle = 270 + CLOCK_BIG_ANGLE},
    {.time_count = 2,
     .angle = 270 + CLOCK_BIG_ANGLE * 2},
    {.time_count = 3,
     .angle = 0},
    {.time_count = 4,
     .angle = CLOCK_BIG_ANGLE * 1},
    {.time_count = 5,
     .angle = CLOCK_BIG_ANGLE * 2},
    {.time_count = 6,
     .angle = CLOCK_BIG_ANGLE * 3},
    {.time_count = 7,
     .angle = CLOCK_BIG_ANGLE * 4},
    {.time_count = 8,
     .angle = CLOCK_BIG_ANGLE * 5},
    {.time_count = 9,
     .angle = CLOCK_BIG_ANGLE * 6},
    {.time_count = 10,
     .angle = CLOCK_BIG_ANGLE * 7},
    {.time_count = 11,
     .angle = CLOCK_BIG_ANGLE * 8},
    {.time_count = 12,
     .angle = CLOCK_BIG_ANGLE * 9}};

void (*current_operation_index)(void);
static menu_para_t menu_para = {0};
/**画表针*/
static void menu_draw_clock_hand(clock_hand_obj_t *clock_hand_obj)
{
    lcd_st778_draw_angle_absolute_line(clock_hand_obj->xs, clock_hand_obj->ys, clock_hand_obj->angle, clock_hand_obj->lens, clock_hand_obj->color);
}
/**清除指定长度的表针*/
static void menu_erase_clock_hand(clock_hand_obj_t *clock_hand_obj)
{
    lcd_st778_draw_angle_relative_line(clock_hand_obj->xs, clock_hand_obj->ys, clock_hand_obj->angle, clock_hand_obj->r, clock_hand_obj->lens, clock_hand_obj->lene, clock_hand_obj->color);
}
/**画小刻度*/
static void menu_draw_sec_min_scale(clock_hand_obj_t *clock_hand_obj)
{
    uint8_t i = 0;
    uint16_t angle = 0;
    /**画秒\分钟刻度*/
    for (i = 0; i < CLOCK_LITTLE_ANGLE_SCALE; i++)
    {
        lcd_st778_draw_angle_relative_line(clock_hand_obj->xs, clock_hand_obj->ys, angle, clock_hand_obj->r, clock_hand_obj->lens, clock_hand_obj->lene, clock_hand_obj->color);
        angle += CLOCK_LITTLE_ANGLE;
    }
}
/**画大刻度*/
static void menu_draw_hour_scale(clock_hand_obj_t *clock_hand_obj)
{
    uint8_t i = 0;
    uint16_t angle = 0;
    /**画一刻钟刻度*/
    for (i = 0; i < CLOCK_BIG_ANGLE_SCALE; i++)
    {
        lcd_st778_draw_angle_relative_line(clock_hand_obj->xs, clock_hand_obj->ys, angle, clock_hand_obj->r, clock_hand_obj->lens, clock_hand_obj->lene, clock_hand_obj->color);
        angle += CLOCK_BIG_ANGLE;
    }
}
static void menu_draw_clock_panel_init(void)
{
    clock_hand_obj_t clock_hand_obj = {0};
    /**画秒\分钟刻度*/
    clock_hand_obj.xs = CLOCK_CENTER_XS;
    clock_hand_obj.ys = CLOCK_CENTER_YS;
    clock_hand_obj.r = CLOCK_PANEL_RADIUS;
    clock_hand_obj.lens = CLOCK_MIN_SEC_ANGLE_LEN;
    clock_hand_obj.lene = CLOCK_PANEL_RADIUS;
    clock_hand_obj.color = LCD_DISP_RED;
    menu_draw_sec_min_scale(&clock_hand_obj);
    /**画一刻钟刻度*/
    clock_hand_obj.lens = CLOCK_HOUR_ANGLE_LEN;
    clock_hand_obj.color = LCD_DISP_BLUE;
    menu_draw_hour_scale(&clock_hand_obj);
    //    /**画初始化秒针*/
    //    clock_hand_obj.xs = 80;
    //    clock_hand_obj.ys = 80;
    //    clock_hand_obj.angle = SENCOND_HAND_ANGLE;
    //    clock_hand_obj.lens = SECOND_HAND_LEN;
    //    clock_hand_obj.color = LCD_DISP_RED;
    //    menu_draw_clock_hand(&clock_hand_obj);
    //    /**画初始化分针*/
    //    clock_hand_obj.angle = MINUTE_HAND_ANGLE;
    //    clock_hand_obj.lens = MINUTE_HAND_LEN;
    //    menu_draw_clock_hand(&clock_hand_obj);
    //    /**画初始化时针*/
    //    clock_hand_obj.angle = HOUR_HAND_ANGLE;
    //    clock_hand_obj.lens = HOUR_HAND_LEN;
    //    menu_draw_clock_hand(&clock_hand_obj);

    /**三针交汇中间圆环*/
    lcd_st7789_color_circle(CLOCK_CENTER_XS, CLOCK_CENTER_YS, 2, LCD_DISP_RED);
    lcd_st7789_color_circle(CLOCK_CENTER_XS, CLOCK_CENTER_YS, 3, LCD_DISP_RED);
    lcd_st7789_color_circle(CLOCK_CENTER_XS, CLOCK_CENTER_YS, 4, LCD_DISP_RED);
}

void menu_analog_watch_run(clock_time_t *clock_time)
{
    int i = 0;
    static uint8_t clock_hour_state = 1;

    clock_hand_obj_t clock_hand_obj = {0};

    /**秒针*/
    for (i = 0; i < 60; i++)
    {
        if (clock_time->second == time_angle_table_second_min[i].time_count)
        {
            clock_hand_obj.angle = time_angle_table_second_min[i].angle;
            break;
        }
    }
    rt_kprintf("clock_hand_obj.angle = %d\n", clock_hand_obj.angle);
    rt_kprintf("clock_time->second = %d\n", clock_time->second);

    clock_hand_obj.xs = CLOCK_CENTER_XS;
    clock_hand_obj.ys = CLOCK_CENTER_YS;
    clock_hand_obj.lens = SECOND_HAND_LEN;
    clock_hand_obj.color = LCD_DISP_RED;
    menu_draw_clock_hand(&clock_hand_obj);

    /**如果秒针将要追赶上分针*/
    if ((clock_time->second - clock_time->minute == 1) || (clock_time->second - clock_time->minute == -59))
    {
        clock_hand_obj.lens = CLOCK_PANEL_RADIUS - MINUTE_HAND_LEN;
        clock_hand_obj.lene = CLOCK_PANEL_RADIUS-CLOCK_HOUR_ANGLE_LEN;
        clock_hand_obj.r = CLOCK_PANEL_RADIUS;
        clock_hand_obj.color = LCD_DISP_WHITE;

        if (clock_time->second)
        {
            clock_hand_obj.angle = time_angle_table_second_min[clock_time->second - 1].angle;
        }
        else
        {
            clock_hand_obj.angle = time_angle_table_second_min[59].angle;
        }
        menu_erase_clock_hand(&clock_hand_obj);
    }

    else
    {
        clock_hand_obj.xs = CLOCK_CENTER_XS;
        clock_hand_obj.ys = CLOCK_CENTER_YS;

        clock_hand_obj.r = CLOCK_PANEL_RADIUS;
        clock_hand_obj.color = LCD_DISP_WHITE;

        if (i && (5 * (clock_time->hour) + 1 == clock_time->second))
        {
            clock_hand_obj.lens = CLOCK_PANEL_RADIUS - HOUR_HAND_LEN;
            clock_hand_obj.lene = CLOCK_PANEL_RADIUS-CLOCK_HOUR_ANGLE_LEN;
        }
        else
        {
            clock_hand_obj.lens = CLOCK_PANEL_RADIUS - 5;
            clock_hand_obj.lene = CLOCK_PANEL_RADIUS-CLOCK_HOUR_ANGLE_LEN;
        }
        if (i)
        {
            clock_hand_obj.angle = time_angle_table_second_min[i - 1].angle;
            menu_erase_clock_hand(&clock_hand_obj);
        }
        else
        {
            clock_hand_obj.angle = time_angle_table_second_min[59].angle;
            menu_erase_clock_hand(&clock_hand_obj);
        }
    }
    /**分针*/
    for (i = 0; i < 60; i++)
    {
        if (clock_time->minute == time_angle_table_second_min[i].time_count)
        {
            clock_hand_obj.angle = time_angle_table_second_min[i].angle;
            break;
        }
    }

    clock_hand_obj.xs = CLOCK_CENTER_XS;
    clock_hand_obj.ys = CLOCK_CENTER_YS;
    clock_hand_obj.lens = MINUTE_HAND_LEN;
    clock_hand_obj.color = LCD_DISP_RED;
    menu_draw_clock_hand(&clock_hand_obj);

    /**如果秒针将要追赶上分针*/
    if ((clock_time->minute - clock_time->second == 1) || (clock_time->minute - clock_time->second == -59))
    {
    }

    else
    {
        clock_hand_obj.r = CLOCK_PANEL_RADIUS;
        clock_hand_obj.color = LCD_DISP_WHITE;
        if ((clock_time->second == 0) && ((clock_time->hour - clock_time->minute == 11) || (5 * (clock_time->hour) + 1 == clock_time->minute)))
        {
            clock_hour_state = 0;
        }
        else
        {
            clock_hour_state = 1;
        }
        if (i &&
            ((5 * (clock_time->hour) + 1 == clock_time->minute) ||
             (clock_time->hour - clock_time->minute == 11)) &&
            clock_hour_state)
        {
            clock_hand_obj.lens = CLOCK_PANEL_RADIUS - HOUR_HAND_LEN;
            clock_hand_obj.lene = CLOCK_PANEL_RADIUS-CLOCK_HOUR_ANGLE_LEN;
        }
        else
        {
            clock_hand_obj.lens = CLOCK_PANEL_RADIUS - 5;
            clock_hand_obj.lene = CLOCK_PANEL_RADIUS-CLOCK_HOUR_ANGLE_LEN;
        }

        if (i)
        {
            clock_hand_obj.angle = time_angle_table_second_min[i - 1].angle;
            menu_erase_clock_hand(&clock_hand_obj);
        }
        else
        {
            clock_hand_obj.angle = time_angle_table_second_min[59].angle;
            menu_erase_clock_hand(&clock_hand_obj);
        }
    }
    /**时针*/
    for (i = 0; i < 12; i++)
    {
        if (clock_time->hour == time_angle_table_hour[i].time_count)
        {
            clock_hand_obj.angle = time_angle_table_hour[i].angle;
            break;
        }
    }
    /**画初始化时针*/
    clock_hand_obj.xs = CLOCK_CENTER_XS;
    clock_hand_obj.ys = CLOCK_CENTER_YS;
    clock_hand_obj.lens = HOUR_HAND_LEN;
    clock_hand_obj.color = LCD_DISP_RED;

    menu_draw_clock_hand(&clock_hand_obj);
}
void menu_digital_watch_run(clock_time_t *clock_time)
{
    char data_disp[50] = {0};
    sprintf(data_disp, "%02d%s%02d%s%02d", clock_time->hour, " : ", clock_time->minute, " : ", clock_time->second);
    lcd_st7789_show_str(2519520, (uint8_t *)data_disp, LCD_DISP_BLUE, LCD_DISP_WHITE, 32);
}
void menu_clock_time_bar(clock_time_t *clock_time)
{
    lcd_st7789_fill_area(160,0,160,20,LCD_DISP_YELLOW);
}
void menu_clock_run(clock_time_t *clock_time)
{
    menu_analog_watch_run(clock_time);
    menu_digital_watch_run(clock_time);
    menu_clock_time_bar(clock_time);
}

void menu_main_window(void)
{
    char data_disp[50] = {0};
    /**160*160,绿色背景*/
    lcd_st7789_fill_area(00240190, LCD_DISP_WHITE);
    /**状态区域,50*50,黄色背景*/
    // lcd_st7789_fill_area(160, 0, 240, 160, LCD_DISP_WHITE);
    /**数字表区域,50*240,蓝色背景*/
    lcd_st7789_fill_area(0190240240, LCD_DISP_BLUE);

    lcd_st7789_color_circle(CLOCK_CENTER_XS, CLOCK_CENTER_YS,CLOCK_PANEL_RADIUS, LCD_DISP_RED);
    lcd_st7789_color_circle(CLOCK_CENTER_XS, CLOCK_CENTER_YS, CLOCK_PANEL_RADIUS-1, LCD_DISP_RED);

    lcd_st7789_color_circle(CLOCK_CENTER_XS, CLOCK_CENTER_YS, CLOCK_PANEL_RADIUS-5, LCD_DISP_YELLOW);
    lcd_st7789_color_circle(CLOCK_CENTER_XS, CLOCK_CENTER_YS, CLOCK_PANEL_RADIUS-6, LCD_DISP_YELLOW);

    menu_draw_clock_panel_init();

    // sprintf(data_disp, "%s", "Digital");
    // lcd_st7789_show_str(165, 20, 20, (uint8_t *)data_disp, LCD_DISP_YELLOW, LCD_DISP_BLUE, 24);

    // sprintf(data_disp, "%s", "Clock");
    // lcd_st7789_show_str(165, 50, 20, (uint8_t *)data_disp, LCD_DISP_YELLOW, LCD_DISP_BLUE, 24);

    // sprintf(data_disp, "%s", "Pause");
    // lcd_st7789_show_str(165, 90, 20, (uint8_t *)data_disp, LCD_DISP_YELLOW, LCD_DISP_RED, 24);

    // sprintf(data_disp, "%s", "Alarm");
    // lcd_st7789_show_str(165, 120, 20, (uint8_t *)data_disp, LCD_DISP_YELLOW, LCD_DISP_RED, 24);

    // sprintf(data_disp, "%s", "12 : 12 : 12");
    // lcd_st7789_show_str(20, 190, 20, (uint8_t *)data_disp, LCD_DISP_BLUE, LCD_DISP_WHITE, 32);
}

看下效果(说实话,纯画的挺丑的...):

经验交流

今天分享就到这里啦,欢迎关注“小飞哥玩嵌入式”,好久不更新这个系列教程了,今年事情确实有点多,后面继续更新,跟兄弟们一起交流学习


小飞哥玩嵌入式 分享嵌入式开发相关知识,喜欢DIY分享
评论
  • 根据Global Info Research项目团队最新调研,预计2030年全球封闭式电机产值达到1425百万美元,2024-2030年期间年复合增长率CAGR为3.4%。 封闭式电机是一种电动机,其外壳设计为密闭结构,通常用于要求较高的防护等级的应用场合。封闭式电机可以有效防止外部灰尘、水分和其他污染物进入内部,从而保护电机的内部组件,延长其使用寿命。 环洋市场咨询机构出版的调研分析报告【全球封闭式电机行业总体规模、主要厂商及IPO上市调研报告,2025-2031】研究全球封闭式电机总体规
    GIRtina 2025-01-06 11:10 89浏览
  •     为控制片内设备并且查询其工作状态,MCU内部总是有一组特殊功能寄存器(SFR,Special Function Register)。    使用Eclipse环境调试MCU程序时,可以利用 Peripheral Registers Viewer来查看SFR。这个小工具是怎样知道某个型号的MCU有怎样的寄存器定义呢?它使用一种描述性的文本文件——SVD文件。这个文件存储在下面红色字体的路径下。    例:南京沁恒  &n
    电子知识打边炉 2025-01-04 20:04 85浏览
  • 这篇内容主要讨论三个基本问题,硅电容是什么,为什么要使用硅电容,如何正确使用硅电容?1.  硅电容是什么首先我们需要了解电容是什么?物理学上电容的概念指的是给定电位差下自由电荷的储藏量,记为C,单位是F,指的是容纳电荷的能力,C=εS/d=ε0εrS/4πkd(真空)=Q/U。百度百科上电容器的概念指的是两个相互靠近的导体,中间夹一层不导电的绝缘介质。通过观察电容本身的定义公式中可以看到,在各个变量中比较能够改变的就是εr,S和d,也就是介质的介电常数,金属板有效相对面积以及距离。当前
    知白 2025-01-06 12:04 111浏览
  • PLC组态方式主要有三种,每种都有其独特的特点和适用场景。下面来简单说说: 1. 硬件组态   定义:硬件组态指的是选择适合的PLC型号、I/O模块、通信模块等硬件组件,并按照实际需求进行连接和配置。    灵活性:这种方式允许用户根据项目需求自由搭配硬件组件,具有较高的灵活性。    成本:可能需要额外的硬件购买成本,适用于对系统性能和扩展性有较高要求的场合。 2. 软件组态   定义:软件组态主要是通过PLC
    丙丁先生 2025-01-06 09:23 71浏览
  • 在智能家居领域中,Wi-Fi、蓝牙、Zigbee、Thread与Z-Wave等无线通信协议是构建短距物联局域网的关键手段,它们常在实际应用中交叉运用,以满足智能家居生态系统多样化的功能需求。然而,这些协议之间并未遵循统一的互通标准,缺乏直接的互操作性,在进行组网时需要引入额外的网关作为“翻译桥梁”,极大地增加了系统的复杂性。 同时,Apple HomeKit、SamSung SmartThings、Amazon Alexa、Google Home等主流智能家居平台为了提升市占率与消费者
    华普微HOPERF 2025-01-06 17:23 97浏览
  • 随着市场需求不断的变化,各行各业对CPU的要求越来越高,特别是近几年流行的 AIOT,为了有更好的用户体验,CPU的算力就要求更高了。今天为大家推荐由米尔基于瑞芯微RK3576处理器推出的MYC-LR3576核心板及开发板。关于RK3576处理器国产CPU,是这些年的骄傲,华为手机全国产化,国人一片呼声,再也不用卡脖子了。RK3576处理器,就是一款由国产是厂商瑞芯微,今年第二季推出的全新通用型的高性能SOC芯片,这款CPU到底有多么的高性能,下面看看它的几个特性:8核心6 TOPS超强算力双千
    米尔电子嵌入式 2025-01-03 17:04 48浏览
  • 本文介绍Linux系统更换开机logo方法教程,通用RK3566、RK3568、RK3588、RK3576等开发板,触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。制作图片开机logo图片制作注意事项(1)图片必须为bmp格式;(2)图片大小不能大于4MB;(3)BMP位深最大是32,建议设置为8;(4)图片名称为logo.bmp和logo_kernel.bmp;开机
    Industio_触觉智能 2025-01-06 10:43 72浏览
  • 自动化已成为现代制造业的基石,而驱动隔离器作为关键组件,在提升效率、精度和可靠性方面起到了不可或缺的作用。随着工业技术不断革新,驱动隔离器正助力自动化生产设备适应新兴趋势,并推动行业未来的发展。本文将探讨自动化的核心趋势及驱动隔离器在其中的重要角色。自动化领域的新兴趋势智能工厂的崛起智能工厂已成为自动化生产的新标杆。通过结合物联网(IoT)、人工智能(AI)和机器学习(ML),智能工厂实现了实时监控和动态决策。驱动隔离器在其中至关重要,它确保了传感器、执行器和控制单元之间的信号完整性,同时提供高
    腾恩科技-彭工 2025-01-03 16:28 166浏览
  • 彼得·德鲁克被誉为“现代管理学之父”,他的管理思想影响了无数企业和管理者。然而,关于他的书籍分类,一种流行的说法令人感到困惑:德鲁克一生写了39本书,其中15本是关于管理的,而其中“专门写工商企业或为企业管理者写的”只有两本——《为成果而管理》和《创新与企业家精神》。这样的表述广为流传,但深入探讨后却发现并不完全准确。让我们一起重新审视这一说法,解析其中的矛盾与根源,进而重新认识德鲁克的管理思想及其著作的真正价值。从《创新与企业家精神》看德鲁克的视角《创新与企业家精神》通常被认为是一本专为企业管
    优思学院 2025-01-06 12:03 76浏览
  • 光耦合器,也称为光隔离器,是一种利用光在两个隔离电路之间传输电信号的组件。在医疗领域,确保患者安全和设备可靠性至关重要。在众多有助于医疗设备安全性和效率的组件中,光耦合器起着至关重要的作用。这些紧凑型设备经常被忽视,但对于隔离高压和防止敏感医疗设备中的电气危害却是必不可少的。本文深入探讨了光耦合器的功能、其在医疗应用中的重要性以及其实际使用示例。什么是光耦合器?它通常由以下部分组成:LED(发光二极管):将电信号转换为光。光电探测器(例如光电晶体管):检测光并将其转换回电信号。这种布置确保输入和
    腾恩科技-彭工 2025-01-03 16:27 171浏览
  • 每日可见的315MHz和433MHz遥控模块,你能分清楚吗?众所周知,一套遥控设备主要由发射部分和接收部分组成,发射器可以将控制者的控制按键经过编码,调制到射频信号上面,然后经天线发射出无线信号。而接收器是将天线接收到的无线信号进行解码,从而得到与控制按键相对应的信号,然后再去控制相应的设备工作。当前,常见的遥控设备主要分为红外遥控与无线电遥控两大类,其主要区别为所采用的载波频率及其应用场景不一致。红外遥控设备所采用的射频信号频率一般为38kHz,通常应用在电视、投影仪等设备中;而无线电遥控设备
    华普微HOPERF 2025-01-06 15:29 93浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦