求助CH32V103的SPI2运行在8bit模式,用来点亮三线9bit屏幕的时候需要添加一个D/C位,应该如何写呀?

我买了一个CH32V103R_NUCLEO,这个板子看起来真漂亮,黑金色。

此外,我拥有我制作的屏幕模块。 我设计了一个 Arduino 标准引脚。 事实证明,它工作良好,可以准确地安装在CH32V103R_NUCLEO板上。

此屏由ST7789驱动,具有三线9位SPI接口。 也就是说,这个屏幕没有一个叫做D/C的引脚来区分SPI数据是reg还是command。 为此,我们需要在 SPI 发送 8 位数据之前使用单个位。 告诉屏幕接下来的 8 位是 reg 还是指令。

首先,我根据现有数据(在STM32平台上,使用IO模拟SPI驱动)移植到CH32V103。 我必须感谢制造商在底层驱动程序上所做的工作。 我的移植很简单,屏幕显示成功。 通过一个小型的逻辑分析仪,我们可以看到一个成功的9位SPI时序。在每一个上升沿采样MOSI引脚的电平。

image.png

这一段是程序中用来对屏幕进行初始化所发送的数据,

......
    /* Memory Data Access Control */
    LCD_WR_REG(0x36);
    LCD_WR_DATA(0x00);
 
     /* RGB 5-6-5-bit  */
    LCD_WR_REG(0x3A);
    LCD_WR_DATA(0x55);
......

到目前为止,一切都还很顺利。

由于Arduino引脚中是包含SPI接口的,我在设计屏幕背板的时候特意将相应的引脚放在特定的位置,通过观察CH32V103R_NUCLEO的原理图,我们可以看到制造商也将SPI2的引脚放在相应的位置,这也就意味着我能够使用硬件SPI来驱动我的屏幕,这也许意味着我能够拥有更快的刷屏速度(实话说,模拟SPI在屏幕切换时的效果太缓慢了)。

于是我做了一些工作来完成我的想法,我从开发板资料包中EVT>EXAM>SPI>1Lines_half-duplex工程中复制了一些代码作为移植的基础。

首先对SPI初始化代码进行修改,示例程序中没有任何和SPI2的初始化相关,我根据头文件中的一些宏定义,修改了一些项目

void SPI2_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    SPI_InitTypeDef SPI_InitStructure;

    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15|GPIO_Pin_13|GPIO_Pin_14;    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        //SPI2相关的引脚复用
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init( GPIOB, &GPIO_InitStructure );
    LCD_CTRL->BSHR=12;

    RCC_APB1PeriphClockCmd(  RCC_APB1Periph_SPI2, ENABLE );    //时钟使能

    SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;

    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;            //时钟上升沿锁定电平?
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;    //设置速度慢一些用于调试
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 7;

    SPI_Init( SPI2, &SPI_InitStructure );    //SPI2的初始化

    SPI_Cmd( SPI2, ENABLE );    //SPI2使能
}

(这些修改应该是没有问题的,因为在后续的验证过程中,当我去掉所添加的D/C发送过程后,SPI便能够顺利的发送所有的reg与command)

SPI2的发送函数如下,这段代码从网络上相关的讨论中获得,判断了一个标志位以及添加超时策略。

void  SPIv_WriteData(u8 Data)
{
    unsigned int retry=0;
    while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET)
    {
        retry++;
        if(retry>1000)
            {
                printf("timeout!\r\n");
                break;
            }
    }
    SPI_I2S_SendData(SPI2,Data);
    retry=0;
}

接下来我开始尝试在SPI发送前添加一个bit来当作D/C位,发送程序如下

void LCD_WR_REG(u8 data)
{ 
    LCD_CS_CLR;
    IO_SW(0);    //用于切换SCLK和MOSI引脚的工作模式
    SPI_MOSI_CLR;    //发送REG时D/C为0,发送COMMAND时D/C为1
    SPI_SCLK_CLR;
    Delay_Us(2);
    SPI_SCLK_SET;
    IO_SW(1);    //用于切换SCLK和MOSI引脚的工作模式
    SPIv_WriteData(data);
    LCD_CS_SET;
}

上述的工作模式切换部分是我通过在一些论坛搜索时得到的提示,在直接操作SCLK和MOSI引脚之前将其初始化为out_PP,在操作完成之后将其恢复AF_PP。这个函数如下所示

void IO_SW(uint8_t flag)
{
    GPIO_InitTypeDef iosw;
    iosw.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_15;
    (flag==0)?(iosw.GPIO_Mode = GPIO_Mode_Out_PP): (iosw.GPIO_Mode = GPIO_Mode_AF_PP);
    iosw.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(GPIOB, &iosw);
}

至此,我感觉移植工作应该已经完成了,从我个人有限的知识来看,我的工作完成的很不错,很周到,但是事实却很糟糕,我的屏幕没有显示任何东西,通过逻辑分析仪我能够看到,我好像离成功已经非常近了(下方红字表示正确的),存在一些问题,我单独操作的MOSI电平似乎延后到了硬件SPI的发送动作中,此外还有操作MOSI不成功的情况,但是现在我却不知道如何进行下一步了,我需要开始寻求大家的帮助。

image.png

我也曾试着在发送函数的不同位置添加延时,但那看起来似乎没有起作用。j_0065.gif


希望有一些朋友有相关经验的能够一起讨论j_0057.gif

你可以参考一下下面链接帖子:硬件SPI驱动OLED,对照修改一下你的初始化和发送函数看看,建议发送函数超时时间修改小一点看看。

https://bbs.21ic.com/icview-3121432-1-1.html


只有登录才能回复,可以选择微信账号登录