[DemoCode下载] PDMA搬移FLASH数据到TFT屏显示(SPI接口)

[复制链接]
 楼主| zhuotuzi 发表于 2024-8-21 16:08 | 显示全部楼层 |阅读模式
en-us--EC_M031_PDMA_FLASH_TO_TFT_V1.00.zip (1.16 MB, 下载次数: 3)

本范例代码使用 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的运行效率。

345366c59f467bdfc.png

图片数据数组定义,代码位于 Image.c。
  1. 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, };
  1. #define DATA_PER_SCREEN (153600UL)
主函数包括对系统,对UART0、USCI_SPI0、PWM、SPI0,对TFT驱动芯片,对SPI FLASH,对Timer0初始化,最后执行主循环。代码位于main函数。
1629666c59fa995ece.png
主循环函数,用于实现将FLASH中数据搬移到TFT显示。当一屏数据传输完成后,串口输出传输所花的时间。代码位于 Copy_FlashData_To_TFT_WithPDMA 函数。
  1. /* Data read from FLASH is sent to TFT via PDMA */
  2. void Copy_FlashData_To_TFT_WithPDMA(void)
  3. {
  4.     uint32_t u32RegValue, u32Abort;

  5.     /* Data Width is swtich to 8 Bits */
  6.     Switch_DataWidth_Is_Byte(TRUE);

  7.     /* Reset LCD display position */
  8.     LCD_Set_Pos();

  9.     /* Reset read start address */
  10.     SpiFlash_SetReadAddress(0x000000);

  11.     /* Data Width is swtich to 16 Bits */
  12.     Switch_DataWidth_Is_Byte(FALSE);

  13.     /* Init PDMA */
  14.     PDMA_Init();

  15.     /* CS = 0 : select chip */
  16.     USPI_CS_CLR;
  17.     /* DC = 1 : write data */
  18.     LCM_DC_SET;

  19.     /* Enable SPI RX PDMA function */
  20.     SPI_TRIGGER_RX_PDMA(SPI0);

  21.     while (1)
  22.     {
  23.         /* Get interrupt status */
  24.         u32RegValue = PDMA_GET_INT_STATUS(PDMA);

  25.         /* Check the PDMA transfer done interrupt flag */
  26.         if (u32RegValue &PDMA_INTSTS_TDIF_Msk)
  27.         {
  28.             /* Check the PDMA transfer done flags */
  29.             if ((PDMA_GET_TD_STS(PDMA) & (0x01 << SPI_MASTER_RX_DMA_CH)) ==
  30.                     (0x01 << SPI_MASTER_RX_DMA_CH))
  31.             {
  32.                 /* Clear the PDMA transfer done flags */
  33.                 PDMA_CLR_TD_FLAG(PDMA, (0x01 << SPI_MASTER_RX_DMA_CH));

  34.                 /* Enable SPI RX PDMA function */
  35.                 SPI_TRIGGER_RX_PDMA(SPI0);

  36.                 u8TransferCnt++;

  37.                 if (u8TransferCnt >= TRANSFER_TIMES)
  38.                 {
  39.                     u8TransferCnt = 0;

  40.                     printf("\nIt takes %d ms to transmit a screen of data. \n",
  41.                            (u8TimerCnt*10));

  42.                     /* Disable Timer0 NVIC */
  43.                     NVIC_DisableIRQ(TMR0_IRQn);

  44.                     while (1);
  45.                 }
  46.             }
  47.         }

  48.         /* Check the DMA transfer abort interrupt flag */
  49.         if (u32RegValue &PDMA_INTSTS_ABTIF_Msk)
  50.         {
  51.             /* Get the target abort flag */
  52.             u32Abort = PDMA_GET_ABORT_STS(PDMA);
  53.             /* Clear the target abort flag */
  54.             PDMA_CLR_ABORT_FLAG(PDMA, u32Abort);
  55.             printf("\nabort happen\n");
  56.         }

  57.         /* Check the DMA time-out interrupt flag */
  58.         if (u32RegValue &(PDMA_INTSTS_REQTOF0_Msk | PDMA_INTSTS_REQTOF1_Msk))
  59.         {
  60.             /* Clear the time-out flag */
  61.             PDMA->INTSTS = u32RegValue &(PDMA_INTSTS_REQTOF0_Msk | PDMA_INTSTS_REQTOF1_Msk);
  62.             printf("\ntime-out happen\n");
  63.         }
  64.     }
  65. }
设置命令的时候将传输位宽设为8 bits,传输数据的时候将位宽设置为16 bits。16 bits传输位宽比8 bits传输位宽更高效。代码位于 Switch_DataWidth_Is_Byte函数。
  1. void Switch_DataWidth_Is_Byte(_Bool bFlag)
  2. {
  3.     if (bFlag)
  4.     {
  5.         /* USPI0 Transfer Data width is 8 Bits */
  6.         USPI0->LINECTL &= ~USPI_LINECTL_DWIDTH_Msk;
  7.         USPI0->LINECTL |= (0x08 << USPI_LINECTL_DWIDTH_Pos);

  8.         /* SPI0 Transfer Unit is 8 Bits*/
  9.         SPI0->CTL &= ~SPI_CTL_DWIDTH_Msk;
  10.         SPI0->CTL |= (0x08 << SPI_CTL_DWIDTH_Pos);
  11.     }
  12.     else
  13.     {
  14.         /* USPI0 Transfer Data width is 16 Bits */
  15.         USPI0->LINECTL &= ~USPI_LINECTL_DWIDTH_Msk;
  16.         USPI0->LINECTL |= (0 << USPI_LINECTL_DWIDTH_Pos);

  17.         /* SPI0 Transfer Unit is 16 Bits */
  18.         SPI0->CTL &= ~SPI_CTL_DWIDTH_Msk;
  19.         SPI0->CTL |= (0x10 << SPI_CTL_DWIDTH_Pos);
  20.     }
  21. }
Timer0初始化,设置每隔10 ms进入一次中断,计数器初始化为0,代码位于Timer0_Init函数中。定时器中断函数,每进入中断对计数器进行加1,代码位于 TMR0_IRQHandler函数。
  1. void Timer0_Init(void)
  2. {
  3.     /* Open Timer0 in periodic mode, enable interrupt
  4.     and 1 interrupt tick per 10 millisecond */
  5.     TIMER_Open(TIMER0, TIMER_PERIODIC_MODE, 100);
  6.     TIMER_EnableInt(TIMER0);

  7.     /* Start Timer0 counting */
  8.     TIMER_Start(TIMER0);

  9.     /* Clear timer counter */
  10.     u8TimerCnt = 0;

  11.     /* Enable Timer0 NVIC */
  12.     NVIC_EnableIRQ(TMR0_IRQn);
  13. }
  1. void TMR0_IRQHandler(void)
  2. {
  3.     if (TIMER_GetIntFlag(TIMER0) == 1)
  4.     {
  5.         /* Clear Timer0 time-out interrupt flag */
  6.         TIMER_ClearIntFlag(TIMER0);

  7.         /* Timer counter plus one */
  8.         u8TimerCnt++;
  9.     }
  10. }
因为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函数。
  1. void PDMA_Init(void)
  2. {
  3.     /* Descriptor table 1 configuration */
  4.     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;
  5.     DMA_DESC[0].src = (uint32_t) &SPI0->RX;
  6.     DMA_DESC[0].dest = (uint32_t) &USPI0->TXDAT;
  7.     DMA_DESC[0].offset = (uint32_t) &DMA_DESC[1] - (PDMA->SCATBA);

  8.     /* Descriptor table 2 configuration */
  9.     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;
  10.     DMA_DESC[1].src = (uint32_t) &SPI0->RX;
  11.     DMA_DESC[1].dest = (uint32_t) &USPI0->TXDAT;
  12.     DMA_DESC[1].offset = 0;

  13.     /* Open PDMA channel 1 for SPI RX */
  14.     PDMA_Open(PDMA, (1 << SPI_MASTER_RX_DMA_CH));

  15.     /* Configure PDMA transfer mode */
  16.     PDMA_SetTransferMode(PDMA, SPI_MASTER_RX_DMA_CH, PDMA_SPI0_RX, 1, (uint32_t) &DMA_DESC[0]);
  17. }


呐咯密密 发表于 2024-8-22 16:25 | 显示全部楼层
很不错的案例
您需要登录后才可以回帖 登录 | 注册

本版积分规则

214

主题

3375

帖子

7

粉丝
快速回复 在线客服 返回列表 返回顶部