打印
[MM32硬件]

【灵动微电子MM32F0121测评】6、SPI主模式实验-驱动ILI9341

[复制链接]
137|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本文的目的:配置MM32F0121为SPI主模式,并成功SPI方式驱动ILI9341显示屏

相关例程可参见:
LibSamples_MM32F0120_V1.13.4\Samples\LibSamples\SPI

如果使用polling方式可参考SPI_Master_FLASH_Polling例程,如何使用DMA方式可参考SPI_Master_FLASH_DMA_Polling


因为MM32F0121只有一个SPI接口,并且已分配给板载的flash,所以需要考虑不能和flash冲突。可以将SPI1复用为其他管脚或SPI1_NSS与flash的使用管脚不同。

本例SPI1_NSS使用其他管脚。


1、SPI配置

//CS:PA0  DC:PA1 RESET:PA2 BK:PA3
//PA4:SPI1_NSS;PA5:SPI1_SCK;PA6:SPI1_MISO;PA7:SPI1_MOSI

void SPI_Configure(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    SPI_InitTypeDef  SPI_InitStruct;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

    SPI_StructInit(&SPI_InitStruct);
    SPI_InitStruct.SPI_Mode      = SPI_Mode_Master;
    SPI_InitStruct.SPI_DataSize  = SPI_DataSize_8b;
    SPI_InitStruct.SPI_DataWidth = 8;
    SPI_InitStruct.SPI_CPOL      = SPI_CPOL_Low;
    SPI_InitStruct.SPI_CPHA      = SPI_CPHA_1Edge;
    SPI_InitStruct.SPI_NSS       = SPI_NSS_Soft;
    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
    SPI_InitStruct.SPI_FirstBit  = SPI_FirstBit_MSB;
    SPI_Init(SPI1, &SPI_InitStruct);

    SPI_BiDirectionalLineConfig(SPI1, SPI_Enable_RX);
    SPI_BiDirectionalLineConfig(SPI1, SPI_Enable_TX);

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

   // GPIO_PinAFConfig(GPIOA, GPIO_PinSource4,  GPIO_AF_0);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource5,  GPIO_AF_0);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource6,  GPIO_AF_0);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource7,  GPIO_AF_0);

    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_5 | GPIO_Pin_7;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_6;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_IPU;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    SPI_Cmd(SPI1, ENABLE);
}


根据手册SPI最大速率可达36Mbps,没有计算,简单地取SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
CS引脚需软件控制,为了和flash的CS不同配置为PA0

2、ILI9341非SPI引脚配置
CS:PA0  DC:PA1 RESET:PA2 BK:PA3
void LCD_GPIO_Configure(void)
{

        GPIO_InitTypeDef GPIO_InitStruct;

        RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);

        GPIO_StructInit(&GPIO_InitStruct);
        GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
        GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
        GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_Out_PP;
        GPIO_Init(GPIOA, &GPIO_InitStruct);

}


3、SPI polling方式发送数据函数
在向LCD发送少量SPI数据时,使用polling方式,如LCD配置,输出字符串
void SPI_TxData_Polling(uint8_t *Buffer, uint8_t Length)
{
    uint8_t i = 0, Data = 0;

    for (i = 0; i < Length; i++)
    {
        //SPI_SendData(SPI1, Buffer[i]);
                                SPI_SendByte(SPI1, Buffer[i]);

        while (RESET == SPI_GetFlagStatus(SPI1, SPI_FLAG_TXEPT))
        {
        }

        while (RESET == SPI_GetFlagStatus(SPI1, SPI_FLAG_RXAVL))
        {
        }

        Data = SPI_ReceiveData(SPI1);
    }
}
4、SPI DMA方式发送数据函数
在向LCD发送大量SPI数据时,用DMA方式,如整个屏幕清屏,由于MM32F0121的RAM较小,在使用DMA时申请的内存有限,导致每次DMA传送的数据较少,影响显示速度的提高
void SPI_TxData_DMA_Polling(uint8_t *Buffer, uint8_t Length)
{
    DMA_InitTypeDef  DMA_InitStruct;

    uint8_t DataCache = 0;

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA, ENABLE);

    DMA_DeInit(DMA_Channel2);
    DMA_DeInit(DMA_Channel3);

    DMA_StructInit(&DMA_InitStruct);
    DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&(SPI1->RXREG);
    DMA_InitStruct.DMA_MemoryBaseAddr     = (uint32_t)&DataCache;
    DMA_InitStruct.DMA_DIR                = DMA_DIR_PeripheralSRC;
    DMA_InitStruct.DMA_BufferSize         = Length;
    DMA_InitStruct.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
    DMA_InitStruct.DMA_MemoryInc          = DMA_MemoryInc_Disable;
    DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStruct.DMA_MemoryDataSize     = DMA_MemoryDataSize_Byte;
    DMA_InitStruct.DMA_Mode               = DMA_Mode_Normal;
    DMA_InitStruct.DMA_Priority           = DMA_Priority_VeryHigh;
    DMA_InitStruct.DMA_M2M                = DMA_M2M_Disable;
    DMA_InitStruct.DMA_Auto_Reload        = DMA_Auto_Reload_Disable;
    DMA_Init(DMA_Channel2, &DMA_InitStruct);

    DMA_StructInit(&DMA_InitStruct);
    DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&(SPI1->TXREG);
    DMA_InitStruct.DMA_MemoryBaseAddr     = (uint32_t)Buffer;
    DMA_InitStruct.DMA_DIR                = DMA_DIR_PeripheralDST;
    DMA_InitStruct.DMA_BufferSize         = Length;
    DMA_InitStruct.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
    DMA_InitStruct.DMA_MemoryInc          = DMA_MemoryInc_Enable;
    DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStruct.DMA_MemoryDataSize     = DMA_MemoryDataSize_Byte;
    DMA_InitStruct.DMA_Mode               = DMA_Mode_Normal;
    DMA_InitStruct.DMA_Priority           = DMA_Priority_VeryHigh;
    DMA_InitStruct.DMA_M2M                = DMA_M2M_Disable;
    DMA_InitStruct.DMA_Auto_Reload        = DMA_Auto_Reload_Disable;
    DMA_Init(DMA_Channel3, &DMA_InitStruct);

    DMA_Cmd(DMA_Channel2, ENABLE);
    DMA_Cmd(DMA_Channel3, ENABLE);

    SPI_DMACmd(SPI1, ENABLE);

    while (RESET == DMA_GetFlagStatus(DMA_FLAG_TC2))
    {
    }

    while (RESET == DMA_GetFlagStatus(DMA_FLAG_TC3))
    {
    }

    SPI_DMACmd(SPI1, DISABLE);

    DMA_Cmd(DMA_Channel2, DISABLE);
    DMA_Cmd(DMA_Channel3, DISABLE);
}
5、ILI9341驱动部分

1)GPIO读写定义
#define LCD_CS          GPIOA,GPIO_Pin_0                        //
#define LCD_DC          GPIOA,GPIO_Pin_1                        //
#define LCD_RST          GPIOA,GPIO_Pin_2                        //
#define LCD_LED          GPIOA,GPIO_Pin_3                        //

#define LCD_CS_SET()                 GPIO_WriteBit(LCD_CS, Bit_SET);
#define LCD_CS_RESET()         GPIO_WriteBit(LCD_CS, Bit_RESET);

#define LCD_DC_SET()                 GPIO_WriteBit(LCD_DC, Bit_SET);
#define LCD_DC_RESET()         GPIO_WriteBit(LCD_DC, Bit_RESET);

#define LCD_RST_SET()         GPIO_WriteBit(LCD_RST, Bit_SET);
#define LCD_RST_RESET() GPIO_WriteBit(LCD_RST, Bit_RESET);

#define LCD_LED_SET()         GPIO_WriteBit(LCD_LED, Bit_SET);
#define LCD_LED_RESET() GPIO_WriteBit(LCD_LED, Bit_RESET);
2)写指令函数
void LCD_WriteIndex(uint8_t index)
{
        LCD_CS_RESET();
        LCD_DC_RESET();
        SPI_TxData_Polling(&index,1);
        LCD_CS_SET();
}
3)写数据函数
void LCD_WriteData(uint8_t *Buffer, uint8_t Length)
{
        LCD_CS_RESET();
        LCD_DC_SET();
        SPI_TxData_Polling(Buffer,Length);
        LCD_CS_SET();
}
4)还可以写发送8位数据或16位数据函数,便于其他函数调用
void LCD_SendData8(uint8_t dat)
{
        SPI_TxData_Polling(&dat,1);
}

void LCD_SendData16(uint16_t dat)
{
        LCD_SendData8(dat>>8);
        LCD_SendData8(dat);
}
5)写命令然后接着写不定长数据函数
void ili9341_SendData(uint8_t cmd, uint8_t *data, uint16_t length)
{
        LCD_WriteIndex(cmd);
        LCD_WriteData(data,length);
}


6)ILI9341初始化
定义一个数据结构,用来传送LCD初始化参数
typedef struct {
        uint8_t cmd;
        uint8_t dat[16];
        uint16_t datLen;
        uint32_t sleep;
} ili9341_ini_str_t;


定义LCD初始化参数
static ili9341_ini_str_t lcd_ini_str[] = {
    // ILI9341 初始化序列
    {0xCF, {0x00, 0x83, 0x30}, 3, 0},      // Power Control B
    {0xED, {0x64, 0x03, 0x12, 0x81}, 4, 0}, // Power On Sequence Control
    {0xE8, {0x85, 0x01, 0x79}, 3, 0},      // Driver Timing Control A
    {0xCB, {0x39, 0x2C, 0x00, 0x34, 0x02}, 5, 0}, // Power Control A
    {0xF7, {0x20}, 1, 0},                  // Pump Ratio Control
    {0xEA, {0x00, 0x00}, 2, 0},            // Driver Timing Control B
    {0xC0, {0x26}, 1, 0},                  // Power Control 1 (VRH[5:0])
    {0xC1, {0x11}, 1, 0},                  // Power Control 2 (SAP[2:0], BT[3:0])
    {0xC5, {0x35, 0x3E}, 2, 0},            // VCOM Control 1
    {0xC7, {0xBE}, 1, 0},                  // VCOM Control 2
    {0x36, {0xc8}, 1, 0},                  // Memory Access Control (MX, MY, RGB mode)
    {0x3A, {0x55}, 1, 0},                  // Pixel Format (16-bit/pixel)
    {0xB1, {0x00, 0x18}, 2, 0},            // Frame Rate Control (Normal mode, 70Hz)
    {0xB6, {0x0A, 0xA2}, 2, 0},            // Display Function Control
    {0xF6, {0x01, 0x30}, 2, 0},            // Interface Control
    {0xF2, {0x00}, 1, 0},                  // 3Gamma Function Disable
    {0x26, {0x01}, 1, 0},                  // Gamma Set (Gamma curve 1)
    {0xE0, {0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00}, 15, 0}, // Positive Gamma Correction
    {0xE1, {0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F}, 15, 0}, // Negative Gamma Correction
    {0x11, {0x00}, 0, 120},                // Sleep Out (延迟120ms)
    {0x29, {0x00}, 0, 50},                 // Display On (延迟50ms)
    {0x00, {0x00}, 0, 0}                   // End Of List
};
发送初始化参数:
void ili9341_SendInitStr()
{
        uint16_t i = 0;
        LCD_CS_RESET();
       
        while(lcd_ini_str[i].cmd != 0x00)
        {
                        uint8_t cmd = lcd_ini_str[i].cmd;
                        uint16_t datLen = lcd_ini_str[i].datLen;
                        uint8_t *dat;
                        uint32_t slp = lcd_ini_str[i].sleep;

                        dat = &(lcd_ini_str[i].dat[0]);
                       
                        LCD_DC_RESET();
                        SPI_TxData_Polling(&cmd, 1);

                        if(datLen > 0)
                        {
                                        LCD_DC_SET();
                                        SPI_TxData_Polling(dat, datLen);
                        }
                        if(slp > 0)
                        {
                                        PLATFORM_DelayMS(slp);
                        }
                        i++;
        }
        LCD_CS_SET();
}
7)ili9341_SetWindow
void ili9341_SetWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
{
        /* CASET */
        uint8_t cmd = 0x2A;
        uint8_t buf4[4];
       
        buf4[0] = (x >> 8) & 0xFF;
        buf4[1] = x & 0xFF;
        buf4[2] = ((x + w -1) >> 8) & 0xFF;
        buf4[3] = (x + w -1) & 0xFF;
        ili9341_SendData(cmd, buf4, 4);

        /* RASET */
        cmd = 0x2B;
        buf4[0] = (y >> 8) & 0xFF;
        buf4[1] = y & 0xFF;
        buf4[2] = ((y + h - 1) >> 8) & 0xFF;
        buf4[3] = (y + h - 1) & 0xFF;
        ili9341_SendData(cmd, buf4, 4);
       

        LCD_WriteIndex(0x2C);
}
8)画点
void setPixel(uint16_t Color)
{
        LCD_CS_RESET();
        LCD_DC_SET();
        LCD_SendData16(Color);
        LCD_CS_SET();
}
9)输出字符串
void GUI_Write16CnChar(uint16_t x, uint16_t y, const uint8_t *cn, uint16_t wordColor, uint16_t backColor) 
{
    uint8_t i, j, wordNum, wordByte;
    uint8_t color;
    uint16_t currentX = x;
    uint16_t currentY = y;
   
    while (*cn != '\0')
    {
        // Handle display boundaries and wrapping
        if (currentX >= X_MAX_PIXEL)
        {
            currentX = 0;
            currentY += 16;
        }
        if (currentY >= Y_MAX_PIXEL)
        {
            currentX = 0;
            currentY = 0;
            GUI_Clear(backColor);
        }
        
        // Handle ASCII characters (8x16)
        if (*cn < 128)
        {
            // Check for newline characters
            if (*cn == '\r' || *cn == '\n')
            {
                return;
            }
            
            wordNum = *cn - 32;
            ili9341_SetWindow(currentX, currentY, 8,  16);

            for (wordByte = 0; wordByte < 16; wordByte++)
            {
                color = Font_Data[wordNum].dat[wordByte];
                for (j = 0; j < 8; j++)
                {
                    setPixel((color & 0x80) ? wordColor : backColor);
                    color <<= 1;
                }
            }
            
            cn++;
            currentX += 8;
        }
        // Handle Chinese characters (16x16)
        else
        {
            ili9341_SetWindow(currentX, currentY,  16,  16);
            
            // Search for the character in the font database
            for (wordNum = 0; wordNum <(sizeof(CnChar16x16) / sizeof(CnChar16x16[0])); wordNum++)
            {
                if (CnChar16x16[wordNum].Index[0] == cn[0] &&
                    CnChar16x16[wordNum].Index[1] == cn[1])
                {
                    // Draw the character
                    for (i = 0; i < 32; i++)
                    {
                        color = CnChar16x16[wordNum].Msk[i];
                        for (j = 0; j < 8; j++)
                        {
                            setPixel((color & 0x80) ? wordColor : backColor);
                            color <<= 1;
                        }
                    }
                    break; // Exit loop once character is found
                }
            }
            
            cn += 2;
            currentX += 16;
        }
    }
}
10)测试一下

void SPI_Master_LCD_Sample(void)
{
       
        char buf[16];
       
                SPI_Configure();
       
                //配置RTC
                RTC_Configure();
                ili9341_Init();

                GUI_Clear(COLOR_Blue);
                GUI_Write16CnChar(10,16,"灵动微F0121开发板",COLOR_White,COLOR_Blue);
                GUI_WriteASCII8x16(10,40,"Hello 21ic",COLOR_White,COLOR_Blue);
                GUI_WriteASCII8x16(10,56,"Hello MM32F0121",COLOR_White,COLOR_Blue);
               
          while (1)
    {       
                                sprintf(buf," -= %02d:%02d:%02d =- ",RTC_Calendar.hour,RTC_Calendar.minute,RTC_Calendar.second);
        GUI_WriteASCII8x16(10,80,buf,COLOR_White,COLOR_Blue);
    }
}




使用特权

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

本版积分规则

71

主题

132

帖子

3

粉丝