文章目录[隐藏]
目前,小一点的系统很流行SPI接口的显示屏,有黑白TN,有彩色TFT,有高对比OLED,显示分辨率从3296到320240不等,这些显示屏成本低,接口少,易于驱动,所以非常受欢迎
硬件连线通常有以下:
复位RESET,片选CS,地址A0,时钟SCK,数据SDI,背光正负,电源正负,可以做到10PIN以内
因为显示屏的SPI在某些TIMING上会有一些要求,所以片选CS通常使用GPIO模拟,另外RESET,A0也是GPIO,SCK,SDI会使用硬件SPI,达到比较高的传输速率(50MHZ)
这是MCU驱动SPI显示屏的时序图,CS选中器件,DC(A0)决定写指令还是写参数,如果当前BYTE传输期间A0是LOW,表示写指令,如果A0是HIGH,表示写参数,传输完成后,CS再拉高
#include "board.h"
#define LCDCODE_REGFLAG_DELAY 0xFFFE
#define LCDCODE_REGFLAG_END 0xFFFF
#define LCDCODE_DATA_LEN 32
typedef struct
{
uint16_t reg;
uint16_t len;
uint8_t dat[LCDCODE_DATA_LEN];
}lcd_code_t;
static struct rt_semaphore spi3_tx_wait = {0x00};
static void spi3_tx_dma_tc0_irq_cb(void)
{
DMA_ClearIrqFlag(SPI3_DMA_UNIT, SPI3_DMA_TX_CHANNEL, TrnCpltIrq);
rt_sem_release(&spi3_tx_wait);
}
static void spi3_dma_config(void)
{
stc_dma_config_t stcDmaCfg;
stc_irq_regi_conf_t stcIrqRegiConf;
/* configuration structure initialization */
MEM_ZERO_STRUCT(stcDmaCfg);
/* Configuration peripheral clock */
PWC_Fcg0PeriphClockCmd(SPI3_DMA_CLOCK_UNIT, Enable);
PWC_Fcg0PeriphClockCmd(PWC_FCG0_PERIPH_AOS, Enable);
/* Configure TX DMA */
stcDmaCfg.u16BlockSize = 1u;
stcDmaCfg.u16TransferCnt = 128;
stcDmaCfg.u32SrcAddr = (uint32_t)(0);
stcDmaCfg.u32DesAddr = (uint32_t)(&SPI3_UNIT->DR);
stcDmaCfg.stcDmaChCfg.enSrcInc = AddressIncrease;
stcDmaCfg.stcDmaChCfg.enDesInc = AddressFix;
stcDmaCfg.stcDmaChCfg.enTrnWidth = Dma8Bit;
stcDmaCfg.stcDmaChCfg.enIntEn = Enable;
DMA_InitChannel(SPI3_DMA_UNIT, SPI3_DMA_TX_CHANNEL, &stcDmaCfg);
DMA_SetTriggerSrc(SPI3_DMA_UNIT, SPI3_DMA_TX_CHANNEL, SPI3_DMA_TX_TRIG_SOURCE);
/* Enable DMA. */
DMA_Cmd(SPI3_DMA_UNIT, Enable);
/* Set DMA block transfer complete IRQ */
stcIrqRegiConf.enIRQn = IRQ_SPI3_DMA_TX;
stcIrqRegiConf.pfnCallback = &spi3_tx_dma_tc0_irq_cb;
stcIrqRegiConf.enIntSrc = INT_DMA1_TC0;
enIrqRegistration(&stcIrqRegiConf);
NVIC_SetPriority(stcIrqRegiConf.enIRQn, IRQ_PRIORITY_SPI3_DMA_TX);
NVIC_ClearPendingIRQ(stcIrqRegiConf.enIRQn);
NVIC_EnableIRQ(stcIrqRegiConf.enIRQn);
}
static void bsp_spi3_init(void)
{
stc_spi_init_t stcSpiInit;
/* configuration structure initialization */
MEM_ZERO_STRUCT(stcSpiInit);
/* Configuration peripheral clock */
PWC_Fcg1PeriphClockCmd(SPI3_UNIT_CLOCK, Enable);
/* Configuration SPI structure */
stcSpiInit.enClkDiv = SpiClkDiv32;
stcSpiInit.enFrameNumber = SpiFrameNumber1;
stcSpiInit.enDataLength = SpiDataLengthBit8;
stcSpiInit.enFirstBitPosition = SpiFirstBitPositionMSB;
stcSpiInit.enSckPolarity = SpiSckIdleLevelLow;
stcSpiInit.enSckPhase = SpiSckOddSampleEvenChange;
stcSpiInit.enReadBufferObject = SpiReadReceiverBuffer;
stcSpiInit.enWorkMode = SpiWorkMode3Line;
stcSpiInit.enTransMode = SpiTransOnlySend;
stcSpiInit.enCommAutoSuspendEn = Disable;
stcSpiInit.enModeFaultErrorDetectEn = Disable;
stcSpiInit.enParitySelfDetectEn = Disable;
stcSpiInit.enParityEn = Disable;
stcSpiInit.enParity = SpiParityEven;
stcSpiInit.enMasterSlaveMode = SpiModeMaster;
stcSpiInit.stcDelayConfig.enSsSetupDelayOption = SpiSsSetupDelayCustomValue;
stcSpiInit.stcDelayConfig.enSsSetupDelayTime = SpiSsSetupDelaySck1;
stcSpiInit.stcDelayConfig.enSsHoldDelayOption = SpiSsHoldDelayCustomValue;
stcSpiInit.stcDelayConfig.enSsHoldDelayTime = SpiSsHoldDelaySck1;
stcSpiInit.stcDelayConfig.enSsIntervalTimeOption = SpiSsIntervalCustomValue;
stcSpiInit.stcDelayConfig.enSsIntervalTime = SpiSsIntervalSck1PlusPck2;
SPI_Init(SPI3_UNIT, &stcSpiInit);
spi3_dma_config();
SPI_Cmd(SPI3_UNIT, Enable);
rt_sem_init(&spi3_tx_wait, "spi3tx", 0, RT_IPC_FLAG_FIFO);
}
static void lcd_spi_send(uint8_t dat)
{
while (Reset == SPI_GetFlag(SPI3_UNIT, SpiFlagSendBufferEmpty)){;}
SPI_SendData8(SPI3_UNIT, dat);
while (Reset == SPI_GetFlag(SPI3_UNIT, SpiFlagSpiIdle)){;}
}
/*
static void lcd_spi_trans(uint8_t *dat, uint16_t len)
{
DMA_SetSrcAddress (SPI3_DMA_UNIT, SPI3_DMA_TX_CHANNEL, (uint32_t)dat);
DMA_SetTransferCnt(SPI3_DMA_UNIT, SPI3_DMA_TX_CHANNEL, len);
LCD_CS_L;
DMA_ChannelCmd(SPI3_DMA_UNIT, SPI3_DMA_TX_CHANNEL, Enable);
SPI_Cmd(SPI3_UNIT, Enable);
rt_sem_take(&spi3_tx_wait, RT_WAITING_FOREVER);
LCD_CS_H;
SPI_Cmd(SPI3_UNIT, Disable);
DMA_ChannelCmd(SPI3_DMA_UNIT, SPI3_DMA_TX_CHANNEL, Disable);
}
*/
static void delayms(uint16_t ms)
{
rt_thread_delay(ms);
}
static void write_codetable(lcd_code_t *code, uint32_t count)
{
lcd_code_t *pcode = code;
//传输指令和参数
for (uint32_t i = 0; i < count; i++)
{
if (pcode->reg == LCDCODE_REGFLAG_END)
{ //结束跳出
break;
}
else if (pcode->reg == LCDCODE_REGFLAG_DELAY)
{ //延时MS
delayms(pcode->len);
}
else
{ //常规发送指令
LCD_A0_L;
LCD_CS_L;
lcd_spi_send(pcode->reg);
for (uint32_t j = 0; j < pcode->len; j++)
{
LCD_A0_H;
lcd_spi_send(pcode->dat[j]);
}
LCD_CS_H;
}
pcode++; //下一个寄存器
}
}
//初始化指令表
static lcd_code_t st7789v_initcode[] = {
//{0x11, 0, {0x00} },
//{LCDCODE_REGFLAG_DELAY, 120, {0x00}},
//{0xb2, 5, {0x0c, 0x0c, 0x00, 0x33, 0x33} },
//{0xb7, 1, {0x35} },
//{0xbb, 1, {0x35} },
//{0xc0, 1, {0x2c} },
//{0xc2, 1, {0x01} },
//{0xc3, 1, {0x0b} },
//{0xc4, 1, {0x20} },
//{0xc6, 1, {0x0f} },
//{0xd0, 2, {0xa4, 0xa1} },
//{0xe0, 14, {0xd0, 0x00, 0x02, 0x07, 0x0b, 0x1a, 0x32, 0x54, 0x40, 0x29, 0x12, 0x12, 0x12, 0x17} },
//{0xe1, 14, {0xd0, 0x00, 0x02, 0x07, 0x05, 0x25, 0x2d, 0x44, 0x45, 0x1c, 0x18, 0x16, 0x1c, 0x1d} },
//{0x36, 1, {0x00} },
{0x3a, 1, {0x05} },
//{0x29, 0, {0x00} },
{LCDCODE_REGFLAG_END, 0x00, {0x00} }
};
/*显示区域设置*/
static lcd_code_t st7789v_blockcode[] = {
{0x2a, 4, {0x00, 0x00, 0x00, 0x00} },
{0x2b, 4, {0x00, 0x00, 0x00, 0x00} },
{0x2c, 0, {0x00} },
{LCDCODE_REGFLAG_END, 0x00, {0x00} }
};
static void write_block(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{
st7789v_blockcode[0].dat[0] = 0x00;
st7789v_blockcode[0].dat[1] = x0;
st7789v_blockcode[0].dat[2] = 0x00;
st7789v_blockcode[0].dat[3] = x1;
st7789v_blockcode[1].dat[0] = y0 >> 8;
st7789v_blockcode[1].dat[1] = y0 & 0xff;
st7789v_blockcode[1].dat[2] = y1 >> 8;
st7789v_blockcode[1].dat[3] = y1 & 0xff;
write_codetable(st7789v_blockcode, 5);
}
void st7789v_init(void)
{
stc_port_init_t stcPortInit;
MEM_ZERO_STRUCT(stcPortInit);
PWC_Fcg1PeriphClockCmd(SPI3_UNIT_CLOCK, Enable);
/* Configuration SPI pin */
PORT_SetFunc(SPI3_NSS_PORT, SPI3_NSS_PIN, Func_Gpio, Disable);
PORT_SetFunc(SPI3_SCK_PORT, SPI3_SCK_PIN, SPI3_SCK_FUNC, Disable);
PORT_SetFunc(SPI3_SDI_PORT, SPI3_SDI_PIN, SPI3_SDI_FUNC, Disable);
PORT_SetFunc(LCD_RST_PORT, LCD_RST_PIN, Func_Gpio, Disable);
PORT_SetFunc(LCD_A0_PORT, LCD_A0_PIN, Func_Gpio, Disable);
stcPortInit.enPinMode = Pin_Mode_Out;
stcPortInit.enPinDrv = Pin_Drv_H;
PORT_Init(LCD_RST_PORT, LCD_RST_PIN, &stcPortInit);
PORT_Init(LCD_A0_PORT, LCD_A0_PIN, &stcPortInit);
PORT_Init(LCD_CS_PORT, LCD_CS_PIN, &stcPortInit);
bsp_spi3_init();
LCD_RST_H;
LCD_A0_H;
LCD_CS_H;
delayms(10);
LCD_RST_L;
delayms(10);
LCD_RST_H;
delayms(120);
write_codetable(st7789v_initcode, sizeof(st7789v_initcode)/sizeof(lcd_code_t));
}
void st7789v_test(void)
{
LCD_RST_H;
LCD_A0_H;
LCD_CS_H;
delayms(10);
LCD_RST_L;
delayms(10);
LCD_RST_H;
delayms(120);
write_codetable(st7789v_initcode, sizeof(st7789v_initcode)/sizeof(lcd_code_t));
delayms(10);
//write_block(1, 1, 128, 300);
}
以上代码是测试MCU发送指令0x3a, 参数0x05,SPI发送代码参考HC32F460 DDL库hc32f460_ddl_Rev2.2.0.zip
static void lcd_spi_send(uint8_t dat)
{
while (Reset == SPI_GetFlag(SPI3_UNIT, SpiFlagSendBufferEmpty)){;}
SPI_SendData8(SPI3_UNIT, dat);
}
LCD_A0_L;
LCD_CS_L;
lcd_spi_send(pcode->reg);
for (uint32_t j = 0; j < pcode->len; j++)
{
LCD_A0_H;
lcd_spi_send(pcode->dat[j]);
}
LCD_CS_H;
每次发送BYTE之前判断SPI BUFFER是否为空,如果空,写入数据,实施证明,BUFFER空,仅仅是BUFFER空,数据还在SPI硬件里发送着,根本就没结束,而且参考手册里面,SPI和发送相关的状态位也就这个BUFFER EMPTY,而不像之前STM,还有一个发送结束COMPLETE状态位。
看下面波形,第一个字节8个CLK还没结束,CS,A0就已经变高了,造成实际传输无效
修改如下:
static void lcd_spi_send(uint8_t dat)
{
while (Reset == SPI_GetFlag(SPI3_UNIT, SpiFlagSendBufferEmpty)){;}
SPI_SendData8(SPI3_UNIT, dat);
while (Reset == SPI_GetFlag(SPI3_UNIT, SpiFlagSpiIdle)){;}
}
LCD_A0_L;
LCD_CS_L;
lcd_spi_send(pcode->reg);
for (uint32_t j = 0; j < pcode->len; j++)
{
LCD_A0_H;
lcd_spi_send(pcode->dat[j]);
}
LCD_CS_H;
每次写数据之前判断BUFFER是否空,每次写数据之后,等待SPI传输结束
波形上已经达到我们时序的要求,所以,直接参考当前库代码,至少对于驱动LCD,是有问题的。
总结
有异常,外设无法工作时候,我们要习惯于分析波形,从时序中排查问题,然后再尝试解决问题。
版权声明:本文为CSDN博主「sainty07」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sainty07/article/details/122441880
目前,小一点的系统很流行SPI接口的显示屏,有黑白TN,有彩色TFT,有高对比OLED,显示分辨率从3296到320240不等,这些显示屏成本低,接口少,易于驱动,所以非常受欢迎
硬件连线通常有以下:
复位RESET,片选CS,地址A0,时钟SCK,数据SDI,背光正负,电源正负,可以做到10PIN以内
因为显示屏的SPI在某些TIMING上会有一些要求,所以片选CS通常使用GPIO模拟,另外RESET,A0也是GPIO,SCK,SDI会使用硬件SPI,达到比较高的传输速率(50MHZ)
这是MCU驱动SPI显示屏的时序图,CS选中器件,DC(A0)决定写指令还是写参数,如果当前BYTE传输期间A0是LOW,表示写指令,如果A0是HIGH,表示写参数,传输完成后,CS再拉高
#include "board.h"
#define LCDCODE_REGFLAG_DELAY 0xFFFE
#define LCDCODE_REGFLAG_END 0xFFFF
#define LCDCODE_DATA_LEN 32
typedef struct
{
uint16_t reg;
uint16_t len;
uint8_t dat[LCDCODE_DATA_LEN];
}lcd_code_t;
static struct rt_semaphore spi3_tx_wait = {0x00};
static void spi3_tx_dma_tc0_irq_cb(void)
{
DMA_ClearIrqFlag(SPI3_DMA_UNIT, SPI3_DMA_TX_CHANNEL, TrnCpltIrq);
rt_sem_release(&spi3_tx_wait);
}
static void spi3_dma_config(void)
{
stc_dma_config_t stcDmaCfg;
stc_irq_regi_conf_t stcIrqRegiConf;
/* configuration structure initialization */
MEM_ZERO_STRUCT(stcDmaCfg);
/* Configuration peripheral clock */
PWC_Fcg0PeriphClockCmd(SPI3_DMA_CLOCK_UNIT, Enable);
PWC_Fcg0PeriphClockCmd(PWC_FCG0_PERIPH_AOS, Enable);
/* Configure TX DMA */
stcDmaCfg.u16BlockSize = 1u;
stcDmaCfg.u16TransferCnt = 128;
stcDmaCfg.u32SrcAddr = (uint32_t)(0);
stcDmaCfg.u32DesAddr = (uint32_t)(&SPI3_UNIT->DR);
stcDmaCfg.stcDmaChCfg.enSrcInc = AddressIncrease;
stcDmaCfg.stcDmaChCfg.enDesInc = AddressFix;
stcDmaCfg.stcDmaChCfg.enTrnWidth = Dma8Bit;
stcDmaCfg.stcDmaChCfg.enIntEn = Enable;
DMA_InitChannel(SPI3_DMA_UNIT, SPI3_DMA_TX_CHANNEL, &stcDmaCfg);
DMA_SetTriggerSrc(SPI3_DMA_UNIT, SPI3_DMA_TX_CHANNEL, SPI3_DMA_TX_TRIG_SOURCE);
/* Enable DMA. */
DMA_Cmd(SPI3_DMA_UNIT, Enable);
/* Set DMA block transfer complete IRQ */
stcIrqRegiConf.enIRQn = IRQ_SPI3_DMA_TX;
stcIrqRegiConf.pfnCallback = &spi3_tx_dma_tc0_irq_cb;
stcIrqRegiConf.enIntSrc = INT_DMA1_TC0;
enIrqRegistration(&stcIrqRegiConf);
NVIC_SetPriority(stcIrqRegiConf.enIRQn, IRQ_PRIORITY_SPI3_DMA_TX);
NVIC_ClearPendingIRQ(stcIrqRegiConf.enIRQn);
NVIC_EnableIRQ(stcIrqRegiConf.enIRQn);
}
static void bsp_spi3_init(void)
{
stc_spi_init_t stcSpiInit;
/* configuration structure initialization */
MEM_ZERO_STRUCT(stcSpiInit);
/* Configuration peripheral clock */
PWC_Fcg1PeriphClockCmd(SPI3_UNIT_CLOCK, Enable);
/* Configuration SPI structure */
stcSpiInit.enClkDiv = SpiClkDiv32;
stcSpiInit.enFrameNumber = SpiFrameNumber1;
stcSpiInit.enDataLength = SpiDataLengthBit8;
stcSpiInit.enFirstBitPosition = SpiFirstBitPositionMSB;
stcSpiInit.enSckPolarity = SpiSckIdleLevelLow;
stcSpiInit.enSckPhase = SpiSckOddSampleEvenChange;
stcSpiInit.enReadBufferObject = SpiReadReceiverBuffer;
stcSpiInit.enWorkMode = SpiWorkMode3Line;
stcSpiInit.enTransMode = SpiTransOnlySend;
stcSpiInit.enCommAutoSuspendEn = Disable;
stcSpiInit.enModeFaultErrorDetectEn = Disable;
stcSpiInit.enParitySelfDetectEn = Disable;
stcSpiInit.enParityEn = Disable;
stcSpiInit.enParity = SpiParityEven;
stcSpiInit.enMasterSlaveMode = SpiModeMaster;
stcSpiInit.stcDelayConfig.enSsSetupDelayOption = SpiSsSetupDelayCustomValue;
stcSpiInit.stcDelayConfig.enSsSetupDelayTime = SpiSsSetupDelaySck1;
stcSpiInit.stcDelayConfig.enSsHoldDelayOption = SpiSsHoldDelayCustomValue;
stcSpiInit.stcDelayConfig.enSsHoldDelayTime = SpiSsHoldDelaySck1;
stcSpiInit.stcDelayConfig.enSsIntervalTimeOption = SpiSsIntervalCustomValue;
stcSpiInit.stcDelayConfig.enSsIntervalTime = SpiSsIntervalSck1PlusPck2;
SPI_Init(SPI3_UNIT, &stcSpiInit);
spi3_dma_config();
SPI_Cmd(SPI3_UNIT, Enable);
rt_sem_init(&spi3_tx_wait, "spi3tx", 0, RT_IPC_FLAG_FIFO);
}
static void lcd_spi_send(uint8_t dat)
{
while (Reset == SPI_GetFlag(SPI3_UNIT, SpiFlagSendBufferEmpty)){;}
SPI_SendData8(SPI3_UNIT, dat);
while (Reset == SPI_GetFlag(SPI3_UNIT, SpiFlagSpiIdle)){;}
}
/*
static void lcd_spi_trans(uint8_t *dat, uint16_t len)
{
DMA_SetSrcAddress (SPI3_DMA_UNIT, SPI3_DMA_TX_CHANNEL, (uint32_t)dat);
DMA_SetTransferCnt(SPI3_DMA_UNIT, SPI3_DMA_TX_CHANNEL, len);
LCD_CS_L;
DMA_ChannelCmd(SPI3_DMA_UNIT, SPI3_DMA_TX_CHANNEL, Enable);
SPI_Cmd(SPI3_UNIT, Enable);
rt_sem_take(&spi3_tx_wait, RT_WAITING_FOREVER);
LCD_CS_H;
SPI_Cmd(SPI3_UNIT, Disable);
DMA_ChannelCmd(SPI3_DMA_UNIT, SPI3_DMA_TX_CHANNEL, Disable);
}
*/
static void delayms(uint16_t ms)
{
rt_thread_delay(ms);
}
static void write_codetable(lcd_code_t *code, uint32_t count)
{
lcd_code_t *pcode = code;
//传输指令和参数
for (uint32_t i = 0; i < count; i++)
{
if (pcode->reg == LCDCODE_REGFLAG_END)
{ //结束跳出
break;
}
else if (pcode->reg == LCDCODE_REGFLAG_DELAY)
{ //延时MS
delayms(pcode->len);
}
else
{ //常规发送指令
LCD_A0_L;
LCD_CS_L;
lcd_spi_send(pcode->reg);
for (uint32_t j = 0; j < pcode->len; j++)
{
LCD_A0_H;
lcd_spi_send(pcode->dat[j]);
}
LCD_CS_H;
}
pcode++; //下一个寄存器
}
}
//初始化指令表
static lcd_code_t st7789v_initcode[] = {
//{0x11, 0, {0x00} },
//{LCDCODE_REGFLAG_DELAY, 120, {0x00}},
//{0xb2, 5, {0x0c, 0x0c, 0x00, 0x33, 0x33} },
//{0xb7, 1, {0x35} },
//{0xbb, 1, {0x35} },
//{0xc0, 1, {0x2c} },
//{0xc2, 1, {0x01} },
//{0xc3, 1, {0x0b} },
//{0xc4, 1, {0x20} },
//{0xc6, 1, {0x0f} },
//{0xd0, 2, {0xa4, 0xa1} },
//{0xe0, 14, {0xd0, 0x00, 0x02, 0x07, 0x0b, 0x1a, 0x32, 0x54, 0x40, 0x29, 0x12, 0x12, 0x12, 0x17} },
//{0xe1, 14, {0xd0, 0x00, 0x02, 0x07, 0x05, 0x25, 0x2d, 0x44, 0x45, 0x1c, 0x18, 0x16, 0x1c, 0x1d} },
//{0x36, 1, {0x00} },
{0x3a, 1, {0x05} },
//{0x29, 0, {0x00} },
{LCDCODE_REGFLAG_END, 0x00, {0x00} }
};
/*显示区域设置*/
static lcd_code_t st7789v_blockcode[] = {
{0x2a, 4, {0x00, 0x00, 0x00, 0x00} },
{0x2b, 4, {0x00, 0x00, 0x00, 0x00} },
{0x2c, 0, {0x00} },
{LCDCODE_REGFLAG_END, 0x00, {0x00} }
};
static void write_block(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{
st7789v_blockcode[0].dat[0] = 0x00;
st7789v_blockcode[0].dat[1] = x0;
st7789v_blockcode[0].dat[2] = 0x00;
st7789v_blockcode[0].dat[3] = x1;
st7789v_blockcode[1].dat[0] = y0 >> 8;
st7789v_blockcode[1].dat[1] = y0 & 0xff;
st7789v_blockcode[1].dat[2] = y1 >> 8;
st7789v_blockcode[1].dat[3] = y1 & 0xff;
write_codetable(st7789v_blockcode, 5);
}
void st7789v_init(void)
{
stc_port_init_t stcPortInit;
MEM_ZERO_STRUCT(stcPortInit);
PWC_Fcg1PeriphClockCmd(SPI3_UNIT_CLOCK, Enable);
/* Configuration SPI pin */
PORT_SetFunc(SPI3_NSS_PORT, SPI3_NSS_PIN, Func_Gpio, Disable);
PORT_SetFunc(SPI3_SCK_PORT, SPI3_SCK_PIN, SPI3_SCK_FUNC, Disable);
PORT_SetFunc(SPI3_SDI_PORT, SPI3_SDI_PIN, SPI3_SDI_FUNC, Disable);
PORT_SetFunc(LCD_RST_PORT, LCD_RST_PIN, Func_Gpio, Disable);
PORT_SetFunc(LCD_A0_PORT, LCD_A0_PIN, Func_Gpio, Disable);
stcPortInit.enPinMode = Pin_Mode_Out;
stcPortInit.enPinDrv = Pin_Drv_H;
PORT_Init(LCD_RST_PORT, LCD_RST_PIN, &stcPortInit);
PORT_Init(LCD_A0_PORT, LCD_A0_PIN, &stcPortInit);
PORT_Init(LCD_CS_PORT, LCD_CS_PIN, &stcPortInit);
bsp_spi3_init();
LCD_RST_H;
LCD_A0_H;
LCD_CS_H;
delayms(10);
LCD_RST_L;
delayms(10);
LCD_RST_H;
delayms(120);
write_codetable(st7789v_initcode, sizeof(st7789v_initcode)/sizeof(lcd_code_t));
}
void st7789v_test(void)
{
LCD_RST_H;
LCD_A0_H;
LCD_CS_H;
delayms(10);
LCD_RST_L;
delayms(10);
LCD_RST_H;
delayms(120);
write_codetable(st7789v_initcode, sizeof(st7789v_initcode)/sizeof(lcd_code_t));
delayms(10);
//write_block(1, 1, 128, 300);
}
以上代码是测试MCU发送指令0x3a, 参数0x05,SPI发送代码参考HC32F460 DDL库hc32f460_ddl_Rev2.2.0.zip
static void lcd_spi_send(uint8_t dat)
{
while (Reset == SPI_GetFlag(SPI3_UNIT, SpiFlagSendBufferEmpty)){;}
SPI_SendData8(SPI3_UNIT, dat);
}
LCD_A0_L;
LCD_CS_L;
lcd_spi_send(pcode->reg);
for (uint32_t j = 0; j < pcode->len; j++)
{
LCD_A0_H;
lcd_spi_send(pcode->dat[j]);
}
LCD_CS_H;
每次发送BYTE之前判断SPI BUFFER是否为空,如果空,写入数据,实施证明,BUFFER空,仅仅是BUFFER空,数据还在SPI硬件里发送着,根本就没结束,而且参考手册里面,SPI和发送相关的状态位也就这个BUFFER EMPTY,而不像之前STM,还有一个发送结束COMPLETE状态位。
看下面波形,第一个字节8个CLK还没结束,CS,A0就已经变高了,造成实际传输无效
修改如下:
static void lcd_spi_send(uint8_t dat)
{
while (Reset == SPI_GetFlag(SPI3_UNIT, SpiFlagSendBufferEmpty)){;}
SPI_SendData8(SPI3_UNIT, dat);
while (Reset == SPI_GetFlag(SPI3_UNIT, SpiFlagSpiIdle)){;}
}
LCD_A0_L;
LCD_CS_L;
lcd_spi_send(pcode->reg);
for (uint32_t j = 0; j < pcode->len; j++)
{
LCD_A0_H;
lcd_spi_send(pcode->dat[j]);
}
LCD_CS_H;
每次写数据之前判断BUFFER是否空,每次写数据之后,等待SPI传输结束
波形上已经达到我们时序的要求,所以,直接参考当前库代码,至少对于驱动LCD,是有问题的。
总结
有异常,外设无法工作时候,我们要习惯于分析波形,从时序中排查问题,然后再尝试解决问题。
版权声明:本文为CSDN博主「sainty07」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sainty07/article/details/122441880
暂无评论