iot-fan

联系: iotfan123#163.com
注意:
1,本博客之内容来源于网上收集以及相关技术人员提供,如果有侵犯到您的权益,请电邮我沟通;
2,本博客之内容乃分享,交流,学习,研究之目的,作者不对内容的真实性,有效性,及时性负责,也不对因本博客的任何内容导致的任何后果负责;
3,本博客之内容禁止转发到CSDN网站,转到别的网站请保留出处.

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

说明

  • 在开发过程中,涉及到不同优先级操作同一个变量的时候,在低优先级操作时往往需要屏蔽高优先级的代码,否则可能有一些我们不愿看到的结果
  • WCH 作为国内为数不多RSIC-V的相关MCU产品被大规模商用的供应商,其riscv MCU(ch32v0系列除外)基本都加入了原子指令扩展,利用好这些扩展指令将大大方便我们的开发

一个简单的问题示例

下面我们在中断中和主循环中同时操作一个变量,这里我们使用的是GPIOB寄存器,在不屏蔽的情况下,看产生什么问题
在ch582芯片上,
->中断里面每10us翻转一次PB15,使用例程的GPIOB_InverseBits,原型为(R32_PB_OUT ^= pin)
->主循环里面不断翻转PB14,使用例程GPIOB_InverseBits

void main_loop() {
    while(1) {
        GPIOB_InverseBits(GPIO_Pin_14);
    }
}

__INTERRUPT
__HIGH_CODE
void TMR0_IRQHandler(void) {
    GPIOB_InverseBits(GPIO_Pin_15);
    TMR0_ClearITFlag(TMR0_3_IT_CYC_END);
}

我们发现中断里面的控制的gpio 并没有总是按照我们想象的那样按10us去翻转,经常我们翻转过去,立刻后变了回来
image

传统的解决方法

->主循环翻转前关掉对应的定时器中断,或者全局中断

void main_loop() {
    while(1) {
        PFIC_DisableIRQ(TMR0_IRQn)
        GPIOB_InverseBits(GPIO_Pin_14);
        PFIC_EnableIRQ(TMR0_IRQn)
    }
}

__INTERRUPT
__HIGH_CODE
void TMR0_IRQHandler(void) {
    GPIOB_InverseBits(GPIO_Pin_15);
    TMR0_ClearITFlag(TMR0_3_IT_CYC_END);
}

这里我们在低优先级的主循环里面操作这个寄存器时候,我们先屏蔽了会同时修改这个寄存器的中断,保证在我们读出来再回写的时候不会被打断
image

使用riscv原子操作指令解决

->中断里面每10us翻转一次PB15,使用例程的 amoxor.w
->主循环里面不断翻转PB14,使用原子指令 amoxor.w

static inline uint32_t __AMOXOR_W(volatile int32_t *addr,uint32_t value) {
    uint32_t result;
    __asm volatile ("amoxor.w %0, %2, %1" : \
            "=r"(result), "+A"(*addr) : "r"(value) : "memory");
    return *addr;
}

void main_loop() {
    while(1) {
        __AMOXOR_W(&R32_PB_OUT,GPIO_Pin_14);
    }
}

__INTERRUPT
__HIGH_CODE
void TMR0_IRQHandler(void) {
    __AMOXOR_W(&R32_PB_OUT,GPIO_Pin_15);
    TMR0_ClearITFlag(TMR0_3_IT_CYC_END);
}

这里我们使用的原子指令,这个结果可以看出与中断屏蔽再修改的结果是一样的
image

后记

上面我们只是演示了riscv atomic扩展指令中的amoxor.w指令,用来进行异或运算,实际上原子指令还有很多,大家可以直接翻看riscv的官方手册对应章节
image

附件

riscv 原子指令常用:与,或,异或

/**
 * \brief  Atomic And with 32bit value
 * \details Atomically AND 32bit value with value in memory using amoand.d.
 * \param [in]    addr   Address pointer to data, address need to be 4byte aligned
 * \param [in]    value  value to be ANDed
 * \return  return memory value & and value
 */
static inline int32_t __AMOAND_W(volatile int32_t *addr, int32_t value) {
    register int32_t result;

    __asm volatile ("amoand.w %0, %2, %1" : \
            "=r"(result), "+A"(*addr) : "r"(value) : "memory");
    return *addr;
}

/**
 * \brief  Atomic OR with 32bit value
 * \details Atomically OR 32bit value with value in memory using amoor.d.
 * \param [in]    addr   Address pointer to data, address need to be 4byte aligned
 * \param [in]    value  value to be ORed
 * \return  return memory value | and value
 */
static inline int32_t __AMOOR_W(volatile int32_t *addr, int32_t value) {
    register int32_t result;

    __asm volatile ("amoor.w %0, %2, %1" : \
            "=r"(result), "+A"(*addr) : "r"(value) : "memory");
    return *addr;
}

/**
 * \brief  Atomic XOR with 32bit value
 * \details Atomically XOR 32bit value with value in memory using amoxor.d.
 * \param [in]    addr   Address pointer to data, address need to be 4byte aligned
 * \param [in]    value  value to be XORed
 * \return  return memory value ^ and value
 */
static inline uint32_t __AMOXOR_W(volatile int32_t *addr,uint32_t value) {
    uint32_t result;
    __asm volatile ("amoxor.w %0, %2, %1" : \
            "=r"(result), "+A"(*addr) : "r"(value) : "memory");
    return *addr;
}

posted on 2023-03-25 15:28  iot-fan  阅读(415)  评论(0编辑  收藏  举报