AVR ATmega keeps resetting while using printf before main loop
我正在AVR ATmega328P微控制器上使用avr-libc开发C应用程序。由于我没有ICE调试器,因此我按照这些说明和本教程进行操作,以使
那行得通,我可以在连接到目标板上的PC终端上看到输出,但是奇怪的是:当我在主处理器上只有一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #include <stdio.h> /* stream definitions for UART input/output */ FILE uart_output = FDEV_SETUP_STREAM(uart_drv_send_byte, NULL, _FDEV_SETUP_WRITE); FILE uart_input = FDEV_SETUP_STREAM(NULL, uart_drv_read_byte, _FDEV_SETUP_READ); int main() { /* Definition of stdout and stdin */ stdout = &uart_output; stdin = &uart_input; /* Configures Timer1 for generating a compare interrupt each 1ms (1kHz) */ timer_init() /* UART initialization */ uart_drv_start(UBRRH_VALUE, UBRRL_VALUE, USE_2X, &PORTB, 2); /* Sets the sleep mode to idle */ set_sleep_mode(SLEEP_MODE_IDLE); printf("START"); /* main loop */ while(1) { printf("LOOP"); /* Sleeps so the main loop iterates only on interrupts (avoids busy loop) */ sleep_mode(); } } |
上面的代码产生以下输出:
1 | START LOOP LOOP LOOP LOOP LOOP LOOP ... LOOP |
这是预期的。如果我们注释
1 | LOOP LOOP LOOP LOOP LOOP LOOP LOOP ... LOOP |
也可以问题是,如果我在
1 | START START START START START START ... START |
这清楚地表明处理器正在重新启动,因为预期的输出将只是一个
使用GPIO引脚监控执行
为了了解情况,我围绕主循环中有问题的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | int main() { /* Irrelevant parts suppressed... */ GPIO1_ON; printf("START"); GPIO1_OFF; /* Main loop */ while(1) { /* Sleeps so the main loop iterates only on interrupts (avoids busy loop) */ GPIO2_ON; sleep_mode(); GPIO2_OFF; } } |
事实证明,GPIO1保持打开状态持续132μs(
(已解决!):为完整起见,UART初始化和发送代码如下**
这是我为AVR编写一个中断驱动的UART驱动程序的第一次尝试,但是可以在RS-232或RS-485上使用,在发送数据时需要激活TX_ENABLE引脚。事实证明,由于我必须使代码在ATmega328P或ATmega644上都可以使用,因此中断向量具有不同的名称,因此我根据所使用的处理器使用了
现在,我刚刚为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | // Interrupt vectors for Atmega328P #if defined(__AVR_ATmega328P__) #define RX_VECTOR USART_RX_vect #define TX_VECTOR USART_UDRE_vect // Interrupt vectors for Atmega644 #elif defined(__AVR_ATmega644P__) #define RX_VECTOR USART0_RX_vect #define TX_VECTOR USART0_UDRE_vect #endif ISR(TX_VECTOR) { uint8_t byte; if (!ringbuffer_read_byte(&txrb, &byte)) { /* If RS-485 is enabled, sets TX_ENABLE high */ if (TX_ENABLE_PORT) *TX_ENABLE_PORT |= _BV(TX_ENABLE_PIN); UDR0 = byte; } else { /* No more chars to be read from ringbuffer, disables empty * data register interrupt */ UCSR0B &= ~_BV(UDRIE0); } /* If RS-485 mode is on and the interrupt was called with TXC0 set it * means transmission is over. TX_ENABLED should be cleared. */ if ((TX_ENABLE_PORT) && (UCSR0A & _BV(TXC0) & _BV(UDR0))) { *TX_ENABLE_PORT &= ~_BV(TX_ENABLE_PIN); UCSR0B &= ~_BV(UDRIE0); } } void uart_drv_start(uint8_t ubrrh, uint8_t ubrrl, uint8_t use2x, volatile uint8_t* rs485_tx_enable_io_port, uint8_t rs485_tx_enable_io_pin) { /* Initializes TX and RX ring buffers */ ringbuffer_init(&txrb, &tx_buffer[0], UART_TX_BUFSIZE); ringbuffer_init(&rxrb, &rx_buffer[0], UART_RX_BUFSIZE); /* Disables UART */ UCSR0B = 0x00; /* Initializes baud rate */ UBRR0H = ubrrh; UBRR0L = ubrrl; if (use2x) UCSR0A |= _BV(U2X0); else UCSR0A &= ~_BV(U2X0); /* Configures async 8N1 operation */ UCSR0C = _BV(UCSZ00) | _BV(UCSZ01); /* If a port was specified for a pin to be used as a RS-485 driver TX_ENABLE, * configures the pin as output and enables the TX data register empty * interrupt so it gets disabled in the end of transmission */ if (rs485_tx_enable_io_port) { TX_ENABLE_PORT = rs485_tx_enable_io_port; TX_ENABLE_PIN = rs485_tx_enable_io_pin; /* Configures the RS-485 driver as an output (on the datasheet the data * direction register is always on the byte preceding the I/O port addr) */ *(TX_ENABLE_PORT-1) |= _BV(TX_ENABLE_PIN); /* Clears TX_ENABLE pin (active high) */ *TX_ENABLE_PORT &= ~_BV(TX_ENABLE_PIN); /* Enables end of transmission interrupt */ UCSR0B = _BV(TXCIE0); } /* Enables receptor, transmitter and RX complete interrupts */ UCSR0B |= _BV(RXEN0) | _BV(TXEN0) | _BV(RXCIE0); } |
固定的UART代码(现在工作100%!)
为了帮助对AVR ATmega感兴趣或开发类似的中断驱动UART驱动程序的任何人,此处提供了上面已解决和测试的问题的代码。感谢所有帮助我发现缺少ISR的问题的人!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | // Interrupt vectors for Atmega328P #if defined(__AVR_ATmega328P__) #define RX_BYTE_AVAILABLE USART_RX_vect #define TX_FRAME_ENDED USART_TX_vect #define TX_DATA_REGISTER_EMPTY USART_UDRE_vect // Interrupt vectors for Atmega644 #elif defined(__AVR_ATmega644P__) #define RX_BYTE_AVAILABLE USART0_RX_vect #define TX_FRAME_ENDED USART0_TX_vect #define TX_DATA_REGISTER_EMPTY USART0_UDRE_vect #endif /* I/O port containing the pin to be used as TX_ENABLE for the RS-485 driver */ static volatile uint8_t* TX_ENABLE_PORT = NULL; /** Pin from the I/O port to be used as TX_ENABLE for the RS-485 driver */ static volatile uint8_t TX_ENABLE_PIN = 0; ISR(RX_BYTE_AVAILABLE) { // Read the status and RX registers. uint8_t status = UCSR0A; // Framing error - treat as EOF. if (status & _BV(FE0)) { /* TODO: increment statistics */ } // Overrun or parity error. if (status & (_BV(DOR0) | _BV(UPE0))) { /* TODO: increment statistics */ } ringbuffer_write_byte(&rxrb, UDR0); } ISR(TX_FRAME_ENDED) { /* The end of frame interrupt will be enabled only when in RS-485 mode, so * there is no need to test, just turn off the TX_ENABLE pin */ *TX_ENABLE_PORT &= ~_BV(TX_ENABLE_PIN); } ISR(TX_DATA_REGISTER_EMPTY) { uint8_t byte; if (!ringbuffer_read_byte(&txrb, &byte)) { /* If RS-485 is enabled, sets TX_ENABLE high */ if (TX_ENABLE_PORT) *TX_ENABLE_PORT |= _BV(TX_ENABLE_PIN); UDR0 = byte; } else { /* No more chars to be read from ringbuffer, disables empty * data register interrupt */ UCSR0B &= ~_BV(UDRIE0); } } void uart_drv_start(uint8_t ubrrh, uint8_t ubrrl, uint8_t use2x, volatile uint8_t* rs485_tx_enable_io_port, uint8_t rs485_tx_enable_io_pin) { /* Initializes TX and RX ring buffers */ ringbuffer_init(&txrb, &tx_buffer[0], UART_TX_BUFSIZE); ringbuffer_init(&rxrb, &rx_buffer[0], UART_RX_BUFSIZE); cli(); /* Disables UART */ UCSR0B = 0x00; /* Initializes baud rate */ UBRR0H = ubrrh; UBRR0L = ubrrl; if (use2x) UCSR0A |= _BV(U2X0); else UCSR0A &= ~_BV(U2X0); /* Configures async 8N1 operation */ UCSR0C = _BV(UCSZ00) | _BV(UCSZ01); /* If a port was specified for a pin to be used as a RS-485 driver TX_ENABLE, * configures the pin as output and enables the TX data register empty * interrupt so it gets disabled in the end of transmission */ if (rs485_tx_enable_io_port) { TX_ENABLE_PORT = rs485_tx_enable_io_port; TX_ENABLE_PIN = rs485_tx_enable_io_pin; /* Configures the RS-485 driver as an output (on the datasheet the data * direction register is always on the byte preceding the I/O port addr) */ *(TX_ENABLE_PORT-1) |= _BV(TX_ENABLE_PIN); /* Clears TX_ENABLE pin (active high) */ *TX_ENABLE_PORT &= ~_BV(TX_ENABLE_PIN); /* Enables end of transmission interrupt */ UCSR0B = _BV(TXCIE0); } /* Enables receptor, transmitter and RX complete interrupts */ UCSR0B |= _BV(RXEN0) | _BV(TXEN0) | _BV(RXCIE0); sei(); } void uart_drv_send_byte(uint8_t byte, FILE *stream) { if (byte == ' ') { uart_drv_send_byte(' ', stream); } uint8_t sreg = SREG; cli(); /* Write byte to the ring buffer, blocking while it is full */ while(ringbuffer_write_byte(&txrb, byte)) { /* Enable interrupts to allow emptying a full buffer */ SREG = sreg; _NOP(); sreg = SREG; cli(); } /* Enables empty data register interrupt */ UCSR0B |= _BV(UDRIE0); SREG = sreg; } uint8_t uart_drv_read_byte(FILE *stream) { uint8_t byte; uint8_t sreg = SREG; cli(); ringbuffer_read_byte(&rxrb, &byte); SREG = sreg; return byte; } |
您可能已经启用了UDRE(Uart数据寄存器为空)中断,并且未为其设置向量,所以当该中断触发处理器复位时(根据默认值)。 在主循环中连续调用
来自文档
Catch-all interrupt vector
If an unexpected interrupt occurs (interrupt is enabled and no handler
is installed, which usually indicates a bug), then the default action
is to reset the device by jumping to the reset vector. You can
override this by supplying a function named BADISR_vect which should
be defined with ISR() as such. (The name BADISR_vect is actually an
alias for __vector_default. The latter must be used inside assembly
code in case is not included.)
我现在遇到了同样的情况,但是由于我在stackoverflow上没有很高的声誉,因此我无法投票。
这是导致此问题的初始化过程的片段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | void USART_Init() { cli(); /* Set baud rate */ UBRR0H = (uint8_t)(BAUD_PRESCALE>>8); UBRR0L = (uint8_t)BAUD_PRESCALE; /* Enable receiver and transmitter */ UCSR0B |= (1<<RXEN0)|(1<<TXEN0); /* Set frame format: 8data, 1stop bit 8N1 => 86uS for a byte*/ UCSR0C |= (1<<UCSZ01)|(1<<UCSZ00); /*enable Rx and Tx Interrupts*/ UCSR0B |= (1 << RXCIE0) | (1 << TXCIE0); //<- this was the problem /*initialize the RingBuffer*/ RingBuffer_Init(&RxBuffer); sei(); } |
问题是我最初使用基于中断的传输,但是后来我更改了设计,并为Tx序列进行了10ms的轮询,并且忘记了在初始化过程中也更改了这条线。
非常感谢Peter Gibson指出这一点。