本文的目的:配置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);
}
}
|
|