[AT32F4212] 硬件SPI——驱动LCD屏的一些尝试

[复制链接]
4340|19
 楼主| 呐咯密密 发表于 2024-3-1 10:03 | 显示全部楼层 |阅读模式
总感觉之前的AT32F421板子/片子 有点小毛病,出各种莫名其妙的BUG(实在找不出软件的问题,只能怀疑是硬件 QAQ)。

于是之后咕了很久,最近终于想继续折腾,拿AT32F435画了一块LCD驱动板,准备入坑LVGL。板上资源就一块某园的2.8存240x320带电阻膜的LCD屏、触摸IC用XPT2046,另外还画了一片W25Q64和CH340在上面,有空试试QSPI和ISP功能。

画板子的时候就在思考这个问题:

XPT2046 和 LCD(ST7789) 到底要不要共用1个SPI接口?

之前画过一个小的实验板参照LCD厂家提供的手册上的画法,LCD和XPT2046共用一个SPI。其中有一个我不理解的地方,那就是LCD明明不会发送数据(难道不是这样?),但是却有一个数据引脚SDD,在没有触摸IC的应用中,该引脚悬空;在有XPT2046的应用中,厂家的手册上推荐把该引脚同XPT2046的MISO(同时也是单片机的SPI输入引脚)相连。

这真的是必要的吗?为什么需要用4根线来驱动一个LCD,即使它不可能知道外部有没有触摸IC的存在?

于是新板子上我尝试了以下方法:

双线双工,和XPT2046共用SPI,软件管理CS
双线双工,单独使用一个SPI,硬件管理CS
双线双工,单独使用一个SPI,软件管理CS
单线半双工,单独使用一个SPI,硬件管理CS
基于几个基本逻辑:节约CPU资源:只要不是共用SPI,就用硬件CS;节约引脚:只要没有用到数据输入,就不使用双线双工。因此这里面合理的选项只有1和4。另外,只要和触摸IC共用SPI,就不得不用到MISO,且“一主多从”只支持软件CS。

1在之前的小板上就已经实现过了,但是觉得在IO资源足够的情况下,还是应当以节约CPU资源为主,所以舍弃了软件CS,想试试方法4:在LCD不与XPT2046共用SPI的时候,仅使用 SCLK, MOSI, CS(硬件控制) 3根线来驱动LCD。

然后理所当然的踩了一堆坑。。。调的头都要秃了,在此记录一下。

关于AT32F435开启单线半双工模式
刚开始我以为只要在 spi_init_struct 中配置下面这一项:
    spi_init_struct.transmission_mode = SPI_TRANSMIT_HALF_DUPLEX_TX;
然后 在spi_init() 函数里传进去就够了,结果代码都运行了,没有任何现象,检查走信号的GPIO,初始化后只能看到有一点点毛刺。这种情况通常可能是

GPIO时钟没使能
SPI时钟没使能
SPI没使能
然而负责这三行代码都执行过了,怎么出的问题?

检查寄存器的时候才发现,每当我初始化SPI设置后、执行 spi_enable() 去使能SPI时,STS寄存器蹦出一个MMERR(主模式错误),同时把我的模式改成了从模式,还关掉了我的SPI使能。

查了AT的RM,上面也没说什么情况下会触发MMERR,仔细检查RM提供的信息,总结了下面几点:

单线半双工模式下ctrl1_bit.slben将使能,并且ctrl1_bit.slbtd决定是发还是收;
主模式下 ctrl1_bit.swcsil 必须置位;
ctrl2_bit.hwcsoe 在做主设备时,如果置位,测CS输出低电平;如果清零,必须保证CS电平为高电平。
事实上对于第一项,如果设置了SPI_TRANSMIT_HALF_DUPLEX_TX,则在spi_init()中这两个bit会被配置完成。第二项只有在使用软件CS时才有用。所以最后证明是 hwcsoe 背大锅。。。

RM表示 ctrl2_bit.hwcsoe 是一个 rw 位,它的名字虽然叫硬件CS使能,但描述的功能不像是使能,却像是在用软件控制CS啊?!

所以我只好在spi_enable()前加上把这一位置1的命令。结果就没有MMERR了。

遂舍弃方法4,为了继续追求硬件CS,只能尝试方法2
关于硬件管理CS

在单线半双工下通过 ctrl2_bit.hwcsoe=1 开启硬件CS输出使能,导致的结果就是 spi使能后 全过程 CS一直为低电平。

查看RM:

当 SPI 作主机,硬件 CS 输出时,HWCSOE 位置 1,SWCSEN 置 0,开启硬件 CS 控制,SPI 在使能之后会在 CS 管脚上输出低电平,在 SPI 关闭并且发送完成后,释放 CS 信号。

RM表示:一直开着CS有啥不好?反正你都只有这一个从设备了。你要是想发完一个frame就关CS,那你直接把SPI关了不就o了?


属于又颠覆我对SPI的认知了。这个 ctrl2_bit.hwcsoe()主要是为 MCU做从设备,拿来检测是否被片选用的;如果是做主机,那它就会一直把CS拉低。


然而有的SPI从设备会在CS的上下沿完成锁存等操作,甚至在时序要求里对CS的拉低、释放时间有具体的要求,比如下图

30165e1374843327.png

如上图,AD9833对CS的时序有一个最小setup time (t7)和最小hold time (t8),甚至t8还有一个最大值要求:不能超过t4-5ns。总之需要只在每一个数据帧传输的时候拉低CS,而之后要能迅速释放。


不死心的我仍决定尝试在每发送完一个帧后关掉SPI,实现SPI通信的代码变成这个样子:

6189165e1375945f44.png

果令人失望,没能成功点屏。其中很令人在意的一点就是尽管硬件CS拉低很迅速,但释放(通过失能SPI实现)的速度非常慢,过程持续了百微妙级别:关掉SPI后CS信号的样子是典型RC充电曲线的形状。这就导致了在连续传输数据帧的时候,上一帧的CS根本没来得及完全释放,就马上被下一帧拉低了。


总之,硬件管理CS的尝试算是彻底失败了——因为硬件CS做不到只在一个数据帧传输的时候拉低CS。


如果干脆始终拉低CS,我也做了尝试,结果不行。怀疑是过早拉低CS导致多识别了一个时钟沿导致,我尝试了两种SPI时钟相位和两种SPI时钟空闲电平的4种组合。。,都不行。但是作为对照组,仅仅把CS改成软件管理,其余设置不变,就能在时钟POL=HIGH, PHA=2EDGE的组合下成功点屏。


所以排除时钟相位问题后,结论只能是LCD的驱动芯片需要每帧数据结束后CS的上升沿完成某些操作。


所以底层硬件就决定了即便该LCD单独使用一个SPI驱动,也不可能是单线半双工模式实现——因为它只支持硬件管理CS,而硬件管理CS又不能满足CS的时序要求。


那么答案只有一个,变成软件管理CS,MCU直接控制GPIO在每一帧数据结束后拉高CS。即上文的方法2同样不可行,只能改成方法3。那反正都软件管理CS了,干脆和触摸IC共用一个SPI得了,所以最后还是老老实实回到方法1。。


碎碎念:仔细想想,其实LCD的另一些控制信号如RST, DC 后者几乎快和CS一样常用,都只能靠软件实现。所以也许我真的不应该追求硬件CS?


我当前的GCC编译优化为O0,CPU主频288M,SPI通信用8分频(APB的144M / 8 = 18M),在这个速度下,实测方法2中一个8bit数据帧传输<500ns,最后一个时钟结束后约100ns 近300ns(用上图中的代码)CS被拉高,CS高电平又持续了近300ns(这个过程完成了DC的拉高,以及MCU数据搬运),然后CS进入下一帧的拉低,拉低后又是约300ns,才开始下一帧的数据传输。

2928665e1377164778.png


 楼主| 呐咯密密 发表于 2024-3-1 10:05 | 显示全部楼层
后续优化了代码,所有图像输出均由DMA完成。
3428065e137a20c09a.png
并尝试运行了LVGL demo:
8526265e137c1e7b93.png
  1. #include "ST7789.h"

  2. void LCD_GPIO_init(void)
  3. {
  4. #if LCD_USE_HARDWARE_SPI == 1

  5.     gpio_init_type gpio_init_struct;

  6.     // =========================> 初始化 SPI数据总线 GPIO <=========================
  7.     crm_periph_clock_enable(LCD_SPIx_GPIO_CRM, TRUE);

  8.     gpio_default_para_init(&gpio_init_struct);

  9.     gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
  10.     gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
  11.     gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  12.     // SCLK
  13.     gpio_init_struct.gpio_pull = GPIO_PULL_DOWN;
  14.     gpio_init_struct.gpio_pins = LCD_SPIx_SCLK_GPIO_PIN;
  15.     gpio_init(LCD_SPIx_GPIO, &gpio_init_struct);
  16.     gpio_pin_mux_config(LCD_SPIx_GPIO, LCD_SPIx_SCLK_SOURCE, LCD_SPIx_MUX_SEL);

  17.     // MOSI
  18.     gpio_init_struct.gpio_pull = GPIO_PULL_UP;
  19.     gpio_init_struct.gpio_pins = LCD_SPIx_MOSI_GPIO_PIN;
  20.     ;
  21.     gpio_init(LCD_SPIx_GPIO, &gpio_init_struct);
  22.     gpio_pin_mux_config(LCD_SPIx_GPIO, LCD_SPIx_MOSI_SOURCE, LCD_SPIx_MUX_SEL);

  23. #if LCD_SHARE_SPI_WITH_TOUCH_IC == 1
  24.     // LCD和触摸IC共用SPI,CS由软件管理,此处不作配置
  25.     // 由于要接收触摸IC的数据,需要配置MISO,注意XPT2046的数据输出引脚外部不能上拉或下拉,只能配置成浮空输入
  26.     gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
  27.     gpio_init_struct.gpio_pins = LCD_SPIx_MISO_GPIO_PIN;
  28.     gpio_init(LCD_SPIx_GPIO, &gpio_init_struct);
  29.     gpio_pin_mux_config(LCD_SPIx_GPIO, LCD_SPIx_MISO_SOURCE, LCD_SPIx_MUX_SEL);
  30. #else
  31.     // LCD单独使用SPI,无需配置MISO,并且为硬件管理CS,
  32.     gpio_init_struct.gpio_pull = GPIO_PULL_UP;
  33.     gpio_init_struct.gpio_pins = LCD_SPIx_CS_GPIO_PIN;
  34.     gpio_init(LCD_SPIx_GPIO, &gpio_init_struct);
  35.     gpio_pin_mux_config(LCD_SPIx_GPIO, LCD_SPIx_CS_SOURCE, LCD_SPIx_MUX_SEL);
  36. #endif

  37.     // =========================> 初始化其余信号线的GPIO <=========================
  38.     crm_periph_clock_enable(LCD_GPIOPORT_CRM, TRUE);

  39.     gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  40.     gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
  41.     gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
  42.     gpio_init_struct.gpio_pull = GPIO_PULL_NONE;

  43. #if LCD_SHARE_SPI_WITH_TOUCH_IC == 1
  44.     // LCD和触摸IC共用SPI,CS由MCU直接控制GPIO实现
  45.     gpio_init_struct.gpio_pins = LCD_PINS_RST | LCD_PINS_DC | LCD_PINS_BLK | LCD_PINS_CS;
  46.     gpio_init(LCD_GPIOPORT, &gpio_init_struct);
  47.     gpio_bits_set(LCD_GPIOPORT, LCD_PINS_RST | LCD_PINS_DC | LCD_PINS_BLK | LCD_PINS_CS);
  48. #else
  49.     // LCD单独使用SPI,CS已经由硬件实现,此处不作配置
  50.     #error "Hardware CS is is a piece of shit, DO NOT try!"
  51.     gpio_init_struct.gpio_pins = LCD_PINS_RST | LCD_PINS_DC | LCD_PINS_BLK;
  52.     gpio_init(LCD_GPIOPORT, &gpio_init_struct);
  53.     gpio_bits_set(LCD_GPIOPORT, LCD_PINS_RST | LCD_PINS_DC | LCD_PINS_BLK);
  54. #endif

  55. #else
  56.     // 使用软件SPI 且必定共用一个SPI
  57.     crm_periph_clock_enable(LCD_GPIOPORT_CRM, TRUE);

  58.     gpio_init_type gpio_init_struct;
  59.     gpio_default_para_init(&gpio_init_struct);
  60.     gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  61.     gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
  62.     gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
  63.     gpio_init_struct.gpio_pins = LCD_PINS_SCLK | LCD_PINS_MOSI | LCD_PINS_RST | LCD_PINS_DC | LCD_PINS_BLK | LCD_PINS_CS;
  64.     gpio_init_struct.gpio_pull = GPIO_PULL_NONE;

  65.     gpio_init(LCD_GPIOPORT, &gpio_init_struct);

  66.     gpio_bits_set(LCD_GPIOPORT, LCD_PINS_SCLK | LCD_PINS_MOSI | LCD_PINS_RST | LCD_PINS_DC | LCD_PINS_BLK | LCD_PINS_CS);
  67. #endif
  68. }

  69. #if LCD_USE_HARDWARE_SPI == 1 && LCD_HARDWARE_SPI_USE_DMA == 0

  70. /**

  71.   * [url=home.php?mod=space&uid=247401]@brief[/url]  spi configuration.
  72.   * @param  none
  73.   * @retval none
  74.   */
  75. void LCD_SPIx_init(void)
  76. {
  77.     spi_init_type spi_init_struct;

  78.     spi_i2s_reset(LCD_SPIx);
  79.     crm_periph_clock_enable(LCD_SPIx_CRM, TRUE);
  80.     spi_default_para_init(&spi_init_struct);
  81.     spi_init_struct.master_slave_mode = SPI_MODE_MASTER;        // 设置SPI工作模式:主机模式
  82.     spi_init_struct.mclk_freq_division = LCD_SPI_SPEED;        // 288M / 8 = 36M
  83.     spi_init_struct.first_bit_transmission = SPI_FIRST_BIT_MSB; // 数据传输高位先行
  84.     spi_init_struct.frame_bit_num = SPI_FRAME_8BIT;             // 设置SPI数据大小:8位帧结构
  85.     spi_init_struct.clock_polarity = SPI_CLOCK_POLARITY_HIGH;   // 串行同步时钟空闲时SCLK位高电平
  86. #if LCD_SHARE_SPI_WITH_TOUCH_IC == 1
  87.     spi_init_struct.clock_phase = SPI_CLOCK_PHASE_2EDGE;        // 串行同步时钟空第二个时钟沿捕获
  88.     spi_init_struct.transmission_mode = SPI_TRANSMIT_FULL_DUPLEX; // 要接收触模信息所以是全双工
  89.     spi_init_struct.cs_mode_selection = SPI_CS_SOFTWARE_MODE;     // 片选信号由软件管理
  90. #else
  91.     spi_init_struct.clock_phase = SPI_CLOCK_PHASE_2EDGE;        // 串行同步时钟空第二个时钟沿捕获
  92.     spi_init_struct.transmission_mode = SPI_TRANSMIT_HALF_DUPLEX_TX; // LCD只接收信息所以是单工
  93.     spi_init_struct.cs_mode_selection = SPI_CS_HARDWARE_MODE;        // 片选信号由硬件管理
  94.     #error "Hardware CS is is a piece of shit, DO NOT try!"
  95.     spi_hardware_cs_output_enable(LCD_SPIx, TRUE);
  96. #endif
  97.     spi_init(LCD_SPIx, &spi_init_struct);
  98.     spi_enable(LCD_SPIx, TRUE);
  99. }



  100. #elif LCD_HARDWARE_SPI_USE_DMA == 1

  101. // uint16_t LCD_SPIx_TX_buffer[LCD_SPI_TX_BUFFER_SIZE];
  102. // #if (LCD_SHARE_SPI_WITH_TOUCH_IC == 1)
  103. // uint16_t LCD_SPIx_RX_buffer[LCD_SPI_RX_BUFFER_SIZE];
  104. // #endif

  105. void LCD_SPIx_init(void)
  106. {

  107.     dma_init_type dma_init_struct;
  108.     spi_init_type spi_init_struct;
  109.    
  110.     /************** DMA 配置 *****************/

  111.     /* LCD_SPI_TX_DMAx_CHy configuration */
  112.     // 时钟配置
  113.     crm_periph_clock_enable(LCD_SPI_TX_DMAx_CRM_CLOCK, TRUE);
  114.     dma_reset(LCD_SPI_TX_DMAx_CHy);
  115.     // DMA MUX 通道配置
  116.     dmamux_enable(LCD_SPI_TX_DMAx, TRUE);
  117.     dmamux_init(LCD_SPI_TX_DMAxMUX_CHy, LCD_SPI_TX_DMAMUX_REQ_ID);
  118.     // DMA 通道配置
  119.     dma_default_para_init(&dma_init_struct);
  120.     dma_init_struct.buffer_size = LCD_SPI_TX_BUFFER_SIZE;
  121.     dma_init_struct.direction = DMA_DIR_MEMORY_TO_PERIPHERAL;
  122.     // dma_init_struct.memory_base_addr = (uint32_t)LCD_SPIx_TX_buffer;
  123.     dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_HALFWORD;
  124.     dma_init_struct.memory_inc_enable = TRUE;
  125.     dma_init_struct.peripheral_base_addr = (uint32_t)&(LCD_SPIx->dt);
  126.     // dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE; // 如果 MWIDTH 是16bit,但 PWIDTH 是8bit,方向是M2P,那么DMA只会搬运存MEM中每个半字的低8位,也即低地址处的字节。
  127.     dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_HALFWORD;
  128.     dma_init_struct.peripheral_inc_enable = FALSE;
  129.     dma_init_struct.priority = DMA_PRIORITY_MEDIUM;
  130.     dma_init_struct.loop_mode_enable = FALSE; //是否循环模式
  131.     dma_init(LCD_SPI_TX_DMAx_CHy, &dma_init_struct);

  132.     #if (LCD_SHARE_SPI_WITH_TOUCH_IC == 1)
  133.     /* LCD_SPI_TX_DMAx_CHy configuration */
  134.     // DMA MUX 通道配置
  135.     dmamux_init(LCD_SPI_RX_DMAxMUX_CHy, LCD_SPI_RX_DMAMUX_REQ_ID);
  136.     // DMA 通道配置
  137.     dma_default_para_init(&dma_init_struct);
  138.     dma_init_struct.buffer_size = LCD_SPI_RX_BUFFER_SIZE;
  139.     dma_init_struct.direction = DMA_DIR_PERIPHERAL_TO_MEMORY;
  140.     // dma_init_struct.memory_base_addr = (uint32_t)LCD_SPIx_RX_buffer;
  141.     dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_BYTE;
  142.     dma_init_struct.memory_inc_enable = TRUE;
  143.     dma_init_struct.peripheral_base_addr = (uint32_t)&(LCD_SPIx->dt);
  144.     dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE;
  145.     dma_init_struct.peripheral_inc_enable = FALSE;
  146.     dma_init_struct.priority = DMA_PRIORITY_MEDIUM;
  147.     dma_init_struct.loop_mode_enable = FALSE; //是否循环模式
  148.     dma_init(LCD_SPI_RX_DMAx_CHy, &dma_init_struct);
  149.     #else
  150.     // 不需要 RX buffer 和 RX DMA
  151.     #endif

  152.     /************** SPI 配置 *****************/

  153.     crm_periph_clock_enable(LCD_SPIx_CRM, TRUE);
  154.     spi_default_para_init(&spi_init_struct);
  155.     #if (LCD_SHARE_SPI_WITH_TOUCH_IC == 1)
  156.     spi_init_struct.transmission_mode = SPI_TRANSMIT_FULL_DUPLEX;
  157.     #else
  158.     spi_init_struct.transmission_mode = SPI_TRANSMIT_HALF_DUPLEX_TX;
  159.     #endif
  160.     spi_init_struct.master_slave_mode = SPI_MODE_MASTER;
  161.     spi_init_struct.mclk_freq_division = LCD_SPI_SPEED;
  162.     spi_init_struct.first_bit_transmission = SPI_FIRST_BIT_MSB;
  163.     spi_init_struct.frame_bit_num = SPI_FRAME_16BIT;
  164.     spi_init_struct.clock_polarity = SPI_CLOCK_POLARITY_HIGH;
  165.     spi_init_struct.clock_phase = SPI_CLOCK_PHASE_2EDGE;
  166.     spi_init_struct.cs_mode_selection = SPI_CS_SOFTWARE_MODE;

  167.     spi_init(LCD_SPIx, &spi_init_struct);
  168.     spi_i2s_dma_transmitter_enable(LCD_SPIx, TRUE);
  169.     // spi_i2s_dma_receiver_enable(LCD_SPIx,TRUE);
  170.     spi_enable(LCD_SPIx, TRUE);

  171.     #if (LCD_HARDWARE_SPI_TX_DMA_USE_INT == 1)
  172.     //中断配置
  173.     nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);

  174.     /* enable transfer full data intterrupt 当数据全部发送完毕后产生中断*/
  175.     dma_interrupt_enable(LCD_SPI_TX_DMAx_CHy, DMA_FDT_INT, TRUE);
  176.     /* LCD_SPI_TX_DMAx_CHy interrupt nvic init */
  177.     nvic_irq_enable(LCD_SPI_TX_DMAx_CHy_IRQn, 5, 0);
  178.     /* config flexible dma for LCD_SPI_TX_DMAxMUX_CHy 配置弹性映射 */
  179.     dma_flexible_config(LCD_SPI_TX_DMAx, LCD_SPI_TX_DMAxMUX_CHy, LCD_SPI_TX_DMAMUX_REQ_ID);

  180.     #if (LCD_SHARE_SPI_WITH_TOUCH_IC == 1)
  181.     /* enable transfer full data intterrupt 当数据全部发送完毕后产生中断*/
  182.     dma_interrupt_enable(LCD_SPI_RX_DMAx_CHy, DMA_FDT_INT, TRUE);
  183.     /* LCD_SPI_RX_DMAx_CHy interrupt nvic init */
  184.     nvic_irq_enable(LCD_SPI_RX_DMAx_CHy_IRQn, 3, 0);
  185.     /* config flexible dma for LCD_SPI_RX_DMAxMUX_CHy 配置弹性映射 */
  186.     dma_flexible_config(LCD_SPI_TX_DMAx, LCD_SPI_RX_DMAxMUX_CHy, LCD_SPI_RX_DMAMUX_REQ_ID);
  187.     #endif
  188.     #endif
  189.    
  190. }

  191. /**
  192.   * @brief  data transfer to LCD_SPIx using DMA
  193.   * @param  buf : buffer address
  194.   * @param  size : this uint32_t value should be less than 65535. Or otherwise size%0xFFFF is what finally takes effect.
  195.   * @retval none
  196.   */
  197. void LCD_SPI_data_send_use_DMA(const void *buf, uint32_t size)
  198. {
  199.     #if (LCD_HARDWARE_SPI_TX_DMA_USE_INT == 1)
  200.     // 在使用 SPI TX DMA 中断的情况下,如果此时DMA还在搬运,那么应该等DMA中断服务程序结束后再向DMA发送新的buf和size,否则DMA传输中断,屏幕收到数据不完整
  201.     if (LCD_SPI_TX_DMAx_CHy->ctrl_bit.chen == 1)
  202.     {
  203.         __WFI();
  204.     }
  205.     #endif
  206.     // 确保 LCD_SPIx 为16bit传输模式 且 频率为初始设定频率
  207.     if(LCD_SPIx->ctrl1_bit.fbn == SPI_FRAME_8BIT || LCD_SPIx->ctrl1_bit.mdiv_l != LCD_SPI_SPEED)
  208.     {
  209.         // 需要把SPI改为16bit模式 或 把频率改为设定频率

  210.         // DMA搬运完成,但此时SPI还在发送,等待SPI通信忙结束
  211.         while (spi_i2s_flag_get(LCD_SPIx, SPI_I2S_TDBE_FLAG) == RESET && spi_i2s_flag_get(LCD_SPIx, SPI_I2S_BF_FLAG) == SET) // Tx dt buf空 清零,说明还有数据在传输
  212.         {
  213.             // 等待主机数据发送完毕
  214.         }
  215.         while (spi_i2s_flag_get(LCD_SPIx, SPI_I2S_RDBF_FLAG) == RESET && spi_i2s_flag_get(LCD_SPIx, SPI_I2S_BF_FLAG) == SET) // Rx dt buf满 清零,说明还有数据未发送
  216.         {
  217.             // 等待主机数据接收完毕
  218.         }
  219.         spi_i2s_data_receive(LCD_SPIx);
  220.         // SPI改为16bit模式
  221.         spi_frame_bit_num_set(LCD_SPIx, SPI_FRAME_16BIT);
  222.         // SPI时钟分频改为初始设定值
  223.         LCD_SPIx->ctrl1_bit.mdiv_l = LCD_SPI_SPEED;
  224.     }

  225.     // 确保对应通道的 CHEN 位为 0,否则无法写入
  226.     dma_channel_enable(LCD_SPI_TX_DMAx_CHy, FALSE);
  227.     LCD_SPI_TX_DMAx_CHy->maddr = (uint32_t)buf;
  228.     LCD_SPI_TX_DMAx_CHy->dtcnt_bit.cnt = size; // 注意 dtcnt 是一个32位寄存器,但只有低16位是可用的
  229.     // 开启DMA搬运前拉低CS
  230.     LCD_CS_CLR();
  231.     // DMA开始搬运,随即SPI开始传输
  232.     dma_channel_enable(LCD_SPI_TX_DMAx_CHy, TRUE);
  233.     // =============================> 接收通道的DMA配置 <=============================
  234.     #if (LCD_SHARE_SPI_WITH_TOUCH_IC == 1)
  235.     dma_channel_enable(LCD_SPI_RX_DMAx_CHy, FALSE);
  236.     // 确保对应通道的 CHEN 位为 0,否则无法写入
  237.     // LCD_SPI_RX_DMAx_CHy->maddr = (uint32_t)LCD_SPIx_RX_buffer;
  238.     LCD_SPI_RX_DMAx_CHy->dtcnt_bit.cnt = size; // 注意 dtcnt 是一个32位寄存器,但只有低16位是可用的
  239.     dma_channel_enable(LCD_SPI_RX_DMAx_CHy, TRUE); // (在轮询结束或在中断服务程序的最后会关掉)
  240.     #endif

  241.     #if LCD_HARDWARE_SPI_TX_DMA_USE_INT == 1
  242.     // 将在中断服务程序中完成 标志位清除、等待SPI发送结束并读一次数据(如果是全双工)、关闭DMA通道、拉高CS 的工作
  243.     #else
  244.     /*** 若不开DMA完成中断,则轮询DMAx_FDTy标志位 ***/
  245.     // while(spi_i2s_flag_get(LCD_SPIx,SPI_I2S_BF_FLAG) == SET){};
  246.     // while(dma_flag_get(DMA2_FDT1_FLAG) == RESET)
  247.     // {
  248.     // };
  249.     // dma_flag_clear(DMA2_FDT1_FLAG);
  250.     // LCD_CS_SET();
  251.     // // spi_i2s_dma_transmitter_enable(LCD_SPIx, FALSE);
  252.     // dma_channel_enable(LCD_SPI_TX_DMAx_CHy, FALSE);
  253.     // spi_i2s_data_receive(LCD_SPIx);
  254.     // // lv_disp_flush_complete();

  255.     #endif
  256. }
  257. #if LCD_HARDWARE_SPI_TX_DMA_USE_INT == 1
  258. // 将在中断服务程序中完成 标志位清除、等待SPI发送结束并读一次数据(如果是全双工)、关闭DMA通道、拉高CS 的工作

  259. void LCD_SPI_TX_DMAx_CHy_IRQHandler(void)
  260. {
  261.     /*DMA发送完成中断*/
  262.     if (dma_flag_get(LCD_SPI_TX_DMAx_FDTy_FLAG) == SET)
  263.     {
  264.         dma_channel_enable(LCD_SPI_TX_DMAx_CHy, FALSE);

  265.         // DMA搬运完成,但此时SPI还在发送
  266.         while (spi_i2s_flag_get(LCD_SPIx, SPI_I2S_TDBE_FLAG) == RESET || spi_i2s_flag_get(LCD_SPIx, SPI_I2S_RDBF_FLAG) == SET || spi_i2s_flag_get(LCD_SPIx, SPI_I2S_BF_FLAG) == SET)// Tx dt buf非空 或者 Rx dt buf满 或者 通信忙
  267.         {
  268.             spi_i2s_data_receive(LCD_SPIx);
  269.         }
  270.         while (spi_i2s_flag_get(LCD_SPIx, SPI_I2S_ROERR_FLAG) == SET || spi_i2s_flag_get(LCD_SPIx, SPI_I2S_BF_FLAG) == SET)// Tx dt buf非空 或者 Rx dt buf满 或者 通信忙
  271.         {
  272.             spi_i2s_data_receive(LCD_SPIx);
  273.         }
  274.         spi_i2s_data_receive(LCD_SPIx); // !!!血泪教训!!全双工下DMA发完SPI后一定要读一次!!!
  275.         LCD_CS_SET();
  276.         // // DMA改回默认设置:MEM地址递增 (刷纯色的时候会改成FALSE)
  277.         // LCD_SPI_TX_DMAx_CHy->ctrl_bit.mincm = TRUE;
  278.         dma_flag_clear(LCD_SPI_TX_DMAx_FDTy_FLAG);
  279.     }
  280. }

  281. #endif

  282. void LCD_SPI_RX_DMAx_CHy_IRQHandler(void)
  283. {
  284.     /*DMA发送完成中断*/
  285.     if (dma_flag_get(LCD_SPI_RX_DMAx_FDTy_FLAG) == SET)
  286.     {
  287.         dma_flag_clear(LCD_SPI_RX_DMAx_FDTy_FLAG);
  288.         // LCD_SPI_RX_DMAx_CHy 将在发送数据的时候被再次使能
  289.         dma_channel_enable(LCD_SPI_RX_DMAx_CHy, FALSE);
  290.     }
  291. }
  292. #endif

  293. /******************************************************************************
  294.      函数说明:LCD串行数据写入函数
  295.     入口数据:data  要写入的串行数据
  296.     返回值:  无
  297. ******************************************************************************/
  298. static void LCD_SPI_write_bus(uint16_t data)
  299. {
  300. #if (LCD_USE_HARDWARE_SPI == 1)
  301.     // #if (LCD_HARDWARE_SPI_USE_DMA == 1)
  302.     // LCD_SPI_data_send_use_DMA(&data, 1);
  303.     // #else
  304.     LCD_CS_CLR();
  305.     while (spi_i2s_flag_get(LCD_SPIx, SPI_I2S_TDBE_FLAG) == RESET && spi_i2s_flag_get(LCD_SPIx, SPI_I2S_BF_FLAG) == SET) // Tx dt buf空 清零,说明还有数据在传输
  306.     {
  307.         // 等待主机数据发送完毕
  308.     }
  309.     spi_i2s_data_transmit(LCD_SPIx, data);
  310.     while (spi_i2s_flag_get(LCD_SPIx, SPI_I2S_RDBF_FLAG) == RESET && spi_i2s_flag_get(LCD_SPIx, SPI_I2S_BF_FLAG) == SET) // Rx dt buf满 清零,说明还有数据未发送
  311.     {
  312.         // 等待主机数据接收完毕
  313.     }
  314.     spi_i2s_data_receive(LCD_SPIx);// 即使实际上没有用到 MISO,也需要读一次DT寄存器
  315.     // while (spi_i2s_flag_get(LCD_SPIx, SPI_I2S_TDBE_FLAG) == RESET && spi_i2s_flag_get(LCD_SPIx, SPI_I2S_BF_FLAG) == SET) // Tx dt buf空 清零,说明还有数据在传输
  316.     // {
  317.     //     // 等待主机数据发送完毕
  318.     //     // 他说他自己发完了,其实他发完个屁
  319.     // }
  320.     LCD_CS_SET();
  321.     // #endif
  322. #else
  323.     uint8_t i;
  324.     LCD_CS_CLR();
  325.     for (i = 0; i < 8; i++) {
  326.         LCD_SCLK_CLR();
  327.         if (data & 0x80) {
  328.             LCD_MOSI_SET();
  329.         } else {
  330.             LCD_MOSI_CLR();
  331.         }
  332.         LCD_SCLK_SET();
  333.         data <<= 1;
  334.     }
  335.     LCD_CS_SET();
  336. #endif
  337. }

  338. /******************************************************************************
  339.      函数说明:LCD写入数据
  340.     入口数据:data 写入的数据
  341.     返回值:  无
  342. ******************************************************************************/
  343. void LCD_write_byte(uint8_t data_byte)
  344. {
  345.     // 确保 LCD_SPIx 为8bit传输模式
  346.     if(LCD_SPIx->ctrl1_bit.fbn == SPI_FRAME_16BIT)
  347.     {
  348.         // 需要把SPI改为16bit模式
  349.         // 等待SPI通信忙结束
  350.         while (spi_i2s_flag_get(LCD_SPIx, SPI_I2S_TDBE_FLAG) == RESET && spi_i2s_flag_get(LCD_SPIx, SPI_I2S_BF_FLAG) == SET) // Tx dt buf空 清零,说明还有数据在传输
  351.         {
  352.             // 等待主机数据发送完毕
  353.         }
  354.         while (spi_i2s_flag_get(LCD_SPIx, SPI_I2S_RDBF_FLAG) == RESET && spi_i2s_flag_get(LCD_SPIx, SPI_I2S_BF_FLAG) == SET) // Rx dt buf满 清零,说明还有数据未发送
  355.         {
  356.             // 等待主机数据接收完毕
  357.         }
  358.         spi_i2s_data_receive(LCD_SPIx);
  359.         // SPI改为8bit模式
  360.         spi_frame_bit_num_set(LCD_SPIx, SPI_FRAME_8BIT);
  361.     }
  362.     LCD_SPI_write_bus(data_byte);
  363. }

  364. /******************************************************************************
  365.      函数说明:LCD写入数据
  366.     入口数据:data 写入的数据
  367.     返回值:  无
  368. ******************************************************************************/
  369. void LCD_write_half(uint16_t data_half)
  370. {
  371.     // 确保 LCD_SPIx 为16bit传输模式
  372.     if(LCD_SPIx->ctrl1_bit.fbn == SPI_FRAME_8BIT)
  373.     {
  374.         // 需要把SPI改为16bit模式

  375.         // 等待SPI通信忙结束
  376.         while (spi_i2s_flag_get(LCD_SPIx, SPI_I2S_TDBE_FLAG) == RESET && spi_i2s_flag_get(LCD_SPIx, SPI_I2S_BF_FLAG) == SET) // Tx dt buf空 清零,说明还有数据在传输
  377.         {
  378.             // 等待主机数据发送完毕
  379.         }
  380.         while (spi_i2s_flag_get(LCD_SPIx, SPI_I2S_RDBF_FLAG) == RESET && spi_i2s_flag_get(LCD_SPIx, SPI_I2S_BF_FLAG) == SET) // Rx dt buf满 清零,说明还有数据未发送
  381.         {
  382.             // 等待主机数据接收完毕
  383.         }
  384.         spi_i2s_data_receive(LCD_SPIx);
  385.         // SPI改为16bit模式
  386.         spi_frame_bit_num_set(LCD_SPIx, SPI_FRAME_16BIT);
  387.     }
  388.     LCD_SPI_write_bus(data_half);

  389. }

  390. /******************************************************************************
  391.      函数说明:LCD写入命令
  392.     入口数据:data 写入的命令
  393.     返回值:  无
  394. ******************************************************************************/
  395. void LCD_write_cmd(uint8_t data_byte)
  396. {
  397.     #if (LCD_HARDWARE_SPI_TX_DMA_USE_INT == 1)
  398.     // 在使用 SPI TX DMA 中断的情况下,如果此时DMA还在搬运,那么应该等DMA中断服务程序结束,并且SPI当前帧发送结束后再把DC拉低,否则将导致cmd数据和data数据错位,屏幕卡死
  399.     if (LCD_SPI_TX_DMAx_CHy->ctrl_bit.chen == 1)
  400.     {
  401.     __WFI();
  402.     }
  403.     // DMA搬运完成,但此时SPI还在发送
  404.     while (spi_i2s_flag_get(LCD_SPIx, SPI_I2S_TDBE_FLAG) == RESET && spi_i2s_flag_get(LCD_SPIx, SPI_I2S_BF_FLAG) == SET) // Tx dt buf空 清零,说明还有数据在传输
  405.     {
  406.         // 等待主机数据发送完毕
  407.     }
  408.     while (spi_i2s_flag_get(LCD_SPIx, SPI_I2S_RDBF_FLAG) == RESET && spi_i2s_flag_get(LCD_SPIx, SPI_I2S_BF_FLAG) == SET) // Rx dt buf满 清零,说明还有数据未发送
  409.     {
  410.         // 等待主机数据接收完毕
  411.     }
  412.     spi_i2s_data_receive(LCD_SPIx);
  413.     #endif
  414.     LCD_DC_CLR(); //写命令
  415.     LCD_write_byte(data_byte);
  416.     LCD_DC_SET(); //写数据
  417. }


  418. /**
  419.   * @brief  设置区域的左上角(起始)和右下角(结束)
  420.   * @param  x1 : 列起始坐标
  421.   * @param  y1 : 行起始坐标
  422.   * @param  x2 : 列结束坐标
  423.   * @param  y2 : 行结束坐标
  424.   * @retval none
  425.   */
  426. void LCD_addr_set(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
  427. {
  428.     // 列地址设置
  429.     LCD_write_cmd(0x2a);
  430.     LCD_write_half(x1);
  431.     LCD_write_half(x2);
  432.     // 行地址设置
  433.     LCD_write_cmd(0x2b);
  434.     LCD_write_half(y1);
  435.     LCD_write_half(y2);
  436.     // 准备接收数据
  437.     LCD_write_cmd(0x2c);
  438. }

  439. /******************************************************************************
  440.      函数说明:LCD初始化函数
  441.     入口数据:无
  442.     返回值:  无
  443. ******************************************************************************/
  444. void LCD_init(void)
  445. {
  446. #if CHIP_USE_ST7789 == 0 //初始化ILI9341
  447.     {
  448.         LCD_RES_CLR(); //复位
  449.         delay_ms(100);
  450.         LCD_RES_SET();
  451.         delay_ms(100);

  452.         LCD_BLK_SET(); //打开背光
  453.         delay_ms(100);

  454.         //************* Start Initial Sequence **********//
  455.         LCD_write_cmd(0x11); // Sleep out
  456.         delay_ms(120);       // Delay 120ms
  457.         //************* Start Initial Sequence **********//
  458.         LCD_write_cmd(0xCF);
  459.         LCD_write_byte(0x00);
  460.         LCD_write_byte(0xC1);
  461.         LCD_write_byte(0X30);
  462.         LCD_write_cmd(0xED);
  463.         LCD_write_byte(0x64);
  464.         LCD_write_byte(0x03);
  465.         LCD_write_byte(0X12);
  466.         LCD_write_byte(0X81);
  467.         LCD_write_cmd(0xE8);
  468.         LCD_write_byte(0x85);
  469.         LCD_write_byte(0x00);
  470.         LCD_write_byte(0x79);
  471.         LCD_write_cmd(0xCB);
  472.         LCD_write_byte(0x39);
  473.         LCD_write_byte(0x2C);
  474.         LCD_write_byte(0x00);
  475.         LCD_write_byte(0x34);
  476.         LCD_write_byte(0x02);
  477.         LCD_write_cmd(0xF7);
  478.         LCD_write_byte(0x20);
  479.         LCD_write_cmd(0xEA);
  480.         LCD_write_byte(0x00);
  481.         LCD_write_byte(0x00);
  482.         LCD_write_cmd(0xC0);  // Power control
  483.         LCD_write_byte(0x1D); // VRH[5:0]
  484.         LCD_write_cmd(0xC1);  // Power control
  485.         LCD_write_byte(0x12); // SAP[2:0];BT[3:0]
  486.         LCD_write_cmd(0xC5);  // VCM control
  487.         LCD_write_byte(0x33);
  488.         LCD_write_byte(0x3F);
  489.         LCD_write_cmd(0xC7); // VCM control
  490.         LCD_write_byte(0x92);
  491.         LCD_write_cmd(0x3A); // Memory Access Control
  492.         LCD_write_byte(0x55);
  493.         LCD_write_cmd(0x36); // Memory Access Control
  494.         if (USE_HORIZONTAL == 0)
  495.             LCD_write_byte(0x08);
  496.         else if (USE_HORIZONTAL == 1)
  497.             LCD_write_byte(0xC8);
  498.         else if (USE_HORIZONTAL == 2)
  499.             LCD_write_byte(0x78);
  500.         else
  501.             LCD_write_byte(0xA8);
  502.         LCD_write_cmd(0xB1);
  503.         LCD_write_byte(0x00);
  504.         LCD_write_byte(0x12);
  505.         LCD_write_cmd(0xB6); // Display Function Control
  506.         LCD_write_byte(0x0A);
  507.         LCD_write_byte(0xA2);

  508.         LCD_write_cmd(0x44);
  509.         LCD_write_byte(0x02);

  510.         LCD_write_cmd(0xF2); // 3Gamma Function Disable
  511.         LCD_write_byte(0x00);
  512.         LCD_write_cmd(0x26); // Gamma curve selected
  513.         LCD_write_byte(0x01);
  514.         LCD_write_cmd(0xE0); // Set Gamma
  515.         LCD_write_byte(0x0F);
  516.         LCD_write_byte(0x22);
  517.         LCD_write_byte(0x1C);
  518.         LCD_write_byte(0x1B);
  519.         LCD_write_byte(0x08);
  520.         LCD_write_byte(0x0F);
  521.         LCD_write_byte(0x48);
  522.         LCD_write_byte(0xB8);
  523.         LCD_write_byte(0x34);
  524.         LCD_write_byte(0x05);
  525.         LCD_write_byte(0x0C);
  526.         LCD_write_byte(0x09);
  527.         LCD_write_byte(0x0F);
  528.         LCD_write_byte(0x07);
  529.         LCD_write_byte(0x00);
  530.         LCD_write_cmd(0XE1); // Set Gamma
  531.         LCD_write_byte(0x00);
  532.         LCD_write_byte(0x23);
  533.         LCD_write_byte(0x24);
  534.         LCD_write_byte(0x07);
  535.         LCD_write_byte(0x10);
  536.         LCD_write_byte(0x07);
  537.         LCD_write_byte(0x38);
  538.         LCD_write_byte(0x47);
  539.         LCD_write_byte(0x4B);
  540.         LCD_write_byte(0x0A);
  541.         LCD_write_byte(0x13);
  542.         LCD_write_byte(0x06);
  543.         LCD_write_byte(0x30);
  544.         LCD_write_byte(0x38);
  545.         LCD_write_byte(0x0F);
  546.         LCD_write_cmd(0x29); // Display on
  547.     }
  548. #else //初始化ST7789
  549.     {
  550.         LCD_RES_CLR(); //复位
  551.         delay_ms(100);
  552.         LCD_RES_SET();
  553.         delay_ms(100);
  554.         LCD_BLK_SET(); //打开背光
  555.         delay_ms(500);
  556.         LCD_write_cmd(0x11);
  557.         delay_ms(120); // Delay 120ms
  558.         //************* Start Initial Sequence **********//
  559.         //------------------------------display and color format setting--------------------------------//

  560.         LCD_write_cmd(0X36); // Memory Access Control
  561.         if (USE_HORIZONTAL == 0)
  562.             LCD_write_byte(0x00);
  563.         else if (USE_HORIZONTAL == 1)
  564.             LCD_write_byte(0xC0);
  565.         else if (USE_HORIZONTAL == 2)
  566.             LCD_write_byte(0x70);
  567.         else
  568.             LCD_write_byte(0xA0);
  569.         LCD_write_cmd(0X3A);
  570.         LCD_write_byte(0X05);
  571.         //--------------------------------ST7789S Frame rate setting-------------------------

  572.         LCD_write_cmd(0xb2);
  573.         LCD_write_byte(0x0c);
  574.         LCD_write_byte(0x0c);
  575.         LCD_write_byte(0x00);
  576.         LCD_write_byte(0x33);
  577.         LCD_write_byte(0x33);
  578.         LCD_write_cmd(0xb7);
  579.         LCD_write_byte(0x35);
  580.         //---------------------------------ST7789S Power setting-----------------------------

  581.         LCD_write_cmd(0xbb);
  582.         LCD_write_byte(0x35);
  583.         LCD_write_cmd(0xc0);
  584.         LCD_write_byte(0x2c);
  585.         LCD_write_cmd(0xc2);
  586.         LCD_write_byte(0x01);
  587.         LCD_write_cmd(0xc3);
  588.         LCD_write_byte(0x13);
  589.         LCD_write_cmd(0xc4);
  590.         LCD_write_byte(0x20);
  591.         LCD_write_cmd(0xc6);
  592.         LCD_write_byte(0x0f);
  593.         LCD_write_cmd(0xca);
  594.         LCD_write_byte(0x0f);
  595.         LCD_write_cmd(0xc8);
  596.         LCD_write_byte(0x08);
  597.         LCD_write_cmd(0x55);
  598.         LCD_write_byte(0x90);
  599.         LCD_write_cmd(0xd0);
  600.         LCD_write_byte(0xa4);
  601.         LCD_write_byte(0xa1);
  602.         //--------------------------------ST7789S gamma setting------------------------------
  603.         LCD_write_cmd(0xe0);
  604.         LCD_write_byte(0xd0);
  605.         LCD_write_byte(0x00);
  606.         LCD_write_byte(0x06);
  607.         LCD_write_byte(0x09);
  608.         LCD_write_byte(0x0b);
  609.         LCD_write_byte(0x2a);
  610.         LCD_write_byte(0x3c);
  611.         LCD_write_byte(0x55);
  612.         LCD_write_byte(0x4b);
  613.         LCD_write_byte(0x08);
  614.         LCD_write_byte(0x16);
  615.         LCD_write_byte(0x14);
  616.         LCD_write_byte(0x19);
  617.         LCD_write_byte(0x20);
  618.         LCD_write_cmd(0xe1);
  619.         LCD_write_byte(0xd0);
  620.         LCD_write_byte(0x00);
  621.         LCD_write_byte(0x06);
  622.         LCD_write_byte(0x09);
  623.         LCD_write_byte(0x0b);
  624.         LCD_write_byte(0x29);
  625.         LCD_write_byte(0x36);
  626.         LCD_write_byte(0x54);
  627.         LCD_write_byte(0x4b);
  628.         LCD_write_byte(0x0d);
  629.         LCD_write_byte(0x16);
  630.         LCD_write_byte(0x14);
  631.         LCD_write_byte(0x21);
  632.         LCD_write_byte(0x20);
  633.         LCD_write_cmd(0x29);
  634.     }
  635. #endif
  636. }
  1. /* Define to prevent recursive inclusion -------------------------------------*/
  2. #ifndef __ST7789_H
  3. #define __ST7789_H

  4. #ifdef __cplusplus
  5. extern "C" {
  6. #endif

  7. /* includes ------------------------------------------------------------------*/
  8. #include "at32f435_437.h"

  9. #include "systick.h"

  10. /* micro and typedef ---------------------------------------------------------*/

  11. #define USE_HORIZONTAL  0 //设置横屏或者竖屏显示 0或1为竖屏 2或3为横屏
  12. #define CHIP_USE_ST7789 1 //设置芯片初始化 0为ILI9341  1为ST7789

  13. #if USE_HORIZONTAL == 0 || USE_HORIZONTAL == 1
  14. #define LCD_W 240
  15. #define LCD_H 320
  16. #else
  17. #define LCD_W 320
  18. #define LCD_H 240
  19. #endif

  20. //-----------------LCD端口定义----------------

  21. /* 请按照以下逻辑修改宏定义:
  22.     1,使用硬件SPI?否则使用软件模拟时序。
  23.         若是,下一步;否则将配置为和触摸IC共用一个软件SPI。
  24.     2,判断触摸IC是否使用硬件SPI?
  25.         若是,下一步;否则LCD使用硬件SPI(本文件负责配置)而触摸IC使用软件SPI(不在本文件中配置)。
  26.     3,和触摸IC共用同一个SPI?否则使用两个硬件SPI。
  27.         若是,请指定SPIx,SPIx将设置成全双工模式,软件管理CS,触摸IC也使用此SPI,并下一步;
  28.         否则,请指定两个完全独立的SPI端口分别驱动LCD和触摸IC,硬件管理CS,下一步。
  29.     4,使用DMA?

  30. */
  31. #define LCD_USE_HARDWARE_SPI     1
  32. #define XPT2046_USE_HARDWARE_SPI 1

  33. #if LCD_USE_HARDWARE_SPI == 1
  34. #include "at32f435_437_spi.h"

  35. #if LCD_USE_HARDWARE_SPI == 1 && XPT2046_USE_HARDWARE_SPI == 1
  36. #define LCD_SHARE_SPI_WITH_TOUCH_IC 1 // 见 https://blog.csdn.net/m0_46882426/article/details/127568586?spm=1001.2014.3001.5501 ,共用SPI并软件管理CS是最优解,所以不要改动此宏
  37. #endif

  38. #define LCD_HARDWARE_SPI_USE_DMA 1
  39. #define LCD_HARDWARE_SPI_TX_DMA_USE_INT 1

  40. // SPI 通信速度选择
  41. #define LCD_SPI_USE_SPEED_LEVEL 1

  42. // SPI 端口选择
  43. #if LCD_SHARE_SPI_WITH_TOUCH_IC == 1
  44. // 共用 LCD_SHARE_SPIx_NUM (该宏仅表示SPI序号),且使用软件管理CS
  45. #define LCD_SHARE_SPIx_NUM   2
  46. #define LCD_USE_SPIx_NUM     LCD_SHARE_SPIx_NUM
  47. #define XPT2046_USE_SPIx_NUM LCD_SHARE_SPIx_NUM
  48. #else
  49. // 使用独立的两个SPI,且使用硬件管理CS
  50. #define LCD_USE_SPIx_NUM     1
  51. #define XPT2046_USE_SPIx_NUM 2
  52. #endif

  53. // 非SPI控制 GPIO定义

  54. #define LCD_GPIOPORT     GPIOA
  55. #define LCD_GPIOPORT_CRM CRM_GPIOA_PERIPH_CLOCK

  56. #define LCD_PINS_RST     GPIO_PINS_0
  57. #define LCD_PINS_DC      GPIO_PINS_1
  58. #define LCD_PINS_BLK     GPIO_PINS_8
  59. #define LCD_PINS_CS      GPIO_PINS_4

  60. // DMA 控制器定义
  61. #if LCD_HARDWARE_SPI_USE_DMA == 1
  62. #include "at32f435_437_dma.h"
  63. #define LCD_SPI_TX_DMAx                DMA2
  64. #define LCD_SPI_TX_DMAx_CRM_CLOCK      CRM_DMA2_PERIPH_CLOCK
  65. #define LCD_SPI_TX_DMAx_CHy            DMA2_CHANNEL1
  66. #define LCD_SPI_TX_DMAxMUX_CHy         DMA2MUX_CHANNEL1
  67. #define LCD_SPI_TX_DMAMUX_REQ_ID       DMAMUX_DMAREQ_ID_SPI2_TX
  68. #define LCD_SPI_TX_DMAx_CHy_IRQn       DMA2_Channel1_IRQn
  69. #define LCD_SPI_TX_DMAx_CHy_IRQHandler DMA2_Channel1_IRQHandler
  70. #define LCD_SPI_TX_DMAx_FDTy_FLAG      DMA2_FDT1_FLAG

  71. #define LCD_SPI_RX_DMAx_CHy            DMA2_CHANNEL2
  72. #define LCD_SPI_RX_DMAxMUX_CHy         DMA2MUX_CHANNEL2
  73. #define LCD_SPI_RX_DMAMUX_REQ_ID       DMAMUX_DMAREQ_ID_SPI2_RX
  74. #define LCD_SPI_RX_DMAx_CHy_IRQn       DMA2_Channel2_IRQn
  75. #define LCD_SPI_RX_DMAx_CHy_IRQHandler DMA2_Channel2_IRQHandler
  76. #define LCD_SPI_RX_DMAx_FDTy_FLAG      DMA2_FDT2_FLAG

  77. // 定义 SPI 数据发送缓冲区长度。LCD_SPI_TX_DMAx_CHy 负责将该缓冲区内的数据搬运至 LCD_SPIx
  78. #define LCD_SPI_TX_BUFFER_SIZE 65535 // 设置宽度为半字后,DMA一次最多连续搬运65545个半字
  79. #define LCD_SPI_RX_BUFFER_SIZE 1024/2
  80. #endif

  81. // SPI 控制端口自动定义
  82. #if LCD_USE_SPIx_NUM == 1 // DO NOT MODIFY!

  83. #define LCD_SPIx SPI1
  84. // SPI1 位于 GPIOA 的 MUX5
  85. #define LCD_SPIx_CRM            CRM_SPI1_PERIPH_CLOCK

  86. #define LCD_SPIx_GPIO           GPIOA
  87. #define LCD_SPIx_GPIO_CRM       CRM_GPIOA_PERIPH_CLOCK
  88. #define LCD_SPIx_MUX_SEL        GPIO_MUX_5

  89. #define LCD_SPIx_CS_GPIO_PIN    GPIO_PINS_4
  90. #define LCD_SPIx_SCLK_GPIO_PIN  GPIO_PINS_5
  91. #define LCD_SPIx_MISO_GPIO_PIN  GPIO_PINS_6
  92. #define LCD_SPIx_MOSI_GPIO_PIN  GPIO_PINS_7

  93. #define LCD_SPIx_CS_SOURCE      GPIO_PINS_SOURCE4
  94. #define LCD_SPIx_SCLK_SOURCE    GPIO_PINS_SOURCE5
  95. #define LCD_SPIx_MISO_SOURCE    GPIO_PINS_SOURCE6
  96. #define LCD_SPIx_MOSI_SOURCE    GPIO_PINS_SOURCE7

  97. #define LCD_SPIx_EXT_IQRn       SPI1_IRQn
  98. #define LCD_SPIx_EXT_IQRHandler SPI1_IRQHandler

  99. #elif LCD_USE_SPIx_NUM == 2 // DO NOT MODIFY!

  100. #define LCD_SPIx               SPI2
  101. // SPI2 位于 GPIOA 的 MUX5
  102. #define LCD_SPIx_CRM           CRM_SPI2_PERIPH_CLOCK

  103. #define LCD_SPIx_GPIO          GPIOA
  104. #define LCD_SPIx_GPIO_CRM      CRM_GPIOA_PERIPH_CLOCK
  105. #define LCD_SPIx_MUX_SEL       GPIO_MUX_5

  106. #define LCD_SPIx_SCLK_GPIO_PIN GPIO_PINS_9
  107. #define LCD_SPIx_MOSI_GPIO_PIN GPIO_PINS_10
  108. #define LCD_SPIx_CS_GPIO_PIN   GPIO_PINS_11
  109. #define LCD_SPIx_MISO_GPIO_PIN GPIO_PINS_12

  110. #define LCD_SPIx_SCLK_SOURCE   GPIO_PINS_SOURCE9
  111. #define LCD_SPIx_MOSI_SOURCE   GPIO_PINS_SOURCE10
  112. #define LCD_SPIx_CS_SOURCE     GPIO_PINS_SOURCE11
  113. #define LCD_SPIx_MISO_SOURCE   GPIO_PINS_SOURCE12

  114. #define LCD_SPIx_EXT_IQRn      SPI2_I2S2EXT_IRQn
  115. #else
  116. #error the specified SPI (Macro "LCD_SPIx") is unknown!
  117. #endif

  118. #if LCD_SHARE_SPI_WITH_TOUCH_IC == 1
  119. #define XPT2046_SPIx LCD_SPIx
  120. #endif

  121. #else

  122. #define LCD_GPIOPORT     GPIOA
  123. #define LCD_GPIOPORT_CRM CRM_GPIOA_PERIPH_CLOCK

  124. #define LCD_PINS_SCLK    GPIO_PINS_5
  125. #define LCD_PINS_MOSI    GPIO_PINS_7
  126. #define LCD_PINS_RST     GPIO_PINS_0
  127. #define LCD_PINS_DC      GPIO_PINS_1
  128. #define LCD_PINS_BLK     GPIO_PINS_8
  129. #define LCD_PINS_CS      GPIO_PINS_4

  130. #define LCD_SCLK_CLR()   gpio_bits_reset(LCD_GPIOPORT, LCD_PINS_SCLK)
  131. #define LCD_MOSI_CLR()   gpio_bits_reset(LCD_GPIOPORT, LCD_PINS_MOSI)
  132. #define LCD_CS_CLR()     gpio_bits_reset(LCD_GPIOPORT, LCD_PINS_CS)

  133. #define LCD_SCLK_SET()   gpio_bits_set(LCD_GPIOPORT, LCD_PINS_SCLK)
  134. #define LCD_MOSI_SET()   gpio_bits_set(LCD_GPIOPORT, LCD_PINS_MOSI)
  135. #define LCD_CS_SET()     gpio_bits_set(LCD_GPIOPORT, LCD_PINS_CS)

  136. #endif

  137. #define LCD_RES_CLR() gpio_bits_reset(LCD_GPIOPORT, LCD_PINS_RST)
  138. #define LCD_DC_CLR()  gpio_bits_reset(LCD_GPIOPORT, LCD_PINS_DC)
  139. #define LCD_BLK_CLR() gpio_bits_reset(LCD_GPIOPORT, LCD_PINS_BLK)

  140. #define LCD_RES_SET() gpio_bits_set(LCD_GPIOPORT, LCD_PINS_RST)
  141. #define LCD_DC_SET()  gpio_bits_set(LCD_GPIOPORT, LCD_PINS_DC)
  142. #define LCD_BLK_SET() gpio_bits_set(LCD_GPIOPORT, LCD_PINS_BLK)

  143. #if LCD_SHARE_SPI_WITH_TOUCH_IC == 1
  144. #define LCD_CS_CLR() gpio_bits_reset(LCD_GPIOPORT, LCD_PINS_CS)
  145. #define LCD_CS_SET() gpio_bits_set(LCD_GPIOPORT, LCD_PINS_CS)
  146. #else
  147. // LCD_SPIx 的硬件管理CS将在 ST7789.c 中配置
  148. // XPT2046_SPIx 的硬件管理CS将在 XPT2046.c 中配置
  149. #endif

  150. #if LCD_SPI_USE_SPEED_LEVEL == 1
  151. #define LCD_SPI_SPEED SPI_MCLK_DIV_8 // 288M / 8 = 36M
  152. #elif LCD_SPI_USE_SPEED_LEVEL == 2
  153. #define LCD_SPI_SPEED SPI_MCLK_DIV_16 // 288M / 16 = 18M
  154. #elif LCD_SPI_USE_SPEED_LEVEL == 3
  155. #define LCD_SPI_SPEED SPI_MCLK_DIV_32 // 288M / 32 = 9M
  156. #endif

  157. /* function ------------------------------------------------------------------*/

  158. void LCD_GPIO_init(void); // GPIO初始化
  159. void LCD_SPIx_init(void); // SPI初始化
  160. #if (LCD_HARDWARE_SPI_USE_DMA == 1)
  161. void LCD_SPI_data_send_use_DMA(const void *buf, uint32_t size);
  162. #endif
  163. void LCD_write_half(uint16_t data);                                    
  164. void LCD_addr_set(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
  165. void LCD_init(void);                                                   
  166. void LCD_fill_color(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color);

  167. #ifdef __cplusplus
  168. }
  169. #endif

  170. #endif
  1. /**
  2. * [url=home.php?mod=space&uid=288409]@file[/url] lv_port_disp_templ.c
  3. *
  4. */
  5. /*Copy this file as "lv_port_disp.c" and set this value to "1" to enable content*/
  6. #if 1

  7. /*********************
  8. *      INCLUDES
  9. *********************/
  10. #include "lv_port_disp.h"
  11. #include <stdbool.h>
  12. #include "LCD/ST7789.h"

  13. /*********************
  14. *      DEFINES
  15. *********************/
  16. #ifndef MY_DISP_HOR_RES
  17.     #define MY_DISP_HOR_RES    240
  18. #endif

  19. #ifndef MY_DISP_VER_RES
  20.     #define MY_DISP_VER_RES    320
  21. #endif

  22. /*-----------------------------
  23. * Create a buffer for drawing
  24. *----------------------------*/
  25.     /**
  26.      * LVGL requires a buffer where it internally draws the widgets.
  27.      * Later this buffer will passed to your display driver's `flush_cb` to copy its content to your display.
  28.      * The buffer has to be greater than 1 display row
  29.      *
  30.      * There are 3 buffering configurations:
  31.      * 1. Create ONE buffer:
  32.      *      LVGL will draw the display's content here and writes it to your display
  33.      *
  34.      * 2. Create TWO buffer:
  35.      *      LVGL will draw the display's content to a buffer and writes it your display.
  36.      *      You should use DMA to write the buffer's content to the display.
  37.      *      It will enable LVGL to draw the next part of the screen to the other buffer while
  38.      *      the data is being sent form the first buffer. It makes rendering and flushing parallel.
  39.      *
  40.      * 3. Double buffering
  41.      *      Set 2 screens sized buffers and set disp_drv.full_refresh = 1.
  42.      *      This way LVGL will always provide the whole rendered screen in `flush_cb`
  43.      *      and you only need to change the frame buffer's address.
  44.      */
  45. #define BUFFER_METHOD 2



  46. /**********************
  47. *      TYPEDEFS
  48. **********************/
  49. /**********************
  50. *  STATIC PROTOTYPES
  51. **********************/
  52. static void disp_init(void);

  53. static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p);
  54. //static void gpu_fill(lv_disp_drv_t * disp_drv, lv_color_t * dest_buf, lv_coord_t dest_width,
  55. //        const lv_area_t * fill_area, lv_color_t color);

  56. /**********************
  57. *  STATIC VARIABLES
  58. **********************/
  59. /**********************
  60. *      MACROS
  61. **********************/
  62. /**********************
  63. *   GLOBAL FUNCTIONS
  64. **********************/
  65. void lv_port_disp_init(void)
  66. {
  67.     /*-------------------------
  68.      * Initialize your display
  69.      * -----------------------*/
  70.     disp_init();

  71.     /*-----------------------------
  72.      * Create a buffer for drawing
  73.      *----------------------------*/
  74.     #if (BUFFER_METHOD == 1)
  75.     /* Example for 1) */
  76.     static lv_disp_draw_buf_t draw_buf_dsc_1;
  77.     static lv_color_t buf_1[MY_DISP_HOR_RES * 10];                          /*A buffer for 10 rows*/
  78.     lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * 10);   /*Initialize the display buffer*/
  79.     #elif (BUFFER_METHOD == 2)
  80.     /* Example for 2) */
  81.     static lv_disp_draw_buf_t draw_buf_dsc_2;
  82.     static lv_color_t buf_2_1[MY_DISP_HOR_RES * 10];                        /*A buffer for 10 rows*/
  83.     static lv_color_t buf_2_2[MY_DISP_HOR_RES * 10];                        /*An other buffer for 10 rows*/
  84.     lv_disp_draw_buf_init(&draw_buf_dsc_2, buf_2_1, buf_2_2, MY_DISP_HOR_RES * 10);   /*Initialize the display buffer*/
  85.     #elif (BUFFER_METHOD == 3)
  86.     /* Example for 3) also set disp_drv.full_refresh = 1 below*/
  87.     static lv_disp_draw_buf_t draw_buf_dsc_3;
  88.     static lv_color_t buf_3_1[MY_DISP_HOR_RES * MY_DISP_VER_RES];            /*A screen sized buffer*/
  89.     static lv_color_t buf_3_2[MY_DISP_HOR_RES * MY_DISP_VER_RES];            /*Another screen sized buffer*/
  90.     lv_disp_draw_buf_init(&draw_buf_dsc_3, buf_3_1, buf_3_2,
  91.                           MY_DISP_VER_RES * MY_DISP_HOR_RES);   /*Initialize the display buffer*/
  92.     #else
  93.         #error "the specified macro 'BUFFER_METHOD' is unknown!"
  94.     #endif


  95.     /*-----------------------------------
  96.      * Register the display in LVGL
  97.      *----------------------------------*/
  98.     static lv_disp_drv_t disp_drv;                         /*Descriptor of a display driver*/
  99.     lv_disp_drv_init(&disp_drv);                    /*Basic initialization*/

  100.     /*Set up the functions to access to your display*/

  101.     /*Set the resolution of the display*/
  102.     disp_drv.hor_res = MY_DISP_HOR_RES;
  103.     disp_drv.ver_res = MY_DISP_VER_RES;

  104.     /*Used to copy the buffer's content to the display*/
  105.     disp_drv.flush_cb = disp_flush;
  106.     /*Set a display buffer*/
  107.     #if (BUFFER_METHOD == 1)
  108.     disp_drv.draw_buf = &draw_buf_dsc_1;
  109.     #elif (BUFFER_METHOD == 2)
  110.     disp_drv.draw_buf = &draw_buf_dsc_2;
  111.     #else
  112.     disp_drv.draw_buf = &draw_buf_dsc_3;
  113.     #endif
  114.     #if (BUFFER_METHOD == 3)
  115.     /*Required for Example 3)*/
  116.     disp_drv.full_refresh = 1;
  117.     #endif
  118.     /* Fill a memory array with a color if you have GPU.
  119.      * Note that, in lv_conf.h you can enable GPUs that has built-in support in LVGL.
  120.      * But if you have a different GPU you can use with this callback.*/
  121.     //disp_drv.gpu_fill_cb = gpu_fill;
  122.     /*Finally register the driver*/
  123.     lv_disp_drv_register(&disp_drv);
  124. }
  125. /**********************
  126. *   STATIC FUNCTIONS
  127. **********************/
  128. /*Initialize your display and the required peripherals.*/
  129. static void disp_init(void)
  130. {
  131.     LCD_GPIO_init();
  132.     #if LCD_USE_HARDWARE_SPI == 1
  133.     LCD_SPIx_init();
  134.     #endif
  135.     // 注意在此之前确保已经初始化 GPIO 和 SPI
  136.     LCD_init();
  137. }
  138. volatile bool disp_flush_enabled = true;
  139. /* Enable updating the screen (the flushing process) when disp_flush() is called by LVGL
  140. */
  141. void disp_enable_update(void)
  142. {
  143.     disp_flush_enabled = true;
  144. }
  145. /* Disable updating the screen (the flushing process) when disp_flush() is called by LVGL
  146. */
  147. void disp_disable_update(void)
  148. {
  149.     disp_flush_enabled = false;
  150. }
  151. /*Flush the content of the internal buffer the specific area on the display
  152. *You can use DMA or any hardware acceleration to do this operation in the background but
  153. *'lv_disp_flush_ready()' has to be called when finished.*/
  154. static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
  155. {
  156.     if(disp_flush_enabled)
  157.     {
  158.         /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/
  159.         // int32_t x;
  160.         // int32_t y;
  161.         // for(y = area->y1; y <= area->y2; y++) {
  162.         //     for(x = area->x1; x <= area->x2; x++) {
  163.         //         /*Put a pixel to the display. For example:*/
  164.         //         /*put_px(x, y, *color_p)*/
  165.         //         color_p++;
  166.         //     }
  167.         // }
  168.         // 如果采用写点函数来做,刷屏会很慢,所以推荐的方式如下:
  169.         uint16_t  x1, y1, x2, y2;
  170.         uint16_t total_half;
  171.         x1 = area->x1;
  172.         y1 = area->y1;
  173.         x2 = area->x2;
  174.         y2 = area->y2;
  175.         
  176.         total_half = (x2 - x1 + 1) * (y2 - y1 + 1);
  177.         LCD_addr_set(x1, y1, x2, y2);
  178.         // st7789_cfg_dcx_set();
  179.         // st7789_cfg_spi_write((uint8_t*)color_p, total_half );
  180.         LCD_SPI_data_send_use_DMA((uint16_t *) color_p, total_half);
  181.     }
  182.     /*IMPORTANT!!!
  183.      *Inform the graphics library that you are ready with the flushing*/
  184.     lv_disp_flush_ready(disp_drv);
  185. }
  186. /*OPTIONAL: GPU INTERFACE*/
  187. /*If your MCU has hardware accelerator (GPU) then you can use it to fill a memory with a color*/
  188. //static void gpu_fill(lv_disp_drv_t * disp_drv, lv_color_t * dest_buf, lv_coord_t dest_width,
  189. //                    const lv_area_t * fill_area, lv_color_t color)
  190. //{
  191. //    /*It's an example code which should be done by your GPU*/
  192. //    int32_t x, y;
  193. //    dest_buf += dest_width * fill_area->y1; /*Go to the first line*/
  194. //
  195. //    for(y = fill_area->y1; y <= fill_area->y2; y++) {
  196. //        for(x = fill_area->x1; x <= fill_area->x2; x++) {
  197. //            dest_buf[x] = color;
  198. //        }
  199. //        dest_buf+=dest_width;    /*Go to the next line*/
  200. //    }
  201. //}


  202. #else /*Enable this file at the top*/

  203. /*This dummy typedef exists purely to silence -Wpedantic.*/
  204. typedef int keep_pedantic_happy;
  205. #endif
  1. #include "at32f435_437_clock.h"

  2. #include "systick.h"
  3. #include "usart.h"
  4. #include "LCD/XPT2046.h"

  5. #include "lvgl.h"
  6. #include "examples/porting/lv_port_disp.h"
  7. #include "examples/porting/lv_port_indev.h"
  8. #include "examples/lv_examples.h"

  9. #define LVGL_TICK         1

  10. static void lvgl_init( void )
  11. {
  12.     lv_init();
  13.     lv_port_disp_init();        // 显示器初始化
  14.     lv_port_indev_init();       // 输入设备初始化
  15.     // lv_port_fs_init();          // 文件系统设备初始化
  16. }


  17. /**
  18. * @brief  main function.
  19. * @param  none
  20. * @retval none
  21. */
  22. int main()
  23. {

  24.     system_clock_config();
  25.     delay_init();

  26.         // LED init
  27.     gpio_init_type gpio_init_struct;
  28.     crm_periph_clock_enable(CRM_GPIOD_PERIPH_CLOCK, TRUE);
  29.     gpio_default_para_init(&gpio_init_struct);
  30.     gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  31.     gpio_init_struct.gpio_out_type       = GPIO_OUTPUT_PUSH_PULL;
  32.     gpio_init_struct.gpio_mode           = GPIO_MODE_OUTPUT;
  33.     gpio_init_struct.gpio_pins           = GPIO_PINS_2;
  34.     gpio_init_struct.gpio_pull           = GPIO_PULL_NONE;
  35.     gpio_init(GPIOD, &gpio_init_struct);

  36.         // //USART init
  37.     uart_print_init(115200);

  38.     printf("UART initialized successfully!\r\n");
  39.         // =============================> LCD app <=============================   
  40.         lvgl_init();

  41.     lv_example_obj_2();

  42.         while(1) {
  43.                 // 先调用 lv_tick_inc 再调用 lv_timer_handler
  44.                 lv_tick_inc(LVGL_TICK);
  45.                 lv_timer_handler();
  46.                 delay_ms(LVGL_TICK);
  47.         GPIOD->odt ^= GPIO_PINS_2;
  48.         }
  49. }



xiaoyaodz 发表于 2024-3-1 20:40 | 显示全部楼层
硬件SPI(串行外设接口)是一种常用的通信协议,用于微控制器与各种外设(如传感器、显示模块、存储器等)之间的快速数据交换。
bartonalfred 发表于 2024-3-1 21:34 | 显示全部楼层
如果LCD屏幕的驱动芯片是ST7789,那么需要配置与之对应的SPI参数。
jackcat 发表于 2024-3-2 02:07 | 显示全部楼层
设置SPI时钟速率,以确保它能够与LCD屏的要求相匹配。
配置SPI端口的工作模式,包括数据位宽(通常是8位)、时钟极性和相位等。
zerorobert 发表于 2024-3-2 09:56 | 显示全部楼层
需要按照LCD屏幕的数据手册来配置屏幕。 包括设置屏幕的分辨率、颜色深度、背光等。
wwppd 发表于 2024-3-2 17:18 | 显示全部楼层
不同的LCD屏幕可能具有不同的时序要求和控制逻辑
rosemoore 发表于 2024-3-2 19:15 | 显示全部楼层
LCD屏幕与MCU之间的SPI连接正确无误。检查SPI时钟(SCLK)、数据输入(MISO)和数据输出(MOSI)线的连接。同时,确保CS(片选)线的连接和控制正确。
jtracy3 发表于 2024-3-2 22:31 | 显示全部楼层
根据你的LCD屏型号、SPI接口的配置以及你使用的编程语言或框架而有所不同。
mikewalpole 发表于 2024-3-3 09:22 | 显示全部楼层
编写函数用于通过SPI接口发送数据到LCD屏。通常会有专门的函数负责数据打包和解包,以及发送命令和数据到SPI总线。
averyleigh 发表于 2024-3-3 11:25 | 显示全部楼层
在微控制器上配置硬件SPI,包括设置SPI时钟速率、模式(如主从模式、CPOL、CPHA等)和数据位宽(如8位或16位)。
yeates333 发表于 2024-3-3 13:30 | 显示全部楼层
一旦SPI和LCD屏幕都配置好了,你就可以开始发送命令和数据。通常,你会先发送一系列的命令来设置屏幕的模式、画布、颜色等。
mollylawrence 发表于 2024-3-3 15:22 | 显示全部楼层
SPI驱动代码正确地配置了时钟、数据输入和数据输出。检查CS线的控制逻辑,确保在正确的时间激活和释放。
benjaminka 发表于 2024-3-3 17:13 | 显示全部楼层
如果LCD屏幕支持背光调节,还需要编写相应的代码来实现这一功能。
jackcat 发表于 2024-3-3 19:06 | 显示全部楼层
使用硬件SPI将数据发送到LCD屏。这通常包括发送像素数据、命令和参数。你可以使用微控制器的硬件SPI接口来实现这一点。
mikewalpole 发表于 2024-3-3 22:30 | 显示全部楼层
将LCD屏的SPI引脚连接到微控制器的相应引脚。通常,你需要连接SCLK、MOSI、MISO和CS引脚。此外,还需要连接LCD屏的电源引脚(如VCC、GND、VDD等)。
elsaflower 发表于 2024-3-4 11:26 | 显示全部楼层
微控制器的SPI引脚(通常是SCK - 时钟线、MISO - 主设备输入从设备输出、MOSI - 主设备输出从设备输入,以及NSS/CS - 片选信号)正确连接到LCD屏的SPI接口。
wilhelmina2 发表于 2024-3-4 16:00 | 显示全部楼层
配置SPI外设,设置SPI的时钟频率、工作模式(如主/从模式、CPOL/CPHA极性与时钟相位)、数据宽度(8位、16位等)和片选信号的GPIO口。
claretttt 发表于 2024-3-5 09:55 | 显示全部楼层
可以考虑使用DMA(Direct Memory Access)来发送数据,这样可以减轻CPU的负担。
fengm 发表于 2024-3-5 12:12 | 显示全部楼层
需要选择一个支持硬件SPI接口的LCD屏。 LCD屏需要具有专用的SPI引脚,如SCLK、MOSI、MISO和CS。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:苏州澜宭自动化科技嵌入式工程师
简介:本人从事磁编码器研发工作,负责开发2500线增量式磁编码器以及17位、23位绝对值式磁编码器,拥有多年嵌入式开发经验,精通STM32、GD32、N32等多种品牌单片机,熟练使用单片机各种外设。

568

主题

4085

帖子

56

粉丝
快速回复 在线客服 返回列表 返回顶部