硬汉嵌入式论坛

 找回密码
 立即注册
查看: 847|回复: 7
收起左侧

[emWin] 请教:STM32F4 SPI1采用DMA发送的问题

[复制链接]

2

主题

22

回帖

28

积分

新手上路

积分
28
发表于 2024-6-20 21:42:00 | 显示全部楼层 |阅读模式
本帖最后由 zouhp 于 2024-6-20 22:01 编辑

我在STM32F412平台通过SPI1接口移植ILI9488液晶屏驱动的时候,采用SPI阻塞发送可以正常显示,但速度较慢。为了优化刷新速度,想改为DMA发送数据,却遇到很奇怪的问题!
思路是:
(1)定义一个数组Spi1DMABuf[480*3],液晶屏横向480点,每个点16位颜色值,SPI 需要传输3个字节,即这个数组存放的是矩形框某行的颜色数据,行最多480点。
(2)在矩形填充函数中,每行填充采用DMA方式以提高速度,一共循环调用Ysize次,Ysize是矩形框纵向点数,最大值为320。
问题:
  emWin的GUI_Clear()调用了矩形填充函数,我在该函数中首先初始化数组Spi1DMABuf[480*3],并循环Ysize次调用了  SPI1_DMA_Send()函数(一次显示一行,最多480点),结果发现只是Clear了若干行,而不是全部,真正成功的DMA发送次数并没有达到Ysize次。如果在循环调用函数SPI1_DMA_Send()之间插入一个延时则可以显示正常。
请大家帮分析看看,谢谢!

附上相关代码片段:



//===============================================
//    循环调用  SPI1_DMA_Send()
//===============================================
    for (i=0; i<Ysize; i++)
    {
           SPI1_DMA_Send(Spi1DMABuf,Xsize*3);
           //      for (j=0; j<4500; j++)    {   }   // 不注释掉则可以正常显示
               
    }




//===============================================
//    SPI1 DMA发送程序:一次显示一行,最多480点
//===============================================
void SPI1_DMA_Send(unsigned char *ptr, int size)
{
    OS_ERR err;
   
    // 等待信号量
    OSSemPend((OS_SEM *)&SPI1DMASem, (OS_TICK)0, (OS_OPT)OS_OPT_PEND_BLOCKING, (CPU_TS *)0, (OS_ERR *)&err);
   
    // 启动DMA传输
    HAL_SPI_Transmit_DMA(&SPI_Handler, Spi1DMABuf, size);
}





//===============================================
//    SPI1 DMA发送中断服务程序
//===============================================
void DMA2_Stream3_IRQHandler(void)
{
    OS_ERR err;
  
    HAL_DMA_IRQHandler(&hdma_spi1_tx);
   
    OSSemPost((OS_SEM *)&SPI1DMASem, (OS_OPT)OS_OPT_POST_1, (OS_ERR *)&err);
}




//===============================================
//    SPI1 初始化
//===============================================
void SPI1_Init(void)        
{
    OS_ERR err;
    GPIO_InitTypeDef    GPIO_Initure;
   
    /* DMA controller clock enable */
   __HAL_RCC_DMA2_CLK_ENABLE();

   /* DMA interrupt init */
   /* DMA2_Stream3_IRQn interrupt configuration */
   HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 0, 0);
   HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);

    // 使能时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_SPI1_CLK_ENABLE();
   
    // 初始化GPIO
    GPIO_Initure.Pin       = GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7;
    GPIO_Initure.Mode      = GPIO_MODE_AF_PP;
    GPIO_Initure.Pull      = GPIO_NOPULL;
    GPIO_Initure.Speed     = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_Initure.Alternate = GPIO_AF5_SPI1;
    HAL_GPIO_Init(GPIOA, &GPIO_Initure);
   
    // 初始化SPI1句柄
    SPI_Handler.Instance               = SPI1;
    SPI_Handler.Init.Mode              = SPI_MODE_MASTER;
    SPI_Handler.Init.Direction         = SPI_DIRECTION_2LINES;
    SPI_Handler.Init.DataSize          = SPI_DATASIZE_8BIT;
    SPI_Handler.Init.CLKPolarity       = SPI_POLARITY_LOW;
    SPI_Handler.Init.CLKPhase          = SPI_PHASE_1EDGE;
    SPI_Handler.Init.NSS               = SPI_NSS_HARD_OUTPUT;
    SPI_Handler.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
    SPI_Handler.Init.FirstBit          = SPI_FIRSTBIT_MSB;
    SPI_Handler.Init.TIMode            = SPI_TIMODE_DISABLE;
    SPI_Handler.Init.CRCCalculation    = SPI_CRCCALCULATION_DISABLE;
    SPI_Handler.Init.CRCPolynomial     = 10;
    HAL_SPI_Init(&SPI_Handler);
   
    // 使能SPI1
    __HAL_SPI_ENABLE(&SPI_Handler);
         
    /* SPI1 DMA Init */
    /* SPI1_TX Init */
    hdma_spi1_tx.Instance                 = DMA2_Stream3;
    hdma_spi1_tx.Init.Channel             = DMA_CHANNEL_3;
    hdma_spi1_tx.Init.Direction           = DMA_MEMORY_TO_PERIPH;
    hdma_spi1_tx.Init.PeriphInc           = DMA_PINC_DISABLE;
    hdma_spi1_tx.Init.MemInc              = DMA_MINC_ENABLE;
    hdma_spi1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_spi1_tx.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
    hdma_spi1_tx.Init.Mode                = DMA_NORMAL;
    hdma_spi1_tx.Init.Priority            = DMA_PRIORITY_HIGH;
    hdma_spi1_tx.Init.FIFOMode            = DMA_FIFOMODE_DISABLE;

    HAL_DMA_Init(&hdma_spi1_tx);
    __HAL_LINKDMA(&SPI_Handler, hdmatx, hdma_spi1_tx);
   
    /* 用于资源共享 */
    OSSemCreate((OS_SEM *)&SPI1DMASem,  (CPU_CHAR *)"SPI1DMASem",  (OS_SEM_CTR )1,  (OS_ERR *)&err);
}





回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115896
QQ
发表于 2024-6-21 09:33:59 | 显示全部楼层
先HAL_SPI_Transmit_DMA(&SPI_Handler, Spi1DMABuf, size);,然后 OSSemPost((OS_SEM *)&SPI1DMASem, (OS_OPT)OS_OPT_POST_1, (OS_ERR *)&err);

最后务必是在SPI传输完成回调里面调用OSSemPost((OS_SEM *)&SPI1DMASem, (OS_OPT)OS_OPT_POST_1, (OS_ERR *)&err);,不能直接这样放在中断里面,如果是其他中断标志不就出问题了。
回复

使用道具 举报

3

主题

71

回帖

80

积分

初级会员

积分
80
发表于 2024-6-21 10:27:18 | 显示全部楼层
发送这样写没问题,中断函数里面的信号量释放删掉,修改成在SPI的发送完成回调函数中释放信号量!void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
    if(hspi->Instance == SPI1){
        OSSemPost((OS_SEM *)&SPI1DMASem, (OS_OPT)OS_OPT_POST_1, (OS_ERR *)&err);
    }
}
回复

使用道具 举报

2

主题

22

回帖

28

积分

新手上路

积分
28
 楼主| 发表于 2024-6-21 10:47:42 | 显示全部楼层
eric2013 发表于 2024-6-21 09:33
先HAL_SPI_Transmit_DMA(&SPI_Handler, Spi1DMABuf, size);,然后 OSSemPost((OS_SEM *)&SPI1DMASem, (OS_O ...

嗯,有道理!我再试试。谢谢!
初始化的时候指定一个发送完成回调函数,在这个函数中调用OSSemPost。
回复

使用道具 举报

2

主题

22

回帖

28

积分

新手上路

积分
28
 楼主| 发表于 2024-6-23 21:26:17 | 显示全部楼层
跟着硬汉学 发表于 2024-6-21 10:27
发送这样写没问题,中断函数里面的信号量释放删掉,修改成在SPI的发送完成回调函数中释放信号量!void HAL_ ...

按照您的意见修改,问题仍未能解决!
STM32F4的DMA能否只开启传输完成这一个中断?其他的什么半传输完成、错误等等都关闭。
回复

使用道具 举报

21

主题

481

回帖

544

积分

金牌会员

积分
544
发表于 2024-6-23 23:58:22 | 显示全部楼层
void DMA2_Stream3_IRQHandler(void)
{
    OS_ERR err;
  
    HAL_DMA_IRQHandler(&hdma_spi1_tx);
   
    OSSemPost((OS_SEM *)&SPI1DMASem, (OS_OPT)OS_OPT_POST_1, (OS_ERR *)&err);
}
中断函数写法有问题,ISR的进入和退出要添加代码的
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115896
QQ
发表于 2024-6-24 08:53:52 | 显示全部楼层
zouhp 发表于 2024-6-23 21:26
按照您的意见修改,问题仍未能解决!
STM32F4的DMA能否只开启传输完成这一个中断?其他的什么半传输完成 ...

可以关掉。
回复

使用道具 举报

2

主题

22

回帖

28

积分

新手上路

积分
28
 楼主| 发表于 2024-6-24 11:09:28 | 显示全部楼层
h_007 发表于 2024-6-23 23:58
void DMA2_Stream3_IRQHandler(void)
{
    OS_ERR err;

您指的是下面这个吗,试过也是一样不行的。
void DMA2_Stream3_IRQHandler(void)
{
    // 进入临界区,通知内核进入了中断函数,不可打扰
    CPU_SR_ALLOC();
    CPU_CRITICAL_ENTER();
    OSIntEnter();                              // Tell uC/OS-III that we are starting an ISR
    CPU_CRITICAL_EXIT();

    // 中断服务处理
    HAL_DMA_IRQHandler(&hdma_spi1_tx);

    // Tell uC/OS-III that we are leaving the ISR
        OSIntExit();
}
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2025-5-25 18:45 , Processed in 0.384188 second(s), 25 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表