硬汉嵌入式论坛

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

[有问必答] 关于 裸机前后台程序 中断和主循环 共享全局变量的问题

[复制链接]

1

主题

1

回帖

4

积分

新手上路

积分
4
发表于 2022-4-30 16:22:05 | 显示全部楼层 |阅读模式
看了网上 原子操作 临界段 volatile 可见性等概念,对于全局变量在前后台中共享问题还是有一些疑问。1.对于AVR 、51 单片机 是不是 访问或者赋值一个16位、32位的变量 对应的指令就是好几条的,分别操作高低字节 ?
2.例如 一个16位宽的变量在中断里写 ,主程序里读 需要加保护措施吗
下面设计是不是会导致当中断发生在执行 if(uwcnt>=1000)这条语句时读uwcnt时读到一个跳变的数据。或者单读单写一个超过总线宽度的变量 是不是就要保护了,要进行中断保存-> 关闭相应中断-> if(uwcnt>=1000)->恢复之前中断状态操作。那这样在循环里的频繁开关中断会影响系统实时性 所以这样程序设计是不是不合理?采用共享8位变量 单读单写是不是会解决读取到一个不是正常的数值的问题?可能问的有点乱  希望能解答下 或者哪里有相关资料
[C] 纯文本查看 复制代码
// 51 或avr   8位机
#include <stdint.h>
volatile unsigned short int uwcnt++;
main()
{
init()
while(1)
{
    if(uwcnt>=1000)
    {
        led_on;
        lcd();
      }
}
//中断程序
ISR XXXX()
{
uwcnt++;
}

回复

使用道具 举报

75

主题

684

回帖

909

积分

金牌会员

积分
909
发表于 2022-4-30 21:08:33 | 显示全部楼层
是有可能错过的,但是问题不大。你这个代码只有中断写uwcnt,主循环又是使用>=判断,可以放心安全使用。就是可能存在一些不一致的问题,比如有时候1000进入if语句,有时候会多点。
回复

使用道具 举报

1

主题

1

回帖

4

积分

新手上路

积分
4
 楼主| 发表于 2022-5-2 15:43:01 | 显示全部楼层
我理解的实际uwcnt小于1000的时候也会出现  比如uwcnt=0x2ff (为767), 执行 if(uwcnt>=1000)当加载uwcnt的低字节0xff后发生中断 中断修改uwcnt为0x300,再次回到主程序中执行加载高字节指令0x03,读到的是中断前的低字节与中断后的高字节的组合uwcnt=0x3ff (为1023) 满足条件就执行下面的语句了。所以会提前执行if里面的语句?
回复

使用道具 举报

8

主题

135

回帖

159

积分

初级会员

积分
159
发表于 2022-6-30 14:55:44 | 显示全部楼层
是的,51单片机一定要注意这个问题。这个与volatile没什么关系。volatile无法避免当前指令被打断, 只能防止再次读取这个变量时, 这个变量还是从内存去读取,而不是临时在R0~R7的中间值。
回复

使用道具 举报

5

主题

51

回帖

66

积分

初级会员

积分
66
QQ
发表于 2022-6-30 17:25:04 | 显示全部楼层
应该要看单片机指令集执行时,当前高低位读数据的时候,是否会被中断打断(可能要看51内核的一些手册)?比如arm内核,终端响应的时候,有的指令不能被打断,要执行完当前指令才可以响应中断,有的指令可以被打断。
回复

使用道具 举报

210

主题

1043

回帖

1683

积分

至尊会员

More we do, more we can do.

积分
1683
发表于 2022-7-1 09:34:24 | 显示全部楼层
并发写的情况,就得考虑关中断;只有单个源写,其余都是读就还好,调用时,先把值复制出来,期间使用值,就不受中断影响。
回复

使用道具 举报

1

主题

4

回帖

7

积分

新手上路

积分
7
发表于 2023-12-18 09:11:41 | 显示全部楼层
emwin 发表于 2022-7-1 09:34
并发写的情况,就得考虑关中断;只有单个源写,其余都是读就还好,调用时,先把值复制出来,期间使用值,就 ...

你好,有个问题想请教一下,中断和后台任务都要对一个队列进行写,相当于两个源写,这种情况是不是只能在后台任务中写的时候关中断?但是频繁开关会有什么影响?有没有更好的处理方式
回复

使用道具 举报

3

主题

96

回帖

105

积分

初级会员

积分
105
发表于 2023-12-18 09:35:18 | 显示全部楼层
lceihn 发表于 2023-12-18 09:11
你好,有个问题想请教一下,中断和后台任务都要对一个队列进行写,相当于两个源写,这种情况是不是只能在 ...

加个标志位进行互斥。把数据临时存储,等到下一次中断,并且主循环没有在写的过程时写入数据。
回复

使用道具 举报

1

主题

4

回帖

7

积分

新手上路

积分
7
发表于 2023-12-18 11:06:45 | 显示全部楼层
2859932063 发表于 2023-12-18 09:35
加个标志位进行互斥。把数据临时存储,等到下一次中断,并且主循环没有在写的过程时写入数据。

我按照你这个思路实现了,感觉很好,十分感谢

//中断处理 adcQueueBuf
void DMA1_Channel0_IRQHandler(void)
{
    static uint16_t bufCopy[MAX_COPY_SIZE][SAMPLE_NUMS];
    static uint8_t copyCnt;

    if (SET == dma_interrupt_flag_get(DMA1, DMA_CH0, DMA_INT_FLAG_FTF))
    {
        //防止溢出
        if (copyCnt >= MAX_COPY_SIZE)
            copyCnt = 0;
        //复制一份最新数据到缓冲区
        for (int i = 0; i < SAMPLE_NUMS; ++i)
            bufCopy[copyCnt] = adcBuffer[rank][adcBufIdx];
        copyCnt++;
        //后台任务与中断互斥, 全局变量 adcQueueBuf
        if (adc_queue_is_lock() == 0)
        {
            for (int i = 0; i < copyCnt; ++i)
            {
                for (int j = 0; j < SAMPLE_NUMS; ++j)
                {
                    adc_enqueue(&adcQueueBuf[j], bufCopy[j]);
                }
            }
            copyCnt = 0;
        }

        dma_interrupt_flag_clear(DMA1, DMA_CH0, DMA_INT_FLAG_FTF);
    }
}
//后台任务处理 adcQueueBuf
void adc_dequeue_size(adcQueue *queue, uint16_t *buf, uint16_t size)
{
    adc_queue_lock();
    for (int i = 0; i < size; ++i)
    {
        buf = adc_dequeue(queue);
    }
    adc_queue_unlock();
}
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-5-4 11:31 , Processed in 0.177804 second(s), 25 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

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