打印
[STM32F1]

没有OS,怎么加信号量,关全局中断可否?

[复制链接]
1639|15
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
gowow|  楼主 | 2016-11-18 10:48 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 gowow 于 2016-11-18 10:51 编辑

涉及到中断和主循环中对总线操作的互斥,想加一个信号量,初步代码如下,有几点疑问
1、有没有更好办法来避免中断影响信号量判断的问题?
2、全局中断怎么关?
3、如果中断条件在关闭期间产生了,等中断恢复开启后,是否还能进中断函数?即关中断会不会导致一次的中断事件丢失?(因为关闭中断时间只有2行指令,应该不用考虑中断多次丢失,只要考虑一次丢失)

volatile int sem = 1;
bool sem_try_get()
{
      //需要在这里关中断避免在sem==1判断后,在sem=0锁定前,又在中断里被重复判断,导致中断和任务同时申请到sem的问题
      if (sem == 1)
      {
        sem = 0;
        //恢复中断
        return true;
      }
      //恢复中断
      return false;
}

void sem_free()
{
      sem = 1;
}
沙发
lxyppc| | 2016-11-18 11:04 | 只看该作者
本帖最后由 lxyppc 于 2016-11-18 11:10 编辑

ARM内核自带信号量
bool sem_try_get()
{
        int v = __LDREXW(&sem);
        if(v){
                return __STREXW(0, &sem);
        }
        return false;
}

bool sem_free(){
        int v = __LDREXW(&sem);
        if(!v){
                return __STREXW(1, &sem);
        }
        return false;
}

使用特权

评论回复
板凳
gowow|  楼主 | 2016-11-18 12:10 | 只看该作者
本帖最后由 gowow 于 2016-11-18 13:37 编辑
lxyppc 发表于 2016-11-18 11:04
ARM内核自带信号量
bool sem_try_get()
{

感谢,已经找到定义了

使用特权

评论回复
地板
myxiaonia| | 2016-11-18 12:54 | 只看该作者
lxyppc 发表于 2016-11-18 11:04
ARM内核自带信号量
bool sem_try_get()
{

好牛啊  这两个函数从哪里搞来的

使用特权

评论回复
5
lxyppc| | 2016-11-18 13:36 | 只看该作者
gowow 发表于 2016-11-18 12:10
感谢,不过我用5.16的keil编译,显示__LDREXW未定义,需要加入什么头文件呢? ...

在core_cmInstr.h这个文件里面定义的
如果不行的话试试用 ((uint32_t)__ldrex(&sem))

使用特权

评论回复
6
gowow|  楼主 | 2016-11-18 16:33 | 只看该作者
另外找到一个SWP,感觉使用起来更加简单,不知道ARM为什么要抛弃SWP

使用特权

评论回复
7
gowow|  楼主 | 2016-11-18 17:13 | 只看该作者
本帖最后由 gowow 于 2016-11-18 17:18 编辑
lxyppc 发表于 2016-11-18 11:04
ARM内核自带信号量
bool sem_try_get()
{

请教一下,如果有多个sem,ldrex的标记是否只能有一个?

如果只有一个标记,那是否会有以下问题?
taskA使用ldrex标记了semA,还未使用strex占用semA
但此时taskB使用ldrex标记了semB
这时semA的标记是不是就没了? 后面taskA去用strex去占用semA,因为semA的标记没了而失败?-----------------------------------------

看见了这个说明,看来多信号量是没法使用了。
一个用于解决多task互锁的指令,居然自己本身就有互锁问题。。。。。。
[size=1.1em]用法
利用 LDREX 和 STREX 可在多个处理器和共享内存系统之前实现进程间通信。
出于性能方面的考虑,请将相应 LDREX 指令和 STREX 指令间的指令数控制到最少。
Note
STREX 指令中所用的地址必须要与近期执行次数最多的 LDREX 指令所用的地址相同。 如果使用不同的地址,则 STREX 指令的执行结果将不可预知。


使用特权

评论回复
8
john_lee| | 2016-11-18 18:24 | 只看该作者
ldrex 和 strex 本来就是为了实现“读-修改-写”的原子操作并且不关中断而提供的,作为 semaphore 来说,除了没有阻塞机制外,它与 OS 的 semaphore 的操作是一致的。

semaphore 应该这样实现:
uint_fast8_t sem_try_get (uint32_t* sem)
{
  do {
    uint32_t data = __LDREXW (sem);
    if (data == 0)
      return 0;
    data--;
  } while (__STREXW (data, sem) != 0);
  return 1;
}

void sem_free (uint32_t* sem)
{
  while (__STREXW (__LDREXW (sem) + 1, sem) != 0);
}

sem_try_get 的参数是一个指针,指向 semaphore 计数器。如果计数器为 0,表示 sem 不可用,返回 0;如果计数器不为 0,则计数器 - 1,返回 1。
sem_free 使指针指向的计数器值 + 1。

使用特权

评论回复
9
mohanwei| | 2016-11-18 20:46 | 只看该作者
我曾经也喜欢研究这些东西……
后来发现理清流程后,总是简单的用事件驱动状态机就搞定了。比OS简单的是,不用考虑堆栈了。

如果你确实很喜欢这种OS的风格,可以用protothread

使用特权

评论回复
10
airwill| | 2016-11-20 13:51 | 只看该作者
这个问题问得好啊, 不由得不参加一下.
信号量作为 OS 的资源管理的一个方法, 被广泛支持.
不过它的思想也现实意义并非只在 OS 的环境中才具有.
这个定义 sem_try_get() 在我手头的库函数文件中并没有找到. 不过并不影响对这个问题的讨论.
由于 RISC 的原因, ARM 不能对内存进行单周期的判断并修改操作, (就像 51 的 JBC 指令) CM3 借助 LDREX 和 STREX 检测判断到修改指令之间额外的内存地址访问. 避免内存共享访问的冲突.
我们可以借助 OS 里的 SEM 管理思想应用到非 OS 的系统里, 解决实际问题

使用特权

评论回复
11
gowow|  楼主 | 2016-11-20 15:21 | 只看该作者
john_lee 发表于 2016-11-18 18:24
ldrex 和 strex 本来就是为了实现“读-修改-写”的原子操作并且不关中断而提供的,作为 semaphore 来说,除 ...

但是这里最重要的是LDREX只能标记一个物理地址,也就是说你整个MCU同时只能有一个信号量在使用

使用特权

评论回复
12
gowow|  楼主 | 2016-11-20 15:23 | 只看该作者
mohanwei 发表于 2016-11-18 20:46
我曾经也喜欢研究这些东西……
后来发现理清流程后,总是简单的用事件驱动状态机就搞定了。比OS简单的是, ...

不好意思,我也不想研究,我只想找一个能用的信号量办法,不想为了一个信号量,把整个工程都改掉

只是为了使用而使用,不想为了研究或新奇而使用

使用特权

评论回复
13
john_lee| | 2016-11-20 18:22 | 只看该作者
gowow 发表于 2016-11-20 15:21
但是这里最重要的是LDREX只能标记一个物理地址,也就是说你整个MCU同时只能有一个信号量在使用 ...

所以要有个循环,保证最近的一次 ldrex 是本循环中的。

使用特权

评论回复
14
john_lee| | 2016-11-20 18:35 | 只看该作者
gowow 发表于 2016-11-20 15:21
但是这里最重要的是LDREX只能标记一个物理地址,也就是说你整个MCU同时只能有一个信号量在使用 ...

虽然 ldrex 只能标记一个地址。但配合 strex 加循环就可以轻易地解决这个问题。
当 strex 时,如果标记的地址正确,则 strex 返回 0,否则返回 1。
如果返回 0,自然就成功了,如果返回 1,就循环到头再 ldrex 重新标记一次。

使用特权

评论回复
15
gowow|  楼主 | 2016-11-20 18:56 | 只看该作者
john_lee 发表于 2016-11-20 18:35
虽然 ldrex 只能标记一个地址。但配合 strex 加循环就可以轻易地解决这个问题。
当 strex 时,如果标记的 ...

如果无限循环倒是可以彻底解决,不过中断里一般不会无限循环或者等待超时
循环个几次应该可以减少多信号量之间的互斥

使用特权

评论回复
16
john_lee| | 2016-11-20 22:05 | 只看该作者
放心吧,这是 ARM 推荐的“互斥”访问的标准方法,如果无法做到多个“信号量”共存,那么 ARM 创造 ldrex / strex 指令真是脑子 short 了。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

43

主题

121

帖子

0

粉丝