I'm developing a C application using avr-libc on an AVR ATmega328P microcontroller. Since I don't have an ICE debugger for it, I followed these instructions and this tutorial for making the stdio.h functions such as printf able to use the hardware UART as stdout.
That works, and I can see the output on a PC terminal connected to my target board, but the strange thing is: When I have only one printf on main, but before the main loop something is causing the processor to reset, while if I have a printf only inside the main loop or before the main loop AND inside the loop it works fine. Something like this:
#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();
}
}
The code above produces the following output:
START LOOP LOOP LOOP LOOP LOOP LOOP ... LOOP
which is expected. If we comment the printf("START ") line it produces this:
LOOP LOOP LOOP LOOP LOOP LOOP LOOP ... LOOP
which is also fine. The problem is, if I don't have any printf inside the while loop, it goes like this:
START START START START START START ... START
That clearly shows the processor is being restarted, since the expected output would be just one START and nothing else while the infinite loop goes on being awaken only on the 1 kHz timer interrupts. Why is this happening? I should stress there's no watchdog timer configured (if there was, the cases where only LOOP is printed would be interrupted by a new START also).
Monitoring execution using GPIO pins
To try to get some insight into the situation, I turned GPIO pins ON and OFF around the problematic print("START ") and sleep_mode in the main loop:
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;
}
}
It turned out that GPIO1 stays ON for 132 µs (printf("START ") call time) and then OFF for 6.6 ms - roughly the time to transmit the six characters at 9600 bit/s - and GPIO2 toggles 12 times (six times two interrupts: the UART-ready-to-transmit interrupt and the UART-empty-data-register interrupt), showing sleep active for another 1.4 ms before GPIO1 goes ON again indicating a new printf("START ") - hence after reset. I'll probably have to check out the UART code, but I'm pretty sure the non-interrupt UART version also shows the same problem, and that doesn't explain either why having a printf inside the main loop works OK, without a reset happening (I would expect the reset would happen in any case should the UART code be faulty).
(SOLVED!): For completeness, The UART init and TX code is below**
This was my first attempt in writing an interrupt driven UART driver for the AVR, but one that could be used either on a RS-232 or a RS-485, which requires activating a TX_ENABLE pin while transmitting data. It turned out that, since I had to make the code useable either on ATmega328P or ATmega644, the interrupt vectors have different names, so I used a #define TX_VECTOR to assume the right name according to the processor used. In the process of making and testing the driver the choosing of "TX_VECTOR" for the UDRE data empty interrupt ended up masking the fact I hadn't defined the USART0_TX_vect yet (this was work in progress, I might not even need both anyway...)
Right now I just defined an empty interrupt service routine (ISR) for USART0_TX_vect and the thing doesn't reset anymore, showing #PeterGibson nailed it right on. Thanks a lot!
// 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);
}
FIXED UART CODE (NOW WORKING 100%!)
In order to help anyone interested or developing a similar interrupt driven UART driver for the AVR ATmega, here it goes the code with the problems above fixed and tested. Thanks to everyone who helped me spot the problem with the missing ISR!
// 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 == '\n') {
uart_drv_send_byte('\r', 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;
}
You've possibly enabled the UDRE (Uart Data Register Empty) interrupt and not set a vector for it, so when the interrupt triggers the processor resets (according to the defaults). When printf is called continuously in the main loop, this interrupt is never triggered.
From the docs
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.)
I ran in the same situation right now, but since I don't have a high reputation on stackoverflow, I can not vote.
here is a snippet of my initialization procedure that caused this problem to me:
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();
}
The problem was that I initially used interrupt based transmission, but later on I have changed the design and went for 10ms polling for Tx sequence, and forgotten to change this line as well in the init procedure.
Thanks very much for pointing this out Peter Gibson.
Related
I am trying to implement a 16-bit timer overflow interrupt on the ATMEGA168. The idea is to write a message to the UART I/O register when the timer overflows.
I've tested the UART separately and it works fine via RealTerm (baudrate of 9600 bits/s).
I created a base project from https://start.atmel.com/#dashboard where I had to set the input clock frequency to 16MHz to be compatible with the debugger (see page 5). So I would expect to see a 0x1 on my serial terminal every (16x106 / 1024)-1 x 216 = 4.194 seconds.
However, I'm not seeing anything on the terminal regardless of the prescaler I select. Can anyone please advise what could be going wrong?
I have attached the ISR and the main() below:
#include <atmel_start.h>
#include <stdio.h>
#include <usart_basic.h>
#include <atomic.h>
#include <avr/interrupt.h>
#include <avr/io.h>
// timer1 overflow
ISR(TIMER1_OVF_vect) {
// Send 0x1 over UART
UDR0 = 0x1;
}
int main(void) {
atmel_start_init();
// enable timer overflow interrupt for Timer1
TIMSK1 = (1<<TOIE1); // also tried |=
// start 16-bit counter with /1024 prescaler
TCCR1B = (1 << CS10) | (1 << CS12); // also tried |=
TCCR1A = 0x0;
// enable interrupts
sei();
while(true) {
// more code here...
}
}
I have tried to isolate the problem by not writing to UART in the ISR, but just incrementing a counter (declared with the volatile qualifier) and then printing its value to the screen via UART in the while(true) loop. But the counter doesn't increment either and remains stuck at 0.
You have no USART initialisation code. Specifically you don't enable the transmitter or set the baud rate. I accept that you have tried it with a counter, but that is not the code shown so we can come to no conclusion about its correctness or otherwise.
Without initialisation, the transmitter will not run, and the baud rate will be 1Mbps. Your need at least:
// Set baud rate 9600
uint16_t brr = (FOSC / 16 / 9600) - 1
UBRR0H = (uint8_t)(ubrr >> 8);
UBRR0L = (uint8_t)ubrr;
// Enable transmitter
UCSR0B = (1<<TXEN0);
// Note reset state frame is N,8,1
I am not convinced that it matters but your timer initialisation order is not idiomatic. You would normally enable the interrupt after setting the prescaler and any other configurations, and to ensure the first period is a complete period, reset the counter to zero immediately before enabling interrupts.
// set up timer with prescaler = 1024
TCCR1B = (1 << CS12) & (1 << CS11);
// initialise counter
TCNT1 = 0;
// enable overflow interrupt
TIMSK = (1 << TOIE1);
// enable global interrupts
sei();
As I said, I am not sure that will fix your problem but the elided part:
while(true) {
// more code here...
}
may well be the code that is breaking it. You would do well to discount that possibility by disabling or removing any code there temporarily.
I believe I understand how to use interrupts to receive serial data on UART of an ATmega328p, but I don't understand the mechanics of how to transmit data.
Here is a basic program that I want to use to transmit the character string "hello" using interrupts to drive transmission. I understand that the character 'o' will likely be transmitted twice, and I am ok with that.
#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 16000000UL
#define BAUD 19200
#define DOUBLE_SPEED 1
void initUART(unsigned int baud, unsigned int speed);
volatile uint8_t charIndex = 0;
volatile unsigned char command[5] = "hello";
int main(void)
{
//initialize UART
initUART(BAUD, DOUBLE_SPEED);
sei();
//What do I put here to initiate transmission of character string command?
//Is this even correct?
UDR0 = command[0];
while(1)
{
}
}
ISR(USART_TX_vect)
{
// Transmit complete interrupt triggered
if (charIndex >= 4)
{
//Reach the end of command, end transmission
return;
}
//transmit the first char or byte
UDR0 = command[charIndex];
//Step to the next place of the command
charIndex++;
}
void initUART(unsigned int baud, unsigned int speed)
{
unsigned int ubrr;
if(speed)
{
//double rate mode
ubrr = F_CPU/8/baud-1;
//set double speed mode
UCSR0A = (speed << U2X0);
}
else
{
//normal rate mode
ubrr = F_CPU/16/baud-1;
}
//set the baud rate
UBRR0H = (unsigned char)(ubrr >> 8);
UBRR0L = (unsigned char)(ubrr);
//enable Tx and Rx pins on MCU
UCSR0B = (1 << RXEN0) | (1 << TXEN0);
//enable transmit interrupt
UCSR0B = (1 << TXCIE0);
//set control bits, 8 bit char, 0 stop, no parity
UCSR0C = (1 <<UCSZ00) | (1 <<UCSZ01);
}
My understanding is that if I wrote the first character to UDR0 (as I did in main()), this would then trigger a Transmit Complete Interrupt, and then the next byte would be transmitted via the ISR. This does not seem to work.
The code shown here compiles using gcc. Can someone offer an explanation?
The key thing to understand is that the USART has 2 separate hardware registers that are used in the data transmission: UDRn and the Transmit Shift Register, which I'll just call TSR from now on.
When you write data to UDRn, assuming no tx is in progress, it'll get moved to the TSR immediately and the UDRE irq fires to tell you that the UDRn register is "empty". Note that at this point the transmission has just started, but the point is that you can already write the next byte to UDRn.
When the byte has been fully transmitted, the next byte is moved from UDRn to TSR and UDRE fires again. So, you can write the next byte to UDRn and so on.
You must only write data to the UDRn when it is "empty", otherwise you'll overwrite the byte it's currently storing and pending transmission.
In practice, you don't usually mind about the TXC irq, you want to work with the UDRE to feed more data to the USART module.
The TXC irq, however, is useful if you need to perform some operation when the transmission has actually completed. A common example when dealing with RS485 is to disable the transmitter once you're done sending data and possibly re-enable the receiver that you could have disabled to avoid echo.
Regarding your code
Your main issue is that you're setting UCSR0B 2 times in initUART() and the second write clears the bits you just set, so it's disabling the transmitter. You want to set all bits in one go, or use a |= on the second statement.
I am using a Kinetis KEA64 microcontroller from NXP.
The actual frequency of clock is 20 MHz, and a timer interrupt is generated at every 2.5ms. I have an interrupt handler that toggles an LED when this timer interrupt is generated. LED is toggling on this timer interrupt but i dont know exactly the frequency of LED. Does my LED toggles at 5kHz? Is it correct?
void interrupt_application_timer_FTM0()
{
SIM_SCGC |= SIM_SCGC_FTM0_MASK; /* Enable Clock for FTM0 */
FTM0_SC |= FTM_SC_PS(7); /* Select Preescaler in this case 128. 20 Mhz /128 =156.25 Khz. */
/* Counter increase by one every 6.4 us */
/* Enable Channle 0*/
FTM0_C0SC |= FTM_CnSC_CHIE_MASK; /* Enable channel 0 interrupt */
FTM0_C0SC |= FTM_CnSC_MSA_MASK; /* Channel as Output compare mode */
/*Select interrupt frequency*/
FTM0_C0V = FTM_CnV_VAL(391) ; /* Interrupt every 2.5ms */
FTM0_SC |= FTM_SC_CLKS(1); /*FTM0 use system clock*/
/* Set the ICPR and ISER registers accordingly */
NVIC_ICPR |= 1 << ((INT_FTM0-16)%32);
NVIC_ISER |= 1 << ((INT_FTM0-16)%32);
}
Here is my interrupt handler
void FTM0_IRQHandler()
{
if (1==((FTM0_C0SC & FTM_CnSC_CHF_MASK)>>FTM_CnSC_CHF_SHIFT) ) /* If the CHF of the channel is equal to 0 */
{
(void)FTM0_C0SC; /* Read to clear flag */
FTM0_C0SC ^= FTM_CnSC_CHF_MASK; /* Clear flag */
FTM0_C0V = FTM0_C0V + 391 ; /* Refresh interrupt period */
if (LED_counter>=50){
/* Toggle LED */
/* Reset counter */
LED0_TOGGLE;
LED_counter = 0;
}
LED_counter++;
}
}
Use scope for frequency measuring
In the MCU, you can enable bus clock to one pin (Busclockout), enable that and check the bus clock so that you can confirm your calculations
Then use scope at LED to confirm your frequency
I am new to STM8, and trying to use a STM8S103F3, using IAR Embedded Workbench.
Using C, I like to use the registers directly.
I need serial on 14400 baud, 8N2, and getting the UART transmit is easy, as there are numerous good tutorials and examples on the net.
Then the need is to have the UART receive on interrupt, nothing else will do.
That is the problem.
According to iostm8s103f3.h (IAR) there are 5 interrupts on 0x14 vector
UART1_R_IDLE, UART1_R_LBDF, UART1_R_OR, UART1_R_PE, UART1_R_RXNE
According to Silverlight Developer: Registers on the STM8,
Vector 19 (0x13) = UART_RX
According to ST Microelectronics STM8S.h
#define UART1_BaseAddress 0x5230
#define UART1_SR_RXNE ((u8)0x20) /*!< Read Data Register Not Empty mask */
#if defined(STM8S208) ||defined(STM8S207) ||defined(STM8S103) ||defined(STM8S903)
#define UART1 ((UART1_TypeDef *) UART1_BaseAddress)
#endif /* (STM8S208) ||(STM8S207) || (STM8S103) || (STM8S903) */
According to STM8S Reference manual RM0016
The RXNE flag (Rx buffer not empty) is set on the last sampling clock edge,
when the data is transferred from the shift register to the Rx buffer.
It indicates that a data is ready to be read from the SPI_DR register.
Rx buffer not empty (RXNE)
When set, this flag indicates that there is a valid received data in the Rx buffer.
This flag is reset when SPI_DR is read.
Then I wrote:
#pragma vector = UART1_R_RXNE_vector //as iostm8s103f3 is used, that means 0x14
__interrupt void UART1_IRQHandler(void)
{ unsigned character recd;
recd = UART1_SR;
if(1 == UART1_SR_RXNE) recd = UART1_DR;
etc.
No good, I continually get interrupts, UART1_SR_RXNE is set, but UART1_DR
is empty, and no UART receive has happened. I have disabled all other interrupts
I can see that can vector to this, and still no good.
The SPI also sets this flag, presumably the the UART and SPI cannot be used
together.
I sorely need to get this serial receive interrupt going. Please help.
Thank you
The problem was one bit incorrectly set in the UART1 setup.
The complete setup for the UART1 in the STM8S103F3 is now(IAR):
void InitialiseUART()
{
unsigned char tmp = UART1_SR;
tmp = UART1_DR;
// Reset the UART registers to the reset values.
UART1_CR1 = 0;
UART1_CR2 = 0;
UART1_CR4 = 0;
UART1_CR3 = 0;
UART1_CR5 = 0;
UART1_GTR = 0;
UART1_PSCR = 0;
// Set up the port to 14400,n,8,2.
UART1_CR1_M = 0; // 8 Data bits.
UART1_CR1_PCEN = 0; // Disable parity.
UART1_CR3 = 0x20; // 2 stop bits
UART1_BRR2 = 0x07; // Set the baud rate registers to 14400
UART1_BRR1 = 0x45; // based upon a 16 MHz system clock.
// Disable the transmitter and receiver.
UART1_CR2_TEN = 0; // Disable transmit.
UART1_CR2_REN = 0; // Disable receive.
// Set the clock polarity, clock phase and last bit clock pulse.
UART1_CR3_CPOL = 0;
UART1_CR3_CPHA = 0;
UART1_CR3_LBCL = 0;
// Set the Receive Interrupt RM0016 p358,362
UART1_CR2_TIEN = 0; // Transmitter interrupt enable
UART1_CR2_TCIEN = 0; // Transmission complete interrupt enable
UART1_CR2_RIEN = 1; // Receiver interrupt enable
UART1_CR2_ILIEN = 0; // IDLE Line interrupt enable
// Turn on the UART transmit, receive and the UART clock.
UART1_CR2_TEN = 1;
UART1_CR2_REN = 1;
UART1_CR1_PIEN = 0;
UART1_CR4_LBDIEN = 0;
}
//-----------------------------
#pragma vector = UART1_R_RXNE_vector
__interrupt void UART1_IRQHandler(void)
{
byte recd;
recd = UART1_DR;
//send the byte to circular buffer;
}
You forget to add global interrupt flag
asm("rim") ; //Enable global interrupt
It happens at non isolated connections whenever you connect your board's ground with other source's ground (USB<->TTL converter connected to PC etc.), In this case microcontroller is getting noise due to high value SMPS's Y capacitor etc.
Simply connect your RX and TX line's via 1K resistor and put 1nF (can be deceased for high speed) capacitors on these lines and to ground (micro controller side) to suppress noises.
My question is about real time data logging and multi-interrupt.
I am trying to program an MCU-ATMega 1280 by winAVR to make it read the pulses from the quadrature encoder(20um/pitch) and store the data into the flash memory (Microchip SST25VF080B, serial SPI protocol). After the encoder finishes its operation (around 2 minutes), the MCU export the data from the memory to the screen. My code is below.
But I don't know why it is not run correctly. There are 2 kind of bugs: one bug is some points suddenly out of the trend, another bug is sudden jumping value although the encoder runs slowly. The jumping seems to appear only when there is a turn.
I think the problem may lie only in the data storing because the trend happens like what I expected except for the jumps. I just want to ask if I run both ISR like what I did in the program. is there a case a ISR will be intervened by another ISR when it is running? According to atmega 1280 datasheet, it seems that when one ISR is occurring, no other interrupt allow to happen after the previous interrupt finish its routine.
#include <stdlib.h>
#include <stdio.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "USART.h" // this header is for viewing the data on the computer
#include "flashmemlib.h" // this header contains the function to read n
//write on the memory
#define MISO PB3
#define MOSI PB2
#define SCK PB1
#define CS PB0
#define HOLD PB6
#define WP PB7
#define sigA PD0 //INT0
#define sigB PD2 //INT2
#define LED PD3
uint8_t HADD,MADD,LADD, HDATA, LDATA,i; //HADD=high address, MADD-medium address, LADD-low address
volatile int buffer[8]; //this buffer will store the encoder pulse
uint32_t address = 0;
uint16_t DATA16B = 0;
int main(void)
{
INITIALIZE(); //initialize the IO pin, timer CTC mode, SPI and USART protocol
for(i=0;i<8;i++)
buffer[i]=0;
sei();
//AAI process- AAI is just one writing mode of the memory
AAIInit(address,0);
while (address < 50) //just a dummy loop which lasts for 5 secs (appox)
{
_delay_ms(100);
address++;
}
AAIDI();//disable AAI process
cli(); //disable global interrupt
EIMSK &= ~(1<<INT0);
TIMSK1 &= ~(1<<OCIE1A);
//code for reading procedure. i thought this part is unnecessary because i am quite //confident that it works correcly
return (0);
}
ISR(INT0_vect) // this interrupt is mainly for counting the number of encoder's pulses
{ // When an interrupt occurs, we only have to check the level of
// of pB to determine the direction
PORTB &= ~(1<<HOLD);
for(i=0;i<8;i++)
buffer[i+1]=buffer[i];
if (PIND & (1<<sigB))
buffer[0]++;
else buffer[0]--;
PORTB |= (1<<HOLD);
}
ISR(TIMER0_COMPA_vect) //after around 1ms, this interrupt is triggered. it is for storing the data into the memory.
{
HDATA =(buffer[7]>>8)&0xFF;
LDATA = buffer[7]&0xFF;
PORTB &= ~(1<<CS);
SEND(AD);
SEND(HDATA);
SEND(LDATA);
PORTB |=(1<<CS);
}
void SEND(volatile uint8_t data)
{
SPDR = data; // Start the transmission
while (!(SPSR & (1<<SPIF))){} // Wait the end of the transmission
}
uint8_t SREAD(void)
{
uint8_t data;
SPDR = 0xff; // Start the transmission
while (!(SPSR & (1<<SPIF))){} // Wait the end of the transmission
data=SPDR;
return data;
}
In the Interrupt Service Routine of INT0 you are writing:
for(i=0;i<8;i++)
buffer[i+1]=buffer[i];
Means that when i=7 you are writing outside of the array's predetermined space, and probably overwriting another variable. So you have to do:
for(i=0;i<7;i++)
buffer[i+1]=buffer[i];
AVR will manage the interrupts as you described, based on the ATmega1280 datasheet. Alternatively, if you want to allow the interruption of an ISR vector by another interrupt you need to do as follows (example):
ISR(INT0_vect, ISR_NOBLOCK)
{...
...}