[AT32L021] 【AT-START-L021测评】+单片机SPI外设驱动SPI显示屏

[复制链接]
 楼主| suncat0504 发表于 2024-12-8 16:22 | 显示全部楼层 |阅读模式

在调通了软件模拟方式驱动SPI显示屏后,开始着手使用单片机SPI外设驱动SPI显示屏。前贴说过,由于SPI显示屏还有RST、DC、BL引脚,所以不可能完全由单片机的SPI外设来实现显示驱动。另外考虑到CS在显示驱动中的处理,决定这个CS也由软件控制GPIO来实现,这样的话,不需要复用GPIO口给CS了,而是依旧使用普通的GPIO输出模式提供CS信号。另外由于驱动SPI显示屏时,是不需要返回信息的,是单片机单方面发送数据给SPI显示屏,所以MISO这个数据通讯线其实是没什么用的,但不知道不使用它会不会有麻烦,所以实际编程时保留了这个GPIO口的功能复用。以下是在以外设驱动SPI显示屏时 的GPIO口和SPI外设的初始化处理:

  1. // 使用SPI1外设,关联GPIO口:PA4,PA5,PA6,PA7

  2. void TFT_Init_Gpio_SPI(void) {

  3. gpio_init_type gpio_initstructure;

  4. spi_init_type spi_init_struct; // SPI外设

  5. printf("\r\n TFT_Init_Gpio_SPI");

  6. // SPI主机

  7. crm_periph_clock_enable(TFT_PORT_PERIPH_CLOCK, TRUE);

  8. // SPI1主机

  9. gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE5, GPIO_MUX_0);

  10. gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE7, GPIO_MUX_0);

  11. gpio_default_para_init(&gpio_initstructure);

  12. // PA1 - RST, PA2 - SC, PA3 - BL

  13. gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;

  14. gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;

  15. gpio_initstructure.gpio_mode = GPIO_MODE_OUTPUT;

  16. gpio_initstructure.gpio_pins = TFT_RST_PIN | TFT_DC_PIN | TFT_BL_PIN | TFT_CS_PIN ;

  17. gpio_initstructure.gpio_pull = GPIO_PULL_UP;

  18. gpio_init(GPIOA, &gpio_initstructure);

  19. // PA4 ~ PA7 : SPI1外设

  20. /* 主机SCK引脚:PA5 */

  21. gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;

  22. gpio_initstructure.gpio_pull = GPIO_PULL_DOWN;

  23. gpio_initstructure.gpio_mode = GPIO_MODE_MUX;

  24. gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;

  25. gpio_initstructure.gpio_pins = GPIO_PINS_5;

  26. gpio_init(GPIOA, &gpio_initstructure);

  27. gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;

  28. gpio_initstructure.gpio_pull = GPIO_PULL_UP;

  29. gpio_initstructure.gpio_mode = GPIO_MODE_MUX;

  30. gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;

  31. gpio_initstructure.gpio_pins = GPIO_PINS_6;

  32. gpio_init(GPIOA, &gpio_initstructure);

  33. /* 主机mosi引脚:PA7 */

  34. gpio_initstructure.gpio_pull = GPIO_PULL_UP;

  35. gpio_initstructure.gpio_mode = GPIO_MODE_MUX;

  36. gpio_initstructure.gpio_pins = GPIO_PINS_7;

  37. gpio_init(GPIOA, &gpio_initstructure);

  38. // 初始化SPI外设

  39. crm_periph_clock_enable(CRM_SPI1_PERIPH_CLOCK, TRUE);

  40. spi_default_para_init(&spi_init_struct);

  41. spi_init_struct.transmission_mode = SPI_TRANSMIT_FULL_DUPLEX;

  42. spi_init_struct.master_slave_mode = SPI_MODE_MASTER;

  43. spi_init_struct.mclk_freq_division = SPI_MCLK_DIV_2;

  44. spi_init_struct.first_bit_transmission = SPI_FIRST_BIT_MSB;

  45. spi_init_struct.frame_bit_num = SPI_FRAME_8BIT;

  46. spi_init_struct.clock_polarity = SPI_CLOCK_POLARITY_LOW;

  47. spi_init_struct.clock_phase = SPI_CLOCK_PHASE_1EDGE;

  48. spi_init_struct.cs_mode_selection = SPI_CS_SOFTWARE_MODE;

  49. spi_init(SPI1, &spi_init_struct);

  50. spi_enable(SPI1, TRUE);

  51. }

这里需要注意的是SPI外设的工作频率设置,
spi_init_struct.mclk_freq_division = SPI_MCLK_DIV_2;

在这个地方我花了好多时间,最后才发现,分频系数不能太高。在原来的例程中,是设置为16的。我在这个例程基础上的改造,花了大半天时间,都没有获得成功。最后在2分频、3分频时,才显示正常。

显示屏初始化部分的代码:
  1. // 使用SPI1外设,关联GPIO口:PA4,PA5,PA6,PA7
  2. void TFT_Init_Gpio_SPI(void) {
  3.     gpio_init_type gpio_initstructure;
  4.     spi_init_type spi_init_struct;      // SPI外设
  5.    
  6.     printf("\r\n TFT_Init_Gpio_SPI");
  7.    
  8.     // SPI主机
  9.     crm_periph_clock_enable(TFT_PORT_PERIPH_CLOCK, TRUE);

  10.     // SPI1主机
  11.     gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE5, GPIO_MUX_0);
  12.     gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE7, GPIO_MUX_0);

  13.     gpio_default_para_init(&gpio_initstructure);

  14.     // PA1 - RST, PA2 - SC, PA3 - BL
  15.     gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  16.     gpio_initstructure.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
  17.     gpio_initstructure.gpio_mode = GPIO_MODE_OUTPUT;
  18.     gpio_initstructure.gpio_pins = TFT_RST_PIN | TFT_DC_PIN | TFT_BL_PIN | TFT_CS_PIN ;
  19.     gpio_initstructure.gpio_pull = GPIO_PULL_UP;
  20.     gpio_init(GPIOA, &gpio_initstructure);
  21.    
  22.    
  23.     // PA4 ~ PA7 : SPI1外设

  24.     /* 主机SCK引脚:PA5 */
  25.     gpio_initstructure.gpio_out_type       = GPIO_OUTPUT_PUSH_PULL;
  26.     gpio_initstructure.gpio_pull           = GPIO_PULL_DOWN;
  27.     gpio_initstructure.gpio_mode           = GPIO_MODE_MUX;
  28.     gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  29.     gpio_initstructure.gpio_pins           = GPIO_PINS_5;
  30.     gpio_init(GPIOA, &gpio_initstructure);

  31.     gpio_initstructure.gpio_out_type       = GPIO_OUTPUT_PUSH_PULL;
  32.     gpio_initstructure.gpio_pull           = GPIO_PULL_UP;
  33.     gpio_initstructure.gpio_mode           = GPIO_MODE_MUX;
  34.     gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  35.     gpio_initstructure.gpio_pins = GPIO_PINS_6;
  36.     gpio_init(GPIOA, &gpio_initstructure);

  37.     /* 主机mosi引脚:PA7 */
  38.     gpio_initstructure.gpio_pull           = GPIO_PULL_UP;
  39.     gpio_initstructure.gpio_mode           = GPIO_MODE_MUX;
  40.     gpio_initstructure.gpio_pins           = GPIO_PINS_7;
  41.     gpio_init(GPIOA, &gpio_initstructure);

  42.    
  43.     // 初始化SPI外设
  44.     crm_periph_clock_enable(CRM_SPI1_PERIPH_CLOCK, TRUE);
  45.    
  46.     spi_default_para_init(&spi_init_struct);
  47.    
  48.     spi_init_struct.transmission_mode = SPI_TRANSMIT_FULL_DUPLEX;
  49.     spi_init_struct.master_slave_mode = SPI_MODE_MASTER;
  50.     spi_init_struct.mclk_freq_division = SPI_MCLK_DIV_2;
  51.     spi_init_struct.first_bit_transmission = SPI_FIRST_BIT_MSB;
  52.     spi_init_struct.frame_bit_num = SPI_FRAME_8BIT;
  53.     spi_init_struct.clock_polarity = SPI_CLOCK_POLARITY_LOW;
  54.     spi_init_struct.clock_phase = SPI_CLOCK_PHASE_1EDGE;
  55.     spi_init_struct.cs_mode_selection = SPI_CS_SOFTWARE_MODE;

  56.     spi_init(SPI1, &spi_init_struct);


  57.     spi_enable(SPI1, TRUE);
  58.    
  59. }

在原来使用软件模拟方式驱动时,可以用逻辑分析仪获得非常标准的SPI通讯过程,但改成单片机SPI外设后,逻辑分析仪获得的并行就比较不能理解了,如下图所示:

1b3d609f7451be18d35197cf94f3be4e
98e1da0889d26e36e282bfbd313ac148

这是以50MHz采样的结果,由于SPI外设通讯速度很快,SPI通讯信号不正常。上图中是SPI显示屏初始化处理的部分,可以看到与预定的初始化代码已经严重偏离。所以换成100MHz的采样率进行再次测量,在这个采样率下是没办法采集所有通道了,只能显示3个通道。

5a551e6e16a0316790ace839c099404c
初始化部分的波形
59febffc43523529706142f862cd4eaa
这时的波形才是是正常的,能看到0x36,0x00,0x3A,0x05等初始化代码。显示屏上的测试结果与编程预期也是一致的。但注意看SPI的时钟SCLK:
a2f84163d971ea68e2999667f825a982
显然不是均匀的,不知道是不是我的逻辑分析仪精度不够测的不准,还是实际就是这样的。
测试结果:
测试结果.gif
这程序代码:
  1. int main(void) {
  2.     uint32_t cnt=0;
  3.    
  4.     system_clock_config();        // 配置系统时钟
  5.     at32_board_init();            // 板级初始化
  6.     uart_print_init(115200);
  7.    
  8.     delay_us(100);                // 延迟
  9.    
  10.     // 设置工作模式:软件模拟、外设SPI
  11.     lcddev.mode=HARD_FULL;
  12.     //lcddev.mode=SOFT;
  13.    
  14.     TFT_Init();
  15.     TFT_Clear(BLACK);
  16.     TFT_ShowString(0, 0, 16, "Hello 21ic & Artery" , 0);
  17.    
  18.     while(cnt<1000) {
  19.     cnt++;
  20.     TFT_ShowNum(0, 16, cnt, 8 , 16);
  21. }
  22.    
  23.     while(1) {
  24.     //gpio_bits_toggle(TFT_PORT, TFT_SCK_PIN);
  25.     at32_led_toggle(LED4);
  26.     delay_ms(500);
  27.     at32_led_toggle(LED4);
  28.     at32_led_toggle(LED2);
  29.     delay_ms(500);
  30.     at32_led_toggle(LED2);
  31.     at32_led_toggle(LED3);
  32.     delay_ms(500);
  33.     at32_led_toggle(LED3);
  34.     }
  35.    
  36. }

SPI显示屏部分的处理代码与之前软件模拟方式驱动大体一致,区别在于字节数发送部分。
  1. //向液晶屏写一个8位数据
  2. void TFT_SendByte(unsigned char byte) {
  3.         unsigned char counter;
  4.    
  5.     if (lcddev.mode == SOFT) {
  6.     //printf("\r\n soft ... TFT_SendByte = %02x ", byte);
  7.     // 软件模拟方式发送
  8.     for (counter = 0; counter < 8; counter++) {
  9.         gpio_bits_reset(TFT_PORT, TFT_SCK_PIN);         // SCK=0
  10.             
  11.         if ((byte & 0x80) == 0) {
  12.             gpio_bits_reset(TFT_PORT, TFT_MOSI_PIN);    // MOSI=0
  13.         } else {
  14.             gpio_bits_set(TFT_PORT, TFT_MOSI_PIN);      // MOSI=1
  15.         }
  16.             
  17.         byte = byte << 1;
  18.             
  19.         gpio_bits_set(TFT_PORT, TFT_SCK_PIN);           // SCK=1
  20.     }
  21.     gpio_bits_reset(TFT_PORT, TFT_SCK_PIN);             // SCK=0        

  22.     } else {
  23.         //printf("\r\n <<< %02x ", byte);
  24.         // 使用SPI外设发送
  25.         while(spi_i2s_flag_get(SPI1, SPI_I2S_TDBE_FLAG) == RESET);
  26.         spi_i2s_data_transmit(SPI1, byte);
  27.         //printf("\r\n >>> %02x ", byte);

  28.     }
  29. }
使用SPI外设,发送数据之前,需要判断数据寄存器是否空闲,空闲了才能传送书给SPI的数据寄存器。

您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:大连伊飞特信息技术有限公司软件工程师
简介:本人于1993年毕业于大连理工大学。毕业后从事单片机开发工作5年,之后转入软件开发工作至今。

158

主题

4504

帖子

6

粉丝
快速回复 在线客服 返回列表 返回顶部
认证:大连伊飞特信息技术有限公司软件工程师
简介:本人于1993年毕业于大连理工大学。毕业后从事单片机开发工作5年,之后转入软件开发工作至今。

158

主题

4504

帖子

6

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