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

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

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, 1, 10);
}
/********************************************************************
 *
 *       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, 1, 10);
}

/********************************************************************
 *
 *       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(25, 195, 20, (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(0, 0, 240, 190, LCD_DISP_WHITE);
    /**状态区域,50*50,黄色背景*/
    // lcd_st7789_fill_area(160, 0, 240, 160, LCD_DISP_WHITE);
    /**数字表区域,50*240,蓝色背景*/
    lcd_st7789_fill_area(0, 190, 240, 240, 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);
}

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

经验交流

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/327326.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

李宏毅LLM——机器学习基础知识

文章目录 机器学习基本概念生成式学习 Structured Learning总结 机器学习基本概念 机器学习 机器自动找出一种函数 根据函数的不同&#xff0c;可以分为回归问题&#xff08;输出数值&#xff09;和分类问题&#xff08;输出类别&#xff0c;选择题&#xff09; 生成式学习 …

【征服redis1】基础数据类型详解和应用案例

博客计划 &#xff0c;我们从redis开始&#xff0c;主要是因为这一块内容的重要性不亚于数据库&#xff0c;但是很多人往往对redis的问题感到陌生&#xff0c;所以我们先来研究一下。 本篇&#xff0c;我们先看一下redis的基础数据类型详解和应用案例。 1.redis概述 以mysql为…

用julia演示蝴蝶效应:洛伦兹吸引子

文章目录 Lorentz吸引子julia绘图关闭抗锯齿 蝴蝶效应的名字来源于蝴蝶扇动翅膀的动作&#xff0c;虽然这个动作微小&#xff0c;但可能会在数周后引起飓风等极端天气的发生。这种现象表明&#xff0c;微小的变化可能会被放大并产生非线性的结果。这个概念最早由美国气象学家爱…

Jmeter接口测试(2024版)

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;薪资嘎嘎涨 Jmeter介绍&测试准备&#xff1a; Jmeter介绍&#xff1a;Jmeter是软件行业里面比较常用…

控制网页的灰度显示

1.代码&#xff1a; 普通网页 <style>html {filter: grayscale(100%);}</style> 或是:webkit内核浏览器写法 <style>html {-webkit-filter: grayscale(100%)}</style> 2.说明&#xff1a; grayscale(amount) :进行灰度转换。 amount转换值的大小&…

MySQL系列之数据导入导出

前言 大数据与云计算作为当今时代&#xff0c;数据要素发展的“动力引擎”&#xff0c;已经走进了社会生活的方方方面。而背后承载的云服务或数据服务的高效运转&#xff0c;起了决定作用。 作为数据存储的重要工具&#xff0c;数据库的品类和特性也日新月异。从树型、网络型…

数据库开发工具:Navicat Premium 16 (Win/Mac)中文激活版

Navicat Premium 16 是一款强大的数据库管理工具&#xff0c;旨在帮助用户更轻松地管理和维护各种数据库类型。 以下是关于 Navicat Premium 16 的详细介绍&#xff1a; 数据库支持&#xff1a;Navicat Premium 16 支持多种数据库类型&#xff0c;包括 MySQL、PostgreSQL、SQLi…

C++核心编程之类和对象---C++面向对象的三大特性--多态

目录 一、多态 1. 多态的概念 2.多态的分类&#xff1a; 1. 静态多态&#xff1a; 2. 动态多态&#xff1a; 3.静态多态和动态多态的区别&#xff1a; 4.动态多态需要满足的条件&#xff1a; 4.1重写的概念&#xff1a; 4.2动态多态的调用&#xff1a; 二、多态 三、多…

Git 使用与问题记录 二(公司快速上手版)

写在前面 记录自己学习的内容&#xff0c;方便后面忘记的时候查看。给像我一样的新手提供一点参考 正文 上一章已经安装好了Git&#xff0c;如何使用呢。我这里会分享两种办法&#xff0c;第一种是在VS2022中克隆代码&#xff0c;修改和提交&#xff1b;第二种是用命令提交。…

Linux Centos7静默安装(非图形安装)Oracle RAC 11gR2(Oracle RAC 11.2.0.4)

Oracle RAC (全称Oracle Real Application Clusters &#xff09;静默安装&#xff08;非图形安装&#xff09;教程。 由于这篇文章花费了我太多时间&#xff0c;设置了仅粉丝可见&#xff0c;见谅。 环境说明&#xff1a; 虚拟机软件&#xff1a;VMware Workstation 16 Pro…

Java IO流

目录 一.字符集 二.JavaIo流体系 三.如何提升读取和写入速度&#xff1f; 四.文件读取乱码问题 一.字符集 ASCII:一个字节存储&#xff0c;首尾是0 GBK: 两个字节存储&#xff0c;首位是1 Unicode:统一码&#xff0c;4个字节存储&#xff0c;容纳世界所有文字 UTF-8:Un…

acwing 图的深度搜索DFS

写目录 邻接表的构建邻接表DFSAcWing 846. 树的重心无向图 pat 1034 Head of a Gang有向图的深度搜索&#xff0c;各连通块分别搜索 邻接表的构建 邻接表DFS const int N 1e5 10, M 2*N; int h[N], e[M], ne[M]; // h[N]: 顶点Ni的第一个连接点 bool visited[M]; …

机器学习周报第27周

目录 摘要Abstract一、文献阅读 摘要 本周阅读了一篇混沌时间序列预测的论文&#xff0c;论文模型主要使用的是时间卷积网络&#xff08;Temporal Convolutional Network&#xff0c;TCN&#xff09;、LSTM以及GRU。在数据集方面除了使用现实的时间序列数据外&#xff0c;还通…

接口防刷方案

1、前言 本文为描述通过Interceptor以及Redis实现接口访问防刷Demo 2、原理 通过ip地址uri拼接用以作为访问者访问接口区分 通过在Interceptor中拦截请求&#xff0c;从Redis中统计用户访问接口次数从而达到接口防刷目的 如下图所示 3、案例工程 项目地址&#xff1a; htt…

MongoDB Compass当前版本及历史版本下载安装

mongoDB compass 当前版本下载 官网 https://www.mongodb.com/try/download/compass 官网下载一般只能下载最新版本。 github https://github.com/mongodb-js/compass MongoDB Compass与MongoDB的版本对应关系 MongoDB CompassMongoDB1.9.12MongoDB 2.6.11 Community

STM32H5 Nucleo-144 board开箱

文章目录 开发板资料下载 【目标】 点亮LD1&#xff08;绿&#xff09;、LD2&#xff08;黄&#xff09;和LD3&#xff08;红&#xff09;三个LED灯 【开箱过程】 博主使用的是STM32CubeMX配置生成代码&#xff0c;具体操作如下&#xff1a; 打开STM32CubeMX&#xff0c;File-…

快速知识付费平台搭建,一分钟搭建你的专属知识服务平台

产品服务 线上线下课程传播 线上线下活动管理 项目撮合交易 找商机找合作 一对一线下交流 企业文化宣传 企业产品销售 更多服务 实时行业资讯 动态学习交流 分销代理推广 独立知识店铺 覆盖全行业 个人IP打造 独立小程序 私域运营解决方案 公域引流 营销转化 …

vue前端开发自学,使用yarn脚手架创建vue项目

vue前端开发自学,使用yarn脚手架创建vue项目&#xff01;下面展示一下&#xff0c;如何在本机操作&#xff0c;使用yarn这款脚手架&#xff0c;创建一个vue项目。 第一步&#xff0c;你需要先创建好&#xff0c;即将存档项目的文件夹。我的路径是在&#xff1a;"D:\yarn\…

C++ OJ基础

C OJ基础 在学校学习C程序设计基础课程的OJ题目 缺少第二十题 这里写目录标题 C OJ基础习题练习(一)打印图形习题练习(二)数据的输入输出习题练习(三)函数重载习题练习(四)设计矩形类习题练习(五)定义Tree类习题练习(六)完善职工工资类Salary的设计习题练习(七)设计矩形类recta…

2、BERT:自然语言处理的变革者

请参考之前写的&#xff1a;2、什么是BERT&#xff1f;-CSDN博客文章浏览阅读826次&#xff0c;点赞19次&#xff0c;收藏22次。BERT&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;是Google在2018年提出的一种自然语言处理&#xff08;NLP&…