前言
恩智浦“FRDM-MCXN947”评测活动由安富利和与非网协同举办。本篇内容由与非网用户发布,已获转载许可。原文可在与非网(eefocus)工程师社区查看。
TFT LCD模组介绍
模组名字为SPI_Module_MSP3323,驱动芯片为ILI9341,屏幕尺寸为240x320像素,自带GRAM。通过4线SPI驱动,可以发送数据和命令也可以读取屏幕的IC参数甚至像素点颜色。
模组背面
模组管脚
MCXN947的接口
当前只移植显示接口,只需要关注显示屏相关的引脚。
MCXN947驱动TFT LCD的方式为4线SPI,选择FLEXIO_SPI_EDMA方式驱动。FLEXIO_SPI相关的管脚为如下的FLEXIO_Dxx。
FLEXIO介绍
MCXN947只有一个FLEXIO模块,即FLEXIO0。FLEXIO是一个高度可配置的模块,它提供了:
模拟各种串行或者并行通信协议;
灵活的16位定时器,支持各种触发、复位、使能和禁用条件;
可编程逻辑块,允许实现片上的数字逻辑功能,并且可配置内部和外部模块的交互;
可编程状态机,用于从CPU中卸载基本的系统控制功能;
FLEXIO框图
下图提供了FLEXIO计时器和移位寄存器配置的高层次总览。
FLEXIO使用移位器、计时器和外部触发器来讲数据移入或移除FLEXIO。如方框图所示,计时器控制这个数据移位的时间,您可以将计时器配置为使用通用计时器功能,外部触发器或各种其他条件来决定控制逻辑。
FLEXIO特性
1.具有传输、接收、数据匹配、逻辑和状态模式的32位移位寄存器阵列:
支持连续数据传输的双缓冲移位操作;
支持大块数据传输的移位连接;
支持自动启动和停止位生成;
1,2,4,8,16或者32为多移位宽度的并行接口支持;
中断,DMA或者轮询传输和接收操作;
2.高度灵活的16位定时器,支持各种内部或者外部触发,重置,启用和禁用条件:
可编程波特率独立于总线时钟频率,并支持在停止模式器件的异步操作;
可编程逻辑模式,用于集成外部数字逻辑功能,或组合引脚,移位器,或定时器功能,以产生复杂的输出;
可编程状态机,用于从CPU卸载基本的系统控制功能,支持最多8个状态,8个输出,每个状态和3个可选输入;
3.集成的通用I/O寄存器和引脚上升或下降的边缘中断,以简化软件支持;
4.支持广泛的协议,包括但不限于:I2C,SPI,I2S,Camera IF,Motorola 68K或Intel 8080 bus,PWM波形发生器,输入捕获(脉冲边缘间隔测量),如SENT
管脚配置
当前只需要点亮LCD,只需要配置显示相关的管脚,如下图所示:
在MCUXpress Config Tools中新建一个功能组,命名为TFT_LCD_Init,
设置管脚路由信息,并修改各个管脚的标志符;
其中LCD_RST,LCD_RS,LCD_LED都是GPIO Output,分别对应J3.1,J3.3,J3.5。
FLEXIO模拟的SPI管脚分别是J8.27,J8.28,J8.26,J8.25。
移植
01
移植思路
SPI接口驱动TFT LCD主要设计到3个控制管脚的输出,SPI发送、读取8比特数据。
02
3个GPIO控制管脚
都初始化为GPIO OUTPUT。
其中LCD_LED是背光管脚,拉高即点亮屏幕,拉低熄屏。当前只需要初始化时输出高电平即可;
其中LCD_RS是数据、命令选择功能,需要提供管脚的电平设置功能,提供两个宏定义即可;
其中LCD_RST是LCD复位管脚,输出低电平表示复位,提供两个宏定义即可;
03
SPI管脚
初始化FLEXIO0的4个管脚,然后初始化FLEXIO_SPI,并关联这4个管脚。
关键代码
01
GPIO控制管脚
下面的port_LCD_CtrlPin_Init()函数是我新增的,仅仅设置LCD_LED输出高电平,因为这3个管脚的初始化已经由MCUXpresso Config Tools配置好并生成了初始化代码,见下方的TFT_LCD_Init()函数。
(滑动查看)
/** *@briefLCD 控制管脚初始化 * LCD_RST --> J3.1 P2_0 复位管脚:低电平复位 * LCD_RS --> J3.3 P1_22 命令数据选择管脚:高电平--数据;低电平--命令 * LCD_LED --> J3.5 P2_3 背光管脚:高电平点亮,也可以 PWM 调节亮度 * *@param */ voidport_LCD_CtrlPin_Init(void) { // 这3个管脚已经在 BOARD_InitBootPins() 中初始化了 LCD_LED(1); }
(滑动查看)
/* FUNCTION************************************************************************************************************ * * Function Name : TFT_LCD_Init * Description : Configures pin routing and optionally pin electrical features. * * END****************************************************************************************************************/ void TFT_LCD_Init(void) { /* Enables the clock for GPIO1: Enables clock*/ CLOCK_EnableClock(kCLOCK_Gpio1); /* Enables the clock for GPIO2: Enables clock*/ CLOCK_EnableClock(kCLOCK_Gpio2); /* Enables the clock for PORT1: Enables clock*/ CLOCK_EnableClock(kCLOCK_Port1); /* Enables the clock for PORT2: Enables clock*/ CLOCK_EnableClock(kCLOCK_Port2); /* Enables the clock for PORT4: Enables clock*/ CLOCK_EnableClock(kCLOCK_Port4); gpio_pin_config_t LCD_RS_config= { .pinDirection = kGPIO_DigitalOutput, .outputLogic = 0U }; /* Initialize GPIO functionality on pin PIO1_22 (pin L4) */ GPIO_PinInit(TFT_LCD_LCD_RS_GPIO, TFT_LCD_LCD_RS_PIN, &LCD_RS_config); gpio_pin_config_t LCD_RST_config= { .pinDirection = kGPIO_DigitalOutput, .outputLogic = 0U }; /* Initialize GPIO functionality on pin PIO2_0 (pin H2) */ GPIO_PinInit(TFT_LCD_LCD_RST_GPIO, TFT_LCD_LCD_RST_PIN, &LCD_RST_config); gpio_pin_config_t LCD_LED_config= { .pinDirection = kGPIO_DigitalOutput, .outputLogic = 0U }; /* Initialize GPIO functionality on pin PIO2_3 (pin J3) */ GPIO_PinInit(TFT_LCD_LCD_LED_GPIO, TFT_LCD_LCD_LED_PIN, &LCD_LED_config); /* PORT1_22 (pin L4) is configured as PIO1_22*/ PORT_SetPinMux(TFT_LCD_LCD_RS_PORT, TFT_LCD_LCD_RS_PIN, kPORT_MuxAlt0); PORT1->PCR[22] = ((PORT1->PCR[22] & /* Mask bits to zero which are setting*/ (~(PORT_PCR_DSE_MASK|PORT_PCR_IBE_MASK))) /* Drive Strength Enable: High.*/ |PORT_PCR_DSE(PCR_DSE_dse1) /* Input Buffer Enable: Enables.*/ |PORT_PCR_IBE(PCR_IBE_ibe1)); /* PORT2_0 (pin H2) is configured as PIO2_0*/ PORT_SetPinMux(TFT_LCD_LCD_RST_PORT, TFT_LCD_LCD_RST_PIN, kPORT_MuxAlt0); PORT2->PCR[0] = ((PORT2->PCR[0] & /* Mask bits to zero which are setting*/ (~(PORT_PCR_DSE_MASK|PORT_PCR_IBE_MASK))) /* Drive Strength Enable: High.*/ |PORT_PCR_DSE(PCR_DSE_dse1) /* Input Buffer Enable: Enables.*/ |PORT_PCR_IBE(PCR_IBE_ibe1)); /* PORT2_3 (pin J3) is configured as PIO2_3*/ PORT_SetPinMux(TFT_LCD_LCD_LED_PORT, TFT_LCD_LCD_LED_PIN, kPORT_MuxAlt0); PORT2->PCR[3] = ((PORT2->PCR[3] & /* Mask bits to zero which are setting*/ (~(PORT_PCR_DSE_MASK|PORT_PCR_IBE_MASK))) /* Drive Strength Enable: High.*/ |PORT_PCR_DSE(PCR_DSE_dse1) /* Input Buffer Enable: Enables.*/ |PORT_PCR_IBE(PCR_IBE_ibe1)); /* PORT4_20 (pin T10) is configured as FLEXIO0_D28*/ PORT_SetPinMux(TFT_LCD_LCD_MOSI_PORT, TFT_LCD_LCD_MOSI_PIN, kPORT_MuxAlt6); PORT4->PCR[20] = ((PORT4->PCR[20] & /* Mask bits to zero which are setting*/ (~(PORT_PCR_DSE_MASK|PORT_PCR_IBE_MASK))) /* Drive Strength Enable: High.*/ |PORT_PCR_DSE(PCR_DSE_dse1) /* Input Buffer Enable: Enables.*/ |PORT_PCR_IBE(PCR_IBE_ibe1)); /* PORT4_21 (pin T11) is configured as FLEXIO0_D29*/ PORT_SetPinMux(TFT_LCD_LCD_MISO_PORT, TFT_LCD_LCD_MISO_PIN, kPORT_MuxAlt6); PORT4->PCR[21] = ((PORT4->PCR[21] & /* Mask bits to zero which are setting*/ (~(PORT_PCR_DSE_MASK|PORT_PCR_IBE_MASK))) /* Drive Strength Enable: High.*/ |PORT_PCR_DSE(PCR_DSE_dse1) /* Input Buffer Enable: Enables.*/ |PORT_PCR_IBE(PCR_IBE_ibe1)); /* PORT4_22 (pin T12) is configured as FLEXIO0_D30*/ PORT_SetPinMux(TFT_LCD_LCD_SCK_PORT, TFT_LCD_LCD_SCK_PIN, kPORT_MuxAlt6); PORT4->PCR[22] = ((PORT4->PCR[22] & /* Mask bits to zero which are setting*/ (~(PORT_PCR_DSE_MASK|PORT_PCR_IBE_MASK))) /* Drive Strength Enable: High.*/ |PORT_PCR_DSE(PCR_DSE_dse1) /* Input Buffer Enable: Enables.*/ |PORT_PCR_IBE(PCR_IBE_ibe1)); /* PORT4_23 (pin U12) is configured as FLEXIO0_D31*/ PORT_SetPinMux(TFT_LCD_LCD_CS_PORT, TFT_LCD_LCD_CS_PIN, kPORT_MuxAlt6); PORT4->PCR[23] = ((PORT4->PCR[23] & /* Mask bits to zero which are setting*/ (~(PORT_PCR_DSE_MASK|PORT_PCR_IBE_MASK))) /* Drive Strength Enable: High.*/ |PORT_PCR_DSE(PCR_DSE_dse1) /* Input Buffer Enable: Enables.*/ |PORT_PCR_IBE(PCR_IBE_ibe1)); }
并提供宏定义实现GPIO管脚的输出高低电平功能:
(滑动查看)
// 硬件 CS,此处忽略 #defineLCD_CS_SET #defineLCD_CS_CLR // 数据、命令选择 #defineLCD_RS_SETGPIO_PinWrite(TFT_LCD_LCD_RS_GPIO, TFT_LCD_LCD_RS_GPIO_PIN, 1) #defineLCD_RS_CLRGPIO_PinWrite(TFT_LCD_LCD_RS_GPIO, TFT_LCD_LCD_RS_GPIO_PIN, 0) #defineLCD_RST_SETGPIO_PinWrite(TFT_LCD_LCD_RST_GPIO, TFT_LCD_LCD_RST_GPIO_PIN, 1) #defineLCD_RST_CLRGPIO_PinWrite(TFT_LCD_LCD_RST_GPIO, TFT_LCD_LCD_RST_GPIO_PIN, 0)
02
FLEXIO_SPI初始化
对应的管脚路由已经在TFT_LCD_Init()中配置好了,此处仅需要初始化FLEXIO_SPI并关联对应的4个管脚。
03
全局宏定义和变量
(滑动查看)
/******************************************************************************* * Definitions ******************************************************************************/ #define BOARD_FLEXIO_BASE (FLEXIO0) #define FLEXIO_SPI_MOSI_PIN 28U #define FLEXIO_SPI_MISO_PIN 29U #define FLEXIO_SPI_SCK_PIN 30U #define FLEXIO_SPI_CSn_PIN 31U #define FLEXIO_CLOCK_FREQUENCYCLOCK_GetFlexioClkFreq() #define EXAMPLE_FLEXIO_SPI_DMA_BASEADDRDMA0 #define FLEXIO_SPI_TX_DMA_CHANNEL (0U) #define FLEXIO_SPI_RX_DMA_CHANNEL (1U) #define FLEXIO_TX_SHIFTER_INDEX 0U #define FLEXIO_RX_SHIFTER_INDEX 2U #define EXAMPLE_TX_DMA_SOURCE kDma0RequestMuxFlexIO0ShiftRegister0Request #define EXAMPLE_RX_DMA_SOURCE kDma0RequestMuxFlexIO0ShiftRegister2Request #define FLEXIO_SPI_BAUD_HIGH (1000000*25) #define FLEXIO_SPI_BAUD_LOW (1000000*1) /******************************************************************************* * Variables ******************************************************************************/ static flexio_spi_master_edma_handle_t g_spiHandle; static edma_handle_t txHandle; static edma_handle_t rxHandle; FLEXIO_SPI_Type spiDev; flexio_spi_master_config_t userConfig; volatile bool completeFlag =false; static void spi_master_completionCallback(FLEXIO_SPI_Type*base, flexio_spi_master_edma_handle_t *handle, status_t status, void *userData) { if (status == kStatus_Success) { completeFlag =true; } }
04
FLEXIO_SPI初始化函数
初始化FLEXIO_SPI并初始化EDMA。
(滑动查看)
/** * @brief SPI 初始化 * */ voidport_LCD_SPI_Init(void) { uint8_ti =0; uint8_terr =0; dma_request_source_tdma_request_source_tx; dma_request_source_tdma_request_source_rx; edma_config_tconfig; /* attach PLL0 to FLEXIO */ CLOCK_SetClkDiv(kCLOCK_DivFlexioClk,1u); CLOCK_AttachClk(kPLL0_to_FLEXIO); /* Init FlexIO SPI. */ /* * userConfig.enableMaster = true; * userConfig.enableInDoze = false; * userConfig.enableInDebug = true; * userConfig.enableFastAccess = false; * userConfig.baudRate_Bps = 500000U; * userConfig.phase = kFLEXIO_SPI_ClockPhaseFirstEdge; * userConfig.dataMode = kFLEXIO_SPI_8BitMode; */ FLEXIO_SPI_MasterGetDefaultConfig(&userConfig); //NOTE:此处修改 SPI 通信速率 userConfig.baudRate_Bps =FLEXIO_SPI_BAUD_HIGH; spiDev.flexioBase =BOARD_FLEXIO_BASE; spiDev.SDOPinIndex=FLEXIO_SPI_MOSI_PIN; spiDev.SDIPinIndex=FLEXIO_SPI_MISO_PIN; spiDev.SCKPinIndex=FLEXIO_SPI_SCK_PIN; spiDev.CSnPinIndex=FLEXIO_SPI_CSn_PIN; spiDev.shifterIndex[0] =FLEXIO_TX_SHIFTER_INDEX; spiDev.shifterIndex[1] =FLEXIO_RX_SHIFTER_INDEX; spiDev.timerIndex[0] =0U; spiDev.timerIndex[1] =1U; dma_request_source_tx = (dma_request_source_t)EXAMPLE_TX_DMA_SOURCE; dma_request_source_rx = (dma_request_source_t)EXAMPLE_RX_DMA_SOURCE; #ifdefined(FSL_FEATURE_SOC_DMAMUX_COUNT) &&FSL_FEATURE_SOC_DMAMUX_COUNT /*Init EDMA for example.*/ DMAMUX_Init(EXAMPLE_FLEXIO_SPI_DMAMUX_BASEADDR); /* Request DMA channels for TX & RX. */ DMAMUX_SetSource(EXAMPLE_FLEXIO_SPI_DMAMUX_BASEADDR, FLEXIO_SPI_TX_DMA_CHANNEL, dma_request_source_tx); DMAMUX_SetSource(EXAMPLE_FLEXIO_SPI_DMAMUX_BASEADDR, FLEXIO_SPI_RX_DMA_CHANNEL, dma_request_source_rx); DMAMUX_EnableChannel(EXAMPLE_FLEXIO_SPI_DMAMUX_BASEADDR, FLEXIO_SPI_TX_DMA_CHANNEL); DMAMUX_EnableChannel(EXAMPLE_FLEXIO_SPI_DMAMUX_BASEADDR, FLEXIO_SPI_RX_DMA_CHANNEL); #endif EDMA_GetDefaultConfig(&config); EDMA_Init(EXAMPLE_FLEXIO_SPI_DMA_BASEADDR, &config); EDMA_CreateHandle(&txHandle, EXAMPLE_FLEXIO_SPI_DMA_BASEADDR, FLEXIO_SPI_TX_DMA_CHANNEL); EDMA_CreateHandle(&rxHandle, EXAMPLE_FLEXIO_SPI_DMA_BASEADDR, FLEXIO_SPI_RX_DMA_CHANNEL); #ifdefined(FSL_FEATURE_EDMA_HAS_CHANNEL_MUX) &&FSL_FEATURE_EDMA_HAS_CHANNEL_MUX EDMA_SetChannelMux(EXAMPLE_FLEXIO_SPI_DMA_BASEADDR, FLEXIO_SPI_TX_DMA_CHANNEL, dma_request_source_tx); EDMA_SetChannelMux(EXAMPLE_FLEXIO_SPI_DMA_BASEADDR, FLEXIO_SPI_RX_DMA_CHANNEL, dma_request_source_rx); #endif FLEXIO_SPI_MasterInit(&spiDev, &userConfig, FLEXIO_CLOCK_FREQUENCY); }
05
SPI传输函数
此处仅实现了单个字节的发送和接收函数,通过eDMA中断来判断收发是否完成。
(滑动查看)
uint8_tport_LCD_SPI_TxByte(uint8_tdata) { flexio_spi_transfer_txfer = {0}; uint8_treadBack =0; /* Send to slave. */ xfer.txData =&data xfer.rxData =&readBack xfer.dataSize =1; xfer.flags = kFLEXIO_SPI_8bitMsb; FLEXIO_SPI_MasterTransferCreateHandleEDMA(&spiDev, &g_spiHandle, spi_master_completionCallback,NULL, &txHandle, &rxHandle); FLEXIO_SPI_MasterTransferEDMA(&spiDev, &g_spiHandle, &xfer); while(!completeFlag); completeFlag =false; returnreadBack; }
06
LCD读写寄存器、数据函数
下面是LCD抽象层的函数,仅仅需要控制管脚输出高低电平、SPI传输单个字节即可。
(滑动查看)
/***************************************************************************** * @name :void LCD_WR_REG(uint8_t data) * @date :2018-08-09 * @function :Write an 8-bit command to the LCD screen * @parameters Command value to be written * @retvalue :None ******************************************************************************/ void LCD_WR_REG(uint8_t data) { LCD_CS_CLR; LCD_RS_CLR; port_LCD_SPI_TxByte(data); LCD_CS_SET; } /***************************************************************************** * @name :void LCD_WR_DATA(uint8_t data) * @date :2018-08-09 * @function :Write an 8-bit data to the LCD screen * @parameters data value to be written * @retvalue :None ******************************************************************************/ void LCD_WR_DATA(uint8_t data) { LCD_CS_CLR; LCD_RS_SET; port_LCD_SPI_TxByte(data); LCD_CS_SET; }
运行
屏幕成功点亮,也能显示中英文字符,绘制图片出了点差错,但是刷屏速度好慢。尝试提高了SPI速度,编译器优化等级debug/release都试过,刷屏的百叶窗效果还是很明显。