硬汉嵌入式论坛

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

[技术讨论] FreeRTOS如何保证SPI/I2C/I2S操作不被其他任务中断

  [复制链接]

34

主题

206

回帖

308

积分

高级会员

积分
308
发表于 2024-8-21 16:15:18 | 显示全部楼层 |阅读模式
本帖最后由 LinY 于 2024-8-21 16:22 编辑

如题
测试板子STM32F407VET6,使用HAL库+FreeRTOS+lwip+lvgl+Fatfs
功能:传感器读下数据,然后显示到屏幕上并通过lwip上传服务器,同时判断是否触发报警,触发报警则播放报警音乐,LED灯也变红色。


用到了I2C1、I2C3、SPI1、SPI2、I2S3以及一堆uart
其中SPI2还是多个设备的,每个设备对SPI设置不一样,有CS引脚进行切换。
目前针对SPI2 我用了个全局变量设备类型,每次对SPI2操作前判断当前设备和之前的设备类型是否一致,不一致就执行对应的spi_init 然后关闭所有SPI2片选 只打开当前设备的片选。
目前情况:
SPI2读一个设备的时候,刚好另外一个任务触发SPI2写另外一个设备,判断SPI2还处于Locked状态导致操作失败。

各个外设,包括I2C和SPI等等,如何保证不被其他任务中断。

刚开始用FreeRTOS,不太熟悉,想问下有没有好的方法去避免?

之前搜了下用互斥锁,保证每一个外设操作时不会被打断,我在每个外设读写前后加上了获取和释放,但是很快就卡死了。

[C] 纯文本查看 复制代码
#define SPI2_MUTEX_WAITTIMEOUT 10000

#define SPI2_MUTEX_WAIT    osMutexAcquire(SPI2MutexHandle, SPI2_MUTEX_WAITTIMEOUT)
#define SPI2_MUTEX_RELEASE osMutexRelease(SPI2MutexHandle)
// 外设读写前后分别加上SPI2_MUTEX_WAIT和SPI2_MUTEX_RELEASE但是程序马上卡死了





回复

使用道具 举报

0

主题

20

回帖

20

积分

新手上路

积分
20
发表于 2024-8-21 17:12:53 | 显示全部楼层
用调度器开关函数或者中断开关函数,一般用调度器开关就行

vTaskSuspendAll()        // 开启调度锁
这里的代码不会被其它任务打断
xTaskResumeAll(void)   // 关闭调度锁
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117512
QQ
发表于 2024-8-22 09:43:19 | 显示全部楼层
除了这个还有一个思路,每个外设单独开一个任务,那个任务要操作给这个任务发消息即可。
回复

使用道具 举报

34

主题

206

回帖

308

积分

高级会员

积分
308
 楼主| 发表于 2024-8-22 14:33:41 | 显示全部楼层
serendipity 发表于 2024-8-21 17:12
用调度器开关函数或者中断开关函数,一般用调度器开关就行

vTaskSuspendAll()        // 开启调度锁

这种方式会不会很占用资源 毕竟把其他任务都停了
回复

使用道具 举报

6

主题

306

回帖

324

积分

高级会员

积分
324
发表于 2024-8-22 16:01:10 | 显示全部楼层
有些操作不能在中断里,中断里需要调用ISR开头的
回复

使用道具 举报

0

主题

20

回帖

20

积分

新手上路

积分
20
发表于 2024-8-22 17:14:28 | 显示全部楼层
LinY 发表于 2024-8-22 14:33
这种方式会不会很占用资源 毕竟把其他任务都停了

这个是比较简单的方式,会站cpu资源;如果想更好的方式:
1.就是硬汉哥说的单独搞一个任务,然后所有的总线控制都通过这个任务操作,然后其他任务通过消息队列的方式收发数据
2.就是你用的互斥锁。
回复

使用道具 举报

0

主题

20

回帖

20

积分

新手上路

积分
20
发表于 2024-8-22 17:37:27 | 显示全部楼层
LinY 发表于 2024-8-22 14:33
这种方式会不会很占用资源 毕竟把其他任务都停了

其实还是回到问题本身,你是希望执行总线读写操作时,不被其他任意任务中断;还是希望一个任务在执行某个总线操作时,另一个任务也需要执行这个总线时,必须等前一个任务执行完成。
如果是第一个,那么我觉得通过开关调度器来实现是比较ok的方式。
如果是第二个,就建议使用互斥锁,建议把总线的操作的接口抽象化,然后初始化总线时就初始化互斥锁,并提供总线的锁操作接口(申请/释放)。

注:
总线指I2C/UART/SPI等;
对于spi总线,一个spi含有多个片选时,认为他们是一个总线接口。
回复

使用道具 举报

0

主题

1

回帖

1

积分

新手上路

积分
1
发表于 2024-8-22 18:09:06 | 显示全部楼层
也可以加上互斥信号量,SPI使用完释放
回复

使用道具 举报

0

主题

8

回帖

8

积分

新手上路

积分
8
发表于 2024-8-23 22:04:46 | 显示全部楼层
关调度这个如果太频繁,这对其它任务会有影响。
可以使用使用互斥,或者如硬汉哥所说,为这些外设单独开一个任务,然后通过使用消息来操作。(参考打印机的工作方式,就是类似的原理,要打印的文件加入打印队列)
回复

使用道具 举报

1

主题

3

回帖

6

积分

新手上路

积分
6
发表于 2024-12-3 23:45:24 | 显示全部楼层
eric2013 发表于 2024-8-22 09:43
除了这个还有一个思路,每个外设单独开一个任务,那个任务要操作给这个任务发消息即可。

硬汉哥好,如果我这个IIC读取任务中用的是软件IIC,那么其他高优先级任务打断这个任务的运行,会出现IIC读取异常的情况吗
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117512
QQ
发表于 2024-12-4 09:33:18 | 显示全部楼层
耀风居士 发表于 2024-12-3 23:45
硬汉哥好,如果我这个IIC读取任务中用的是软件IIC,那么其他高优先级任务打断这个任务的运行,会出现IIC ...

会的,做好I2C的应答处理,没有正常应答放弃这次操作下次再处理即可。

回复

使用道具 举报

1

主题

3

回帖

6

积分

新手上路

积分
6
发表于 2024-12-5 19:57:48 | 显示全部楼层
eric2013 发表于 2024-12-4 09:33
会的,做好I2C的应答处理,没有正常应答放弃这次操作下次再处理即可。

懂了。谢谢硬汉哥。
那如果我将软件IIC换成硬件IIC的话,是不是就可以避免通信被任务打断后造成的异常?
感觉硬件IIC打断也是打断字节之间的传输,不会像软件IIC这样打断比特位,不知这样理解是否正确
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117512
QQ
发表于 2024-12-6 08:34:59 | 显示全部楼层
耀风居士 发表于 2024-12-5 19:57
懂了。谢谢硬汉哥。
那如果我将软件IIC换成硬件IIC的话,是不是就可以避免通信被任务打断后造成的异常? ...

对,是这样的
回复

使用道具 举报

4

主题

34

回帖

46

积分

新手上路

积分
46
发表于 2024-12-8 19:05:46 | 显示全部楼层
正确方法是使用互斥锁,如果很快卡死,那就查一下卡死的原因好了
回复

使用道具 举报

0

主题

50

回帖

50

积分

初级会员

积分
50
发表于 2024-12-9 10:14:13 | 显示全部楼层
请问楼主最早怎么解决了,卡死的原因是什么
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-8-12 02:52 , Processed in 0.050444 second(s), 25 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

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