[英]Odd PIC behavior with variables set by ISR
我在PIC代码中观察到一些奇怪的行为,在该行为中,我将ISR写入的全局变量中的数据连续复制到两个不同的缓冲区中,但是该值似乎只能成功写入两个缓冲区之一。 当我尝试使用不是由ISR编写的其他变量来尝试此操作时,它会正确地复制到两个缓冲区中。
我的设置是使用XC8编译器1.45版的PIC16LF15355。 我将PIC MSSP模块之一配置为SPI接口,以将测量数据写入状态并将状态切换到RF收发器。 我还有另一个远程RF收发器,它通过SPI接口通过PIC16LF15355接收和处理数据。 对于这两个PIC,我将第二个MSSP模块配置为连接到Raspberry Pi的I2C接口,以进行调试。 写入发送端SPI缓冲区的任何数据也将复制到I2C缓冲区,以便可以定期读取以查看正在发送的数据。 同样,将读取到接收端SPI缓冲区中的数据复制到I2C缓冲区中,以便可以定期读取以查看正在接收的内容。
发射端有一个开关输入,该输入通过定时器ISR(以下代码中的timer0_handler)进行去抖动。 该ISR将状态写入sw1State。 当需要传输数据时,会将开关状态和其他测量数据复制到I2C缓冲区中,然后将I2C缓冲区复制到SPI缓冲区中进行传输。 当我定期从Pi采样I2C缓冲区的内容时,可以看到开关状态随着开关的按下而改变。 但是,当我在接收端定期对I2C缓冲区内容进行采样时,我只会看到开关状态默认值(1)。
奇怪的是,如果我更改代码以从未由ISR写入的变量中复制值,则该值会在接收端正确显示。 但是,从I2C缓冲区复制到SPI缓冲区只是一个阵列到另一个阵列的通用副本,所有其他测量数据都会在接收端正确显示。 这两个缓冲区应该相同。 从I2C缓冲区复制到SPI缓冲区写到收发器之间的这段时间里,我的代码中没有任何东西可以修改SPI缓冲区。
下面是该代码的精简副本。 我已经看了好几天了,只是看不出是什么原因造成的。
volatile uint8_t sw1State; // current switch 1 state
volatile uint8_t sw1TimeExpired; // true if switch 1 debounce time expired
uint8_t spiOutBuf[13]; // SPI output buffer
uint8_t spiInBuf[13]; // SPI input buffer
uint8_t i2cBuff[13]; // I2C input/output buffer
void interrupt main_ISR( void ) {
I2C_slave_handler();
timer0_handler();
switch_input_handler();
}
int main( void ) {
... device initialization code omitted for brevity
sw1State = 1;
sw1TimeExpired = 1;
for ( ; ; )
{
// Read the ADC values and add to the I2C buffer (for debugging).
for ( uint8_t chan = 0; chan < 9; chan++ )
{
i2cBuff[chan + 1] = ADC_read( chan );
}
// Read the switch status and add to the I2C buffer.
i2cBuff[10] = sw1State;
// Load the write transmit payload command into the SPI buffer.
spiOutBuf[0] = CMD_W_TX_PAYLOAD;
// Copy the payload data from the I2C buffer to the SPI buffer.
for ( uint8_t i = 1; i < 13; i++ )
{
spiOutBuf[i] = i2cBuff[i];
}
// Write the SPI buffer to the transceiver module payload register.
writeReadSPI( 13 );
// Clear the RB4 interrupt-on-change interrupt flag (IRQ change) and
// set CE high to initiate the transmit. Hold CE high until an ack
// is received or there is an ack timeout.
IOCBFbits.IOCBF4 = 0;
CE = 1;
__delay_us( 130 ); // ensure minimum state change transition time
... code here omitted which checks the transceiver status
__delay_ms( 80 );
}
}
void switch_input_handler( void ) {
// Check for switch 1 trigger.
if ( IOCCFbits.IOCCF6 == 1 )
{
IOCCFbits.IOCCF6 = 0; // clear IOC interrupt
// Process switch 1 trigger if debounce time has expired.
if ( sw1TimeExpired == 1 )
{
// Toggle between OFF state and ON state.
if ( sw1State == 1 )
{
sw1State = 2;
}
else
{
sw1State = 1;
}
// Load and restart Timer0 with 2-second counter value.
T0CON0bits.T0EN = 0;
TMR0H = 0xc2;
TMR0L = 0xf7;
T0CON0bits.T0EN = 1;
sw1TimeExpired = 0;
}
}
}
void timer0_handler( void ) {
if ( PIR0bits.TMR0IF == 1 )
{
PIR0bits.TMR0IF = 0; // clear the interrupt
T0CON0bits.T0EN = 0; // disable Timer0
if ( sw1TimeExpired == 0 )
{
sw1TimeExpired = 1; // Note the switch 1 timeout for switch handler.
// No state transition here. This just enables
// the next switch interrupt to change state.
}
}
}
void I2C_slave_handler( void ) {
... code omitted for brevity
}
从代码片段中可以看出,SPI通道正在作为后台任务发送,而I2C通道正在前台(中断处理程序)发送。 这可能会导致竞争状况并解释您遇到的行为。
建议您尝试对两个通道使用单个发送缓冲区,并设置一个变量以指示两个通道何时已成功发送数据。 一旦两个通道都指示已发送数据,则为下一个周期生成新的数据包/缓冲区。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.