HC32F460 SPI DMA 驱动 TFT显示屏

前言

嵌入式系统里面,人机界面显示屏这块,不管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

  • 6
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 28
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 28
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值