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

[复制链接]
 楼主| suncat0504 发表于 2024-12-8 18:28 | 显示全部楼层 |阅读模式
本帖最后由 suncat0504 于 2024-12-8 18:31 编辑

在调通了软件模拟方式驱动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. // 初始化
  2. void TFT_Init(void) {
  3.     if (lcddev.mode == SOFT) {
  4.         printf("\r\n soft ... TFT_Init");
  5.         TFT_Init_Gpio();
  6.     } else {
  7.         printf("\r\n hard ... TFT_Init");
  8.         TFT_Init_Gpio_SPI();
  9.     }
  10.    
  11.     // 硬件复位 RST=0 延时 RST=1
  12.     TFT_Reset();
  13.    
  14.     TFT_Send_Cmd(0x36);     //
  15.         TFT_Send_Data(0x00);

  16.         TFT_Send_Cmd(0x3A);     //65k mode
  17.         TFT_Send_Data(0x05);
  18.    
  19.     //-------------ST7789V Frame rate setting-----------//
  20.         TFT_Send_Cmd(0xB2);                //Porch Setting
  21.         TFT_Send_Data(0xCC);
  22.         TFT_Send_Data(0xCC);
  23.         TFT_Send_Data(0x00);
  24.         TFT_Send_Data(0x33);
  25.         TFT_Send_Data(0x33);

  26.         TFT_Send_Cmd(0xB7);            //Gate Control
  27.         TFT_Send_Data(0x35);    //12.2v   -10.43v
  28.        
  29.     //--------------ST7789V Power setting---------------//
  30.         TFT_Send_Cmd(0xBB);     //VCOM
  31.         TFT_Send_Data(0x1A);

  32.         TFT_Send_Cmd(0xC0);     //Power control
  33.         TFT_Send_Data(0x2C);

  34.         TFT_Send_Cmd(0xC2);            //VDV and VRH Command Enable
  35.         TFT_Send_Data(0x01);

  36.         TFT_Send_Cmd(0xC3);            //VRH Set
  37.         TFT_Send_Data(0x0B);    //4.3+( vcom+vcom offset+vdv)

  38.         TFT_Send_Cmd(0xC4);            //VDV Set
  39.         TFT_Send_Data(0x20);    //0v

  40.         TFT_Send_Cmd(0xC6);            //Frame Rate Control in Normal Mode
  41.         TFT_Send_Data(0x0F);    //

  42.         TFT_Send_Cmd(0xD0);     //Power Control 1
  43.         TFT_Send_Data(0xA4);
  44.         TFT_Send_Data(0xA1);

  45.         //---------------ST7789V gamma setting-------------//
  46.         TFT_Send_Cmd(0xE0);     
  47.         TFT_Send_Data(0x00);   
  48.         TFT_Send_Data(0x03);   
  49.         TFT_Send_Data(0x07);   
  50.         TFT_Send_Data(0x08);   
  51.         TFT_Send_Data(0x07);   
  52.         TFT_Send_Data(0x15);   
  53.         TFT_Send_Data(0x2A);   
  54.         TFT_Send_Data(0x44);   
  55.         TFT_Send_Data(0x42);   
  56.         TFT_Send_Data(0x0A);   
  57.         TFT_Send_Data(0x17);   
  58.         TFT_Send_Data(0x18);   
  59.         TFT_Send_Data(0x25);   
  60.         TFT_Send_Data(0x27);   

  61.         TFT_Send_Cmd(0xE1);     
  62.         TFT_Send_Data(0x00);   
  63.         TFT_Send_Data(0x03);   
  64.         TFT_Send_Data(0x08);   
  65.         TFT_Send_Data(0x07);   
  66.         TFT_Send_Data(0x07);   
  67.         TFT_Send_Data(0x23);   
  68.         TFT_Send_Data(0x2A);   
  69.         TFT_Send_Data(0x43);   
  70.         TFT_Send_Data(0x42);   
  71.         TFT_Send_Data(0x09);   
  72.         TFT_Send_Data(0x18);   
  73.         TFT_Send_Data(0x17);   
  74.         TFT_Send_Data(0x25);   
  75.         TFT_Send_Data(0x27);

  76.         TFT_Send_Cmd(0x21);   // 反显
  77.         TFT_Send_Cmd(0x11);   // 退出睡眠模式
  78.         delay_ms(120);
  79.         TFT_Send_Cmd(0x29);   // 开显示
  80.    
  81.         //设置LCD属性参数
  82.         TFT_direction(USE_HORIZONTAL);//设置LCD显示方向
  83.    
  84.     // 点亮背景灯
  85.     gpio_bits_set(TFT_PORT, TFT_BL_PIN);
  86. }



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

860e6a0986eeda1b2d3522aa19b3b14d
749aae8ed49d7d0cd56dcffe6a9ecf1a

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

7b0c12a61746620a415a5d9a3d636187
初始化部分的波形
7e5204daa7bb82ebc1885788ba4a6446
这时的波形才是是正常的,能看到0x36,0x00,0x3A,0x05等初始化代码。显示屏上的测试结果与编程预期也是一致的。但注意看SPI的时钟SCLK:
577af69e539bae8dff625cfa91ac3567
显然不是均匀的,不知道是不是我的逻辑分析仪精度不够测的不准,还是实际就是这样的。
测试结果:
df2a04038ef56aa6eee01433b79c471b
这程序代码:

  1. /**
  2.   * [url=home.php?mod=space&uid=247401]@brief[/url]  main function.
  3.   * @param  none
  4.   * @retval none
  5.   */
  6. int main(void) {
  7.     uint32_t cnt=0;
  8.    
  9.     system_clock_config();        // 配置系统时钟
  10.     at32_board_init();            // 板级初始化
  11.     uart_print_init(115200);
  12.    
  13.     delay_us(100);                // 延迟
  14.    
  15.     // 设置工作模式:软件模拟、外设SPI
  16.     lcddev.mode=HARD_FULL;
  17.     //lcddev.mode=SOFT;
  18.    
  19.     TFT_Init();
  20.     TFT_Clear(BLACK);
  21.     TFT_ShowString(0, 0, 16, "Hello 21ic & Artery" , 0);
  22.    
  23.     while(cnt<1000) {
  24.         cnt++;
  25.         //at32_led_toggle(LED3);
  26.         //delay_ms(500);
  27.         //testSpiInterface();
  28.         TFT_ShowNum(0, 16, cnt, 8 , 16);
  29.         //delay_ms(100);
  30.         
  31.         //gpio_bits_toggle(TFT_PORT, TFT_SCK_PIN);
  32.     }
  33.    
  34.     while(1) {
  35.         //gpio_bits_toggle(TFT_PORT, TFT_SCK_PIN);
  36.         at32_led_toggle(LED4);
  37.         delay_ms(500);
  38.         at32_led_toggle(LED4);
  39.         at32_led_toggle(LED2);
  40.         delay_ms(500);
  41.         at32_led_toggle(LED2);
  42.         at32_led_toggle(LED3);
  43.         delay_ms(500);
  44.         at32_led_toggle(LED3);
  45.     }
  46.    
  47. }


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

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