裸机系列——IIS_DMA

本文总结了作者在裸机环境下调试IIS总线与DMA时遇到的五个问题,包括DMA物理地址的理解、串口发送接收十六进制数据、IIS无声音、J-Link调试与直接下载的差异以及声音杂音问题。作者通过自己的经验教训强调了注意编程细节、独立解决问题以及代码规范的重要性。

s3c2440的IIS总线已经弄了好久,今天大概可以做个总结了吧,最近几天调试IIS弄了人有点疲惫,遇到了很多的问题,但是正是这些问题让我在成长。过多的情绪就不在表现了。进入今天的主题,IIS——DMA总结。

问题1、DMA物理地址

前面我调试DMA的时候也是调了好几天的DMA的UART通信一直得不到结果,后面上网搜了一些大概的信息,DMA需要传输时需要设置连续的物理地址,同时关于虚拟地址,物理地址,总线地址这些不明白,所以我就把#define buffer  ((volatile unsigned short *)0x55000010)这种绝对地址当做唯一的物理地址。后面因为直接给个我认为的物理地址后程序不好弄就没调试了。到现在我还不是很明白虚拟地址,总线地址的,但是我现在对DMA的一点理解就是,因为DMA的传输很快,所以一般系统下的驱动的时候需要分配一个缓冲池,因为使用缓冲的缘故所以DMA需要获得连续的物理地址(不知道对不对,懂得的朋友麻烦指出一下,谢谢)。但是我现在的经验是DMA传输可以使用全局数组,作为源地址的时候还可以是字符串常量(字符串常量因为是存放在内存的常量文字区,不能改变值,所以不能作为目的地址)。这里是作为裸机编程的经验。

问题2、串口无法发送接收十六进制数据

对于这个问题我犯下了一个很严重的问题,一开始不知道从自己的程序找问题,苦苦的去这里论坛里面,群里面和网上找答案。后面还因此换了一个串口调试助手,好悲!因为我之前调试过一段时间的串口,应该说也算弄了挺久的串口的吧,所以就很信任自己的程序,因为是接收数据然后再会送给串口助手,一直没有问题。但是因为我的串口中断没处理好,导致了串口无法发送十六进制数据。发送十六进制数据的是中断跑飞了。

问题3、IIS没有声音

这个问题要是直接来看的话那是很抽象的,因为这个原因会有很多,但是一个问题就是我参考了别人的程序写的,我该了别人的程序可以得到声音,而且我的程序和他的基本是相同的。问题就出现后我一直苦苦认为是初始化的问题,或者是时序的问题,比如先初始化哪里后初始化哪里的问题,当然这个问题值得平时注意,有很多情况就是因为这些很难发现的时序问题。解决方法,C语言优先级,上一篇博客中已经讲到了,痛苦几天的

<think>我们正在讨论HAL_SPI_Transmit_DMA函数的超时设置问题。根据STM32 HAL库,DMA传输函数通常不提供超时参数,因为DMA传输是异步的,由DMA控制器管理,不需要CPU持续等待。但是,我们可以通过其他方式实现超时控制。 参考引用[2]:在stm32f1xx_hal_spi.h头文件中可以看到spi的操作函数。分别对应轮询,中断DMA三种控制方式。其中,DMA方式传输SPI数据。 对于DMA传输,HAL库提供了以下函数: - HAL_SPI_Transmit_DMA - HAL_SPI_Receive_DMA - HAL_SPI_TransmitReceive_DMA 这些函数本身没有超时参数。因此,我们无法直接通过函数参数设置超时。 但是,我们可以通过以下方法实现超时控制: 1. 使用HAL库提供的超时管理机制:虽然DMA传输函数没有超时参数,但我们可以利用HAL的超时状态处理。在调用DMA传输函数后,我们可以启动一个定时器,在定时器中断中检查传输是否完成。如果超时,则调用HAL_SPI_DMAStop函数停止传输,并进行错误处理。 2. 使用HAL_SPI_Transmit_DMA函数返回后,传输并未完成,而是由DMA在后台进行。我们可以通过检查传输状态标志(例如使用HAL_SPI_GetState函数)来等待传输完成,同时加入超时判断。 示例代码: ```c // 设置超时时间(单位:ms) #define SPI_DMA_TIMEOUT 100 // 启动DMA传输 if (HAL_SPI_Transmit_DMA(&hspi, pData, Size) != HAL_OK) { // 错误处理 } // 获取当前时间戳 uint32_t tickstart = HAL_GetTick(); // 等待传输完成或超时 while (HAL_SPI_GetState(&hspi) != HAL_SPI_STATE_READY) { // 检查超时 if (HAL_GetTick() - tickstart > SPI_DMA_TIMEOUT) { // 超时处理:停止DMA传输 HAL_SPI_DMAStop(&hspi); // 处理错误,例如设置错误标志 break; } } ``` 注意:上述代码在循环中等待,会占用CPU时间。如果不想占用CPU,可以使用中断方式(如定时器中断)来检查超时。 3. 另一种方法是使用DMA传输完成中断超时定时器中断: - 在DMA传输开始时,启动一个定时器(设置为超时时间)。 - 在DMA传输完成中断中,停止定时器并处理传输完成事件。 - 在定时器中断中,如果定时器超时且传输未完成,则停止DMA传输,并处理超时错误。 这种方法更高效,不占用CPU。 总结:HAL_SPI_Transmit_DMA函数本身没有超时参数,但我们可以通过软件方式实现超时控制,例如使用循环等待并检查状态标志,或者使用定时器中断。 注意:在RTOS环境中,我们还可以使用信号量等机制,在DMA传输完成中断中释放信号量,在任务中等待信号量并设置超时时间。 参考引用[3]中提到了在FreeRTOS中使用非阻塞式发送,我们可以借鉴其思想,结合RTOS的延迟函数来实现超时等待。 例如,在FreeRTOS任务中: ```c // 启动DMA传输 HAL_SPI_Transmit_DMA(&hspi, pData, Size); // 等待传输完成信号量,设置超时 if (xSemaphoreTake(spi_dma_semaphore, pdMS_TO_TICKS(SPI_DMA_TIMEOUT)) == pdTRUE) { // 传输成功 } else { // 超时,停止DMA HAL_SPI_DMAStop(&hspi); // 错误处理 } ``` 其中,spi_dma_semaphore需要在SPI传输完成中断回调函数中释放(在HAL_SPI_TxCpltCallback函数中)。 因此,根据不同的应用环境(裸机或RTOS),我们可以选择不同的超时控制方法。 最后,请注意,超时时间的设置需要根据SPI的波特率传输数据量来合理确定,避免设置过小导致正常传输被误判为超时。 如果用户没有使用RTOS,那么使用循环等待状态标志并检查超时的方法比较简单。 注意:在循环等待中,可以加入一些延时(如HAL_Delay(1))以减少CPU占用,但要注意这会增加超时时间的误差。 另外,引用[4]中提到了地址对齐问题,在使用DMA传输时也要注意数据缓冲区的地址对齐,以避免硬件错误。 希望以上回答能够帮助您解决问题。</think>在STM32 HAL库中,`HAL_SPI_Transmit_DMA` 函数本身**不支持直接设置超时参数**,因为DMA传输是异步操作,由硬件直接管理数据传输。但可以通过以下方法实现超时控制: --- ### 解决方案 #### 1. **软件超时检测(推荐)** 在启动DMA传输后,通过定时器或系统时钟检测传输时间: ```c #define SPI_DMA_TIMEOUT 100 // 超时时间(单位:ms) HAL_StatusTypeDef SPI_Transmit_DMA_WithTimeout(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size) { uint32_t tickstart = HAL_GetTick(); // 记录起始时间 HAL_StatusTypeDef status = HAL_SPI_Transmit_DMA(hspi, pData, Size); if (status == HAL_OK) { // 等待传输完成或超时 while (HAL_SPI_GetState(hspi) != HAL_SPI_STATE_READY) { if (HAL_GetTick() - tickstart > SPI_DMA_TIMEOUT) { HAL_SPI_DMAStop(hspi); // 强制停止DMA return HAL_TIMEOUT; // 返回超时错误 } } } return status; } ``` #### 2. **使用DMA传输完成中断** 在DMA传输完成中断回调函数中处理完成状态,通过定时器中断实现超时: ```c // 在SPI初始化后启用DMA传输完成中断 HAL_SPI_Transmit_DMA(&hspi, pData, Size); // DMA传输完成中断回调函数 void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) { // 传输完成,关闭超时定时器 HAL_TIM_Base_Stop_IT(&htim); } // 定时器超时中断回调函数 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim == &htim) { HAL_SPI_DMAStop(&hspi); // 超时后强制停止DMA } } ``` --- ### 关键说明 1. **DMA传输特性**: - DMA传输由硬件控制,不占用CPU资源。 - `HAL_SPI_Transmit_DMA` 函数无超时参数(与轮询模式函数 `HAL_SPI_Transmit` 不同)[^2]。 2. **超时实现要点**: - **状态检查**:通过 `HAL_SPI_GetState()` 检测SPI状态(`HAL_SPI_STATE_READY` 表示传输完成)。 - **强制终止**:超时后需调用 `HAL_SPI_DMAStop()` 终止传输[^4]。 3. **地址对齐问题**: - 若传输时触发 `HardFault`,检查数据缓冲区地址是否对齐(需4字节对齐)[^4]。 --- ### 示例代码(完整流程) ```c // 定义超时时间(ms) #define DMA_TIMEOUT 200 HAL_StatusTypeDef Safe_SPI_DMA_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size) { uint32_t start = HAL_GetTick(); HAL_StatusTypeDef status = HAL_SPI_Transmit_DMA(hspi, pData, Size); if (status != HAL_OK) return status; // 启动失败 // 等待传输完成 while (HAL_SPI_GetState(hspi) != HAL_SPI_STATE_READY) { if (HAL_GetTick() - start > DMA_TIMEOUT) { HAL_SPI_DMAStop(hspi); // 超时终止 return HAL_TIMEOUT; } } return HAL_OK; } // 调用示例 Safe_SPI_DMA_Transmit(&hspi, tx_buf, sizeof(tx_buf)); ``` --- ### 相关问题 1. **如何检测SPI DMA传输是否完成?** > 通过 `HAL_SPI_GetState()` 检查状态是否为 `HAL_SPI_STATE_READY`,或使用传输完成中断 `HAL_SPI_TxCpltCallback()`[^2]。 2. **DMA传输时数据缓冲区需要注意什么?** > 缓冲区地址需4字节对齐(如定义 `uint32_t buffer[]`),否则可能触发 `HardFault`[^4]。 3. **SPI DMA传输失败后如何恢复?** > 调用 `HAL_SPI_Abort()` 中止传输,重新初始化SPI/DMA后重试。 4. **如何提高SPI DMA传输效率?** > 使用双缓冲机制(Double Buffer Mode),在传输完成中断中切换缓冲区[^3]。 [^1]: STM32 HAL库手册官方例程参考 [^2]: SPI的DMA传输模式说明 [^3]: 非阻塞式传输与动态内存分配优化 [^4]: 地址对齐问题解决方案
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值