前言
嵌入式系统里面,人机界面显示屏这块,不管CPU还是RAM,基本都是很耗资源的,所以在驱动这块,尽量选择DMA去驱动。
这次板子用的性价比很高的SPI接口TFT屏,320*240分辨率,565(16BIT)色深
显示屏下指令使用SPI POLLING模式,因为数据量少,用DMA反而不划算,显示屏送图形使用DMA,图片数据长度3202402 = 153600字节
一、驱动代码
#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 = 1;
stcDmaCfg.u16TransferCnt = 1;
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 peripheral clock */
PWC_Fcg1PeriphClockCmd(SPI3_UNIT_CLOCK, Enable);
/* Configuration SPI structure */
stcSpiInit.enClkDiv = SpiClkDiv2;
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();
rt_sem_init(&spi3_tx_wait, "spi3tx", 0, RT_IPC_FLAG_FIFO);
}
static void lcd_spi_send(uint8_t dat)
{
SPI_Cmd(SPI3_UNIT, Enable);
while (Reset == SPI_GetFlag(SPI3_UNIT, SpiFlagSendBufferEmpty)){;}
SPI_SendData8(SPI3_UNIT, dat);
while (Reset == SPI_GetFlag(SPI3_UNIT, SpiFlagSpiIdle)){;}
SPI_Cmd(SPI3_UNIT, Disable);
}
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_A0_H;
LCD_CS_L;
/* Enable DMA channel */
DMA_ChannelCmd(SPI3_DMA_UNIT, SPI3_DMA_TX_CHANNEL, Enable);
/* Enable SPI to start DMA */
SPI_Cmd(SPI3_UNIT, Enable);
rt_sem_take(&spi3_tx_wait, RT_WAITING_FOREVER);
LCD_CS_H;
/* Disable SPI */
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} },
{0x21, 0, {0x00} },
{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, 3);
}
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);
}
void st7789v_disp_pic(uint8_t *dat, uint16_t x, uint16_t y, uint16_t width, uint16_t height)
{
uint32_t len, n;
uint8_t *ptr = dat;
len = width * 2 * height;
write_block(x, y, x + width - 1, y + height - 1);
while (len > 0)
{
n = (len >= 60000)?(60000):(len);
lcd_spi_trans(ptr, n);
ptr += n;
len -= n;
}
}
二、测试代码
#include "rtthread.h"
#include "board.h"
void st7789v_disp_pic(uint8_t *dat, uint16_t x, uint16_t y, uint16_t width, uint16_t height);
extern const unsigned char gImage_pic1[];
extern const unsigned char gImage_pic2[];
static void test_entry(void* parameter)
{
st7789v_init();
while (1)
{
st7789v_disp_pic((uint8_t *)gImage_pic1, 0, 0, 240, 320);
rt_thread_delay(1000);
st7789v_disp_pic((uint8_t *)gImage_pic2, 0, 0, 240, 320);
rt_thread_delay(1000);
}
}
static int test_init(void)
{
rt_thread_t th = rt_thread_create("test", test_entry, RT_NULL,
1024, 10, 50);
if (th != RT_NULL)rt_thread_startup(th);
return 0;
}
INIT_APP_EXPORT(test_init);
三、实测效果
一次刷图耗时35ms,刷图时候丝毫不会影响跑马灯的显示,说明DMA还是蛮好用的
HC32F460 SPI DMA TFT