2

我正在开发一个使用 Microchip PIC24FJ256GA702 的项目。几天来,我一直在寻找一些 UART 代码的间歇性错误。它使用中断服务例程 (ISR) 来传输名为的缓冲区char txBuff[]及其长度int txLen

在主要(即不是 ISR)代码中,我偶尔会访问缓冲区以添加字节或查看还有多少字节要传输。为了确保主代码看到缓冲区的干净副本,它具有以下形式的关键部分:

Disable interrupts
Read and write 'txBuff' and its length 'txLen'
Enable interrupt

有几种方法可以禁用此 PIC 上的 ISR。包括:使用DISI指令,或清除GIE位,或改变中断优先级。

但我选择了清除中断以启用 中特定中断位的位IECx。在这种情况下,它是 IEC1bits.U2TXIE,因为我使用的是 UART2。我这样做是因为它很简单,它不会禁用不相关的中断,而且它在我的其他项目中一直运行良好。所以在 C 中,关键部分是:

IEC1bits.U2TXIE = 0;
copyOfTxLen = txLen;
...
IEC1bits.U2TXIE = 1;

反汇编清单开始:

 BCLR 0x9B, #7 // disable interrupt
 MOV txLen, W1 // read txLen
...

问题:我现在相当肯定 ISR 偶尔会在调用ISR 之前的read txLencopyOfTxLen最终成为txLenISR 之前的值,但关键部分的其余部分会看到变量处于调用 ISR 之后的状态。

问题:我的代码有问题吗?如果是这样,如何避免故障?

一些背景:

  • UART 以 8 MHz 的 PIC24 时钟高速(250 kBaud)运行,每 160 条指令一个字节,因此 ISR 非常繁忙,与我以前的项目相比,我认为这增加了达到竞争条件的机会。
  • 我看到dsPIC33/PIC24 系列参考手册/中断,第 2.3.1 节注 2 说“在清除 GIE 位和禁用中断之间存在一个周期延迟。” 现在这是指GIE,不是U2TXIE,我不确定它是否会导致我看到的问题,因为该MOV指令是 2 周期指令。但是,这似乎是一种诱捕粗心的程序员的狡猾方法。
  • 如果我尝试调试它或添加任何调试代码,那么已经非常间歇性的问题就会消失(感觉就像一个竞争条件)。我不想在NOP不知道为什么要添加的情况下添加一些 s,因为除非你真的修复它们,否则竞争条件总是会咬你。
4

1 回答 1

1

问题中的代码有问题。要修复它,请包装旨在禁用中断的表达式,例如IEC1bits.U2TXIE = 0在这样的宏中:

__write_to_IEC(IEC1bits.U2TXIE = 0);

XC16 编译器发行说明说:

__write_to_IEC(X)- 一个用适当数量的 nop 指令包装表达式 X 的宏,以确保IEC在程序执行之前对寄存器的写入已经生效。例如,__write_to_IEC(IEC0bits.T1IE = 0);在器件禁用 T1(Timer1)的中断使能位之前不会进行。

p24FJ256GA702.hXC16 编译器提供的那个中,定义了宏:

#define __write_to_IEC(X) \
   ( (void)(X), \
     __builtin_nop() \
   )

因此,尽管在PIC24FJ256GA705 FAMILY 数据表单独的中断文档中没有提到任何地方,但很明显,NOP这张 PIC 上需要一个。

更新:我在 dsPIC33EP256MU806(dsPIC33E 系列)上发现了完全相同的问题,需要两条NOP指令。

于 2020-07-02T11:25:17.477 回答