打印
[AT32L021]

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

[复制链接]
513|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主

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

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

void TFT_Init_Gpio_SPI(void) {

gpio_init_type gpio_initstructure;

spi_init_type spi_init_struct; // SPI外设

printf("\r\n TFT_Init_Gpio_SPI");

// SPI主机

crm_periph_clock_enable(TFT_PORT_PERIPH_CLOCK, TRUE);

// SPI1主机

gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE5, GPIO_MUX_0);

gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE7, GPIO_MUX_0);

gpio_default_para_init(&gpio_initstructure);

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

gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;

gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;

gpio_initstructure.gpio_mode = GPIO_MODE_OUTPUT;

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

gpio_initstructure.gpio_pull = GPIO_PULL_UP;

gpio_init(GPIOA, &gpio_initstructure);

// PA4 ~ PA7 : SPI1外设

/* 主机SCK引脚:PA5 */

gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;

gpio_initstructure.gpio_pull = GPIO_PULL_DOWN;

gpio_initstructure.gpio_mode = GPIO_MODE_MUX;

gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;

gpio_initstructure.gpio_pins = GPIO_PINS_5;

gpio_init(GPIOA, &gpio_initstructure);

gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;

gpio_initstructure.gpio_pull = GPIO_PULL_UP;

gpio_initstructure.gpio_mode = GPIO_MODE_MUX;

gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;

gpio_initstructure.gpio_pins = GPIO_PINS_6;

gpio_init(GPIOA, &gpio_initstructure);

/* 主机mosi引脚:PA7 */

gpio_initstructure.gpio_pull = GPIO_PULL_UP;

gpio_initstructure.gpio_mode = GPIO_MODE_MUX;

gpio_initstructure.gpio_pins = GPIO_PINS_7;

gpio_init(GPIOA, &gpio_initstructure);

// 初始化SPI外设

crm_periph_clock_enable(CRM_SPI1_PERIPH_CLOCK, TRUE);

spi_default_para_init(&spi_init_struct);

spi_init_struct.transmission_mode = SPI_TRANSMIT_FULL_DUPLEX;

spi_init_struct.master_slave_mode = SPI_MODE_MASTER;

spi_init_struct.mclk_freq_division = SPI_MCLK_DIV_2;

spi_init_struct.first_bit_transmission = SPI_FIRST_BIT_MSB;

spi_init_struct.frame_bit_num = SPI_FRAME_8BIT;

spi_init_struct.clock_polarity = SPI_CLOCK_POLARITY_LOW;

spi_init_struct.clock_phase = SPI_CLOCK_PHASE_1EDGE;

spi_init_struct.cs_mode_selection = SPI_CS_SOFTWARE_MODE;

spi_init(SPI1, &spi_init_struct);

spi_enable(SPI1, TRUE);

}

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

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

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

    // SPI1主机
    gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE5, GPIO_MUX_0);
    gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE7, GPIO_MUX_0);

    gpio_default_para_init(&gpio_initstructure);

    // PA1 - RST, PA2 - SC, PA3 - BL
    gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
    gpio_initstructure.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
    gpio_initstructure.gpio_mode = GPIO_MODE_OUTPUT;
    gpio_initstructure.gpio_pins = TFT_RST_PIN | TFT_DC_PIN | TFT_BL_PIN | TFT_CS_PIN ;
    gpio_initstructure.gpio_pull = GPIO_PULL_UP;
    gpio_init(GPIOA, &gpio_initstructure);
   
   
    // PA4 ~ PA7 : SPI1外设

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

    gpio_initstructure.gpio_out_type       = GPIO_OUTPUT_PUSH_PULL;
    gpio_initstructure.gpio_pull           = GPIO_PULL_UP;
    gpio_initstructure.gpio_mode           = GPIO_MODE_MUX;
    gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
    gpio_initstructure.gpio_pins = GPIO_PINS_6;
    gpio_init(GPIOA, &gpio_initstructure);

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

   
    // 初始化SPI外设
    crm_periph_clock_enable(CRM_SPI1_PERIPH_CLOCK, TRUE);
   
    spi_default_para_init(&spi_init_struct);
   
    spi_init_struct.transmission_mode = SPI_TRANSMIT_FULL_DUPLEX;
    spi_init_struct.master_slave_mode = SPI_MODE_MASTER;
    spi_init_struct.mclk_freq_division = SPI_MCLK_DIV_2;
    spi_init_struct.first_bit_transmission = SPI_FIRST_BIT_MSB;
    spi_init_struct.frame_bit_num = SPI_FRAME_8BIT;
    spi_init_struct.clock_polarity = SPI_CLOCK_POLARITY_LOW;
    spi_init_struct.clock_phase = SPI_CLOCK_PHASE_1EDGE;
    spi_init_struct.cs_mode_selection = SPI_CS_SOFTWARE_MODE;

    spi_init(SPI1, &spi_init_struct);


    spi_enable(SPI1, TRUE);
   
}

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



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


初始化部分的波形

这时的波形才是是正常的,能看到0x36,0x00,0x3A,0x05等初始化代码。显示屏上的测试结果与编程预期也是一致的。但注意看SPI的时钟SCLK:

显然不是均匀的,不知道是不是我的逻辑分析仪精度不够测的不准,还是实际就是这样的。
测试结果:

这程序代码:
int main(void) {
    uint32_t cnt=0;
   
    system_clock_config();        // 配置系统时钟
    at32_board_init();            // 板级初始化
    uart_print_init(115200);
   
    delay_us(100);                // 延迟
   
    // 设置工作模式:软件模拟、外设SPI
    lcddev.mode=HARD_FULL;
    //lcddev.mode=SOFT;
   
    TFT_Init();
    TFT_Clear(BLACK);
    TFT_ShowString(0, 0, 16, "Hello 21ic & Artery" , 0);
   
    while(cnt<1000) {
    cnt++;
    TFT_ShowNum(0, 16, cnt, 8 , 16);
}
   
    while(1) {
    //gpio_bits_toggle(TFT_PORT, TFT_SCK_PIN);
    at32_led_toggle(LED4);
    delay_ms(500);
    at32_led_toggle(LED4);
    at32_led_toggle(LED2);
    delay_ms(500);
    at32_led_toggle(LED2);
    at32_led_toggle(LED3);
    delay_ms(500);
    at32_led_toggle(LED3);
    }
   
}

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

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

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

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

130

主题

3870

帖子

5

粉丝