en-us--EC_M031_PDMA_FLASH_TO_TFT_V1.00.zip
(1.16 MB)
本范例代码使用 M031KIAAE的SPI、USPI外设 PDMA功能将SPI FLASH中图片数据搬移到TFT屏进行显示。
首先通过SPI外设将图片数据写入到FLASH中,然后通过SPI外设RX PDMA功能将FLASH中图片数据传输到USPI外设TX,由TX驱动TFT屏进行显示。
320x240 TFT以RGB565模式显示,显示一屏图片需要153600 (320*240*2) 个字节数据。FLASH (W25Q16) 存储容量为2M Bytes,上电后将图片数据保存到FLASH。M031KIAAE通过SPI与FLASH进行通信,通过USPI与TFT控制芯片 (ILI9341) 进行通信。SPI外设及USPI外设都支持PDMA功能。通过使能SPI 外设RX PDMA功能实现将FLASH中的图片数据直接传输到USPI外设TX,由TX驱动TFT进行显示。除了设置FLASH 读数据起始地址和设置TFT显示初始位置之外,其他数据传输都由PDMA完成,进而提升CPU的运行效率。
图片数据数组定义,代码位于 Image.c。
const uint8_t u8Image[DATA_PER_SCREEN] = { 0XF7,0XBE,0XF7,0XDE,0XF7,0XDE,0XF7,0XDE,0XF7,0XDE,0XF7,0XBE,0XF7,0XBE,0XF7,0XBE, 0XEF,0X9D,0XE7,0X7C,0XE7,0X7C,0XE7,0X7C,0XE7,0X7C,0XE7,0X7C,0XE7,0X7C,0XE7,0X7C, 0XE7,0X7C,0XE7,0X7C,0XE7,0X7C,0XE7,0X9C,0XE7,0X9D,0XE7,0X9D,0XE7,0X9C,0XE7,0X9C, 0XE7,0X7C,0XE7,0X9C,0XE7,0X9C,0XE7,0X9D,0XE7,0X9D,0XE7,0X9D,0XE7,0X9D,0XEF,0X9D, 0XEF,0X9D,0XEF,0X9E,0XEF,0XBD,0XEF,0XBE,0XEF,0XBE,0XEF,0XBD,0XEF,0XBD,0XEF,0X9D, …… 0X3B,0X67,0X32,0XC6,0X2A,0X24,0X19,0X83,0X19,0X83,0X21,0XA4,0X2A,0X05,0X2A,0X25, 0X21,0XE4,0X21,0XC3,0X21,0XC3,0X21,0X83,0X19,0X43,0X21,0X63,0X2A,0X04,0X32,0X45, 0X2A,0X04,0X32,0X65,0X3A,0XC7,0X2A,0X25,0X19,0X83,0X21,0XC4,0X29,0XE5,0X21,0XA4, 0X19,0X43,0X19,0X43,0X21,0X83,0X21,0X63,0X19,0X22,0X19,0X02,0X10,0XE2,0X10,0XC2, 0X10,0XC2,0X10,0XC2,0X10,0XC2,0X10,0XC2,0X10,0XC2,0X10,0XC2,0X10,0XC2,0X10,0XA2, };
#define DATA_PER_SCREEN (153600UL)
主函数包括对系统,对UART0、USCI_SPI0、PWM、SPI0,对TFT驱动芯片,对SPI FLASH,对Timer0初始化,最后执行主循环。代码位于main函数。
主循环函数,用于实现将FLASH中数据搬移到TFT显示。当一屏数据传输完成后,串口输出传输所花的时间。代码位于 Copy_FlashData_To_TFT_WithPDMA 函数。
/* Data read from FLASH is sent to TFT via PDMA */
void Copy_FlashData_To_TFT_WithPDMA(void)
{
uint32_t u32RegValue, u32Abort;
/* Data Width is swtich to 8 Bits */
Switch_DataWidth_Is_Byte(TRUE);
/* Reset LCD display position */
LCD_Set_Pos();
/* Reset read start address */
SpiFlash_SetReadAddress(0x000000);
/* Data Width is swtich to 16 Bits */
Switch_DataWidth_Is_Byte(FALSE);
/* Init PDMA */
PDMA_Init();
/* CS = 0 : select chip */
USPI_CS_CLR;
/* DC = 1 : write data */
LCM_DC_SET;
/* Enable SPI RX PDMA function */
SPI_TRIGGER_RX_PDMA(SPI0);
while (1)
{
/* Get interrupt status */
u32RegValue = PDMA_GET_INT_STATUS(PDMA);
/* Check the PDMA transfer done interrupt flag */
if (u32RegValue &PDMA_INTSTS_TDIF_Msk)
{
/* Check the PDMA transfer done flags */
if ((PDMA_GET_TD_STS(PDMA) & (0x01 << SPI_MASTER_RX_DMA_CH)) ==
(0x01 << SPI_MASTER_RX_DMA_CH))
{
/* Clear the PDMA transfer done flags */
PDMA_CLR_TD_FLAG(PDMA, (0x01 << SPI_MASTER_RX_DMA_CH));
/* Enable SPI RX PDMA function */
SPI_TRIGGER_RX_PDMA(SPI0);
u8TransferCnt++;
if (u8TransferCnt >= TRANSFER_TIMES)
{
u8TransferCnt = 0;
printf("\nIt takes %d ms to transmit a screen of data. \n",
(u8TimerCnt*10));
/* Disable Timer0 NVIC */
NVIC_DisableIRQ(TMR0_IRQn);
while (1);
}
}
}
/* Check the DMA transfer abort interrupt flag */
if (u32RegValue &PDMA_INTSTS_ABTIF_Msk)
{
/* Get the target abort flag */
u32Abort = PDMA_GET_ABORT_STS(PDMA);
/* Clear the target abort flag */
PDMA_CLR_ABORT_FLAG(PDMA, u32Abort);
printf("\nabort happen\n");
}
/* Check the DMA time-out interrupt flag */
if (u32RegValue &(PDMA_INTSTS_REQTOF0_Msk | PDMA_INTSTS_REQTOF1_Msk))
{
/* Clear the time-out flag */
PDMA->INTSTS = u32RegValue &(PDMA_INTSTS_REQTOF0_Msk | PDMA_INTSTS_REQTOF1_Msk);
printf("\ntime-out happen\n");
}
}
}
设置命令的时候将传输位宽设为8 bits,传输数据的时候将位宽设置为16 bits。16 bits传输位宽比8 bits传输位宽更高效。代码位于 Switch_DataWidth_Is_Byte函数。
void Switch_DataWidth_Is_Byte(_Bool bFlag)
{
if (bFlag)
{
/* USPI0 Transfer Data width is 8 Bits */
USPI0->LINECTL &= ~USPI_LINECTL_DWIDTH_Msk;
USPI0->LINECTL |= (0x08 << USPI_LINECTL_DWIDTH_Pos);
/* SPI0 Transfer Unit is 8 Bits*/
SPI0->CTL &= ~SPI_CTL_DWIDTH_Msk;
SPI0->CTL |= (0x08 << SPI_CTL_DWIDTH_Pos);
}
else
{
/* USPI0 Transfer Data width is 16 Bits */
USPI0->LINECTL &= ~USPI_LINECTL_DWIDTH_Msk;
USPI0->LINECTL |= (0 << USPI_LINECTL_DWIDTH_Pos);
/* SPI0 Transfer Unit is 16 Bits */
SPI0->CTL &= ~SPI_CTL_DWIDTH_Msk;
SPI0->CTL |= (0x10 << SPI_CTL_DWIDTH_Pos);
}
}
Timer0初始化,设置每隔10 ms进入一次中断,计数器初始化为0,代码位于Timer0_Init函数中。定时器中断函数,每进入中断对计数器进行加1,代码位于 TMR0_IRQHandler函数。
void Timer0_Init(void)
{
/* Open Timer0 in periodic mode, enable interrupt
and 1 interrupt tick per 10 millisecond */
TIMER_Open(TIMER0, TIMER_PERIODIC_MODE, 100);
TIMER_EnableInt(TIMER0);
/* Start Timer0 counting */
TIMER_Start(TIMER0);
/* Clear timer counter */
u8TimerCnt = 0;
/* Enable Timer0 NVIC */
NVIC_EnableIRQ(TMR0_IRQn);
}
void TMR0_IRQHandler(void)
{
if (TIMER_GetIntFlag(TIMER0) == 1)
{
/* Clear Timer0 time-out interrupt flag */
TIMER_ClearIntFlag(TIMER0);
/* Timer counter plus one */
u8TimerCnt++;
}
}
因为TFT以RGB565模式显示,一个像素点需要16 bits来表示,且MCU与TFT之间传输基本单位也是16 bits,因此传输一屏图片即320x240像素点需要传输76800x16 bits数据。因为一个PDMA table 传输数量是用16 bits定义,最大传输数据为65536 x16 bits,因此传输一屏图片需要分2次传输,分别传输 38400 x16 bits数据。PDMA操作模式设置为Scatter-Gather。传输数量设置为38400。传输位宽设置为16 bits。传输类型设置为signal。源地址固定为SPI0 RX寄存器,目标地址固定为USPI0 TX寄存器。代码位于PDMA_Init函数。
void PDMA_Init(void)
{
/* Descriptor table 1 configuration */
DMA_DESC[0].ctl = ((TRANSFER_LEN - 1) << PDMA_DSCT_CTL_TXCNT_Pos) | PDMA_WIDTH_16 | PDMA_SAR_FIX | PDMA_DAR_FIX | PDMA_REQ_SINGLE | PDMA_OP_SCATTER;
DMA_DESC[0].src = (uint32_t) &SPI0->RX;
DMA_DESC[0].dest = (uint32_t) &USPI0->TXDAT;
DMA_DESC[0].offset = (uint32_t) &DMA_DESC[1] - (PDMA->SCATBA);
/* Descriptor table 2 configuration */
DMA_DESC[1].ctl = ((TRANSFER_LEN - 1) << PDMA_DSCT_CTL_TXCNT_Pos) | PDMA_WIDTH_16 | PDMA_SAR_FIX | PDMA_DAR_FIX | PDMA_REQ_SINGLE | PDMA_OP_BASIC;
DMA_DESC[1].src = (uint32_t) &SPI0->RX;
DMA_DESC[1].dest = (uint32_t) &USPI0->TXDAT;
DMA_DESC[1].offset = 0;
/* Open PDMA channel 1 for SPI RX */
PDMA_Open(PDMA, (1 << SPI_MASTER_RX_DMA_CH));
/* Configure PDMA transfer mode */
PDMA_SetTransferMode(PDMA, SPI_MASTER_RX_DMA_CH, PDMA_SPI0_RX, 1, (uint32_t) &DMA_DESC[0]);
}
|
共1人点赞
|