HAL库硬件SPI点亮板载LCD屏幕流程详解

2、hal库spi在cubemx中的配置
2.1 硬件原理图
先来看看lcd 硬件连接方式:
stm32与lcd引脚对应关系:
stm32 lcd
pa5 spi-clk
pa4 spi_cs
pa7 spi_mosi
pa6 spi_cs
pb2 spi_rst
pe7 ledk
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 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 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 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, clock_hand_obj.angle);    rt_kprintf(clock_time->second = %d, 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 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 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);}  
看下效果(说实话,纯画的挺丑的...):


R型控制变压器在哪些场合得到广泛应用?
真无线蓝牙耳机哪个好?全球真无线耳机音质排名
TCL新推的柔性触控屏智能机原型让人眼前一亮
华为分布式应用开发平台更新,与鸿蒙系统完美互补
中美宽带现状对比 差的不是一点点
HAL库硬件SPI点亮板载LCD屏幕流程详解
他发明了USB,却没有从中赚取一分钱
全球射频前端市场规模现状 中国射频可立不败之地
LG今年第四季将量产手机柔性屏
通过权限控制提高数据库服务器安全
创业公司企业Ventana推RISC-V高性能处理器
小米Redmi Note 9/Pro官方爆料:电量自由
智能合约属于新兴事物所以缺陷和漏洞还非常多
长江存储正式打入苹果供应链 预计2025年长江存储全球市占率将达到约6%
如何做好一个程序员
VASS PLC控制机器人分析
CPU也可以完美运行大模型 英特尔第五代至强重磅发布
机械密封用于渣浆泵的注意事项
华为顶端旗舰机:华为mate9,荣耀V9和华为P10大PK!
Set Your Holiday Lights to Mus