in an exercise for my embedded programming course we have to program an Atmega328p AVR chip to receive data through the serial port. We have to do this by calling a function that waits until it receives a char. Next it should display the ascii value of that char in led lights, but I am having trouble even receiving it. I've done a lot of debugging and I think I narrowed it down to PuTTY not even sending the data, or the AVR not receiving it properly. I will put my code in below:
/*
From the PC should come a char, sent via the serial port to the USART data register.
It will arrive in the RXB, which receives data sent to the USART data register.
While running the loop, the program should encounter a function that is called and waits for the RXB to be filled.
Then it will read the RXB and return it to the main loop.
The result will be stored and processed accordingly.
*/
#define F_CPU 15974400
#include <util/delay.h>
#include <avr/io.h>
#include <stdlib.h>
#include <avr/interrupt.h>
void writeChar(char x);
void initSerial();
char readChar();
int main(void)
{
initSerial();
while (1)
{
char c = readChar(); //reads char, puts it in c
_delay_ms(250); //waits
writeChar(c); // spits that char back into the terminal
}
}
void initSerial(){
UCSR0A = 0;
//UCSR0B = (1 << TXEN0); // Enable de USART Transmitter
UCSR0B = 0b00011000; //transmit and receive enable
//UCSR0C = (1 << UCSZ01) | (0 << UCSZ00); /* 8 data bits, 1 stop bit */
UCSR0C = 0b00100110; // Even parity, 8 data bits, 1 stop bit
UBRR0H=00;
UBRR0L=103; //baudrade 9600 bij
}
void writeChar(char x){
while(!(UCSR0A & (1 << UDRE0))); // waits until it can send data
UDR0 = x; // Puts x into the UDR0, outputting it
}
char readChar(){
while (!(UCSR0A & (1 << RXC0))); //waits until it can send data
return UDR0; // returns the contents of the UDR0 (the receiving part of course
}
The problem is that when I enter anything in PuTTY (that I assume I set up correctly. https://prnt.sc/rc7f0f and https://prnt.sc/rc7fbj seem to be the important screens.
Thanks in advance, I am completely out of ideas.
I fixed it myself. I figured it out while taking it downstairs to test it on another laptop. I still had LEDs put in all pins of PORTD, all on input mode (that's the default mode). I quick look at the Atmega328p user guide (section 2.5.3) revealed that the pin D0 was actually the RxD in for the USART. By putting an LED on it and effectively grounding it, it was always pulled low, and would never be put high by the CPU, which would stop the while loop check while (!(UCSR0A & (1 << RXC0))); //waits until it can send data in readChar();
So by simply removing that led it would work again. Obviously that would mean it was floating, so I set the DDRD to all be output, as nothing needed to be input anyway.
That ended up fixing it.
Related
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'm a beginner in this field. My goal is to change the output of 8 LEDs (which are connected to PORTA) according to the potentiometer. I have connected the middle line of the potentiometer to PF0, which is ADC0. I also connected the other two lines to the 5V and ground.
I know there's no problem with the chip or connection because the LEDs are working just fine.
But no matter how I change the code below (what I mean by changing is by slightly changing the ADMUX and ADCSRA registers) no output is shown!
I am using atmega128 with 16MHZ clock. Below is the code that I'm trying to solve.
#include <asf.h>
#include <avr/io.h>
#define F_CPU 16000000L
int init_board(void)
{
DDRA=0xff;
PORTA=0x01;
}
int ADC_init(void)
{
//ADCSRA
ADCSRA = 0b10000111;
//ADMUX
ADMUX = 0b01100000; // middle line connected to ADC0
}
int main (void)
{
init_board();
ADC_init();
ADCSRA |= (ADSC >> 1);
while(1)
{
if(ADSC == 0)
{
uint8_t disp_value = ADCL;
PORTA = disp_value;
delay_ms(200);
ADCSRA |= (ADSC >> 1);
}
}
}
I have no idea why the code doesn't work.
I suppose it's because it didn't set my register correctly, but I've followed all the instructions on the atmega128 datasheet.
First issue is your bit shifting, it should be ADCSRA |= (1 << ADSC).
Next issue is results reading. You set fifth bit of ADMUX to 1, so ADLAR=1 and in that mode result is left adjusted so you should read ADCH.
Moreover when you switch to 10-bit resolution, i.e. you start working with multi-byte results, be aware that reading only ADCL is not enough, see datasheet 23.3 for explanation: "Once ADCL is read, ADC access to data registers is blocked. This means that if ADCL has been read, and a conversion completes before ADCH is read, neither register is updated and the result from the conversion is lost. When ADCH is read, ADC access to the ADCH and ADCL Registers is re-enabled."
Lastly, using hardcoded delays for reading is not good practice especially when you change code later to read ADC as fast as possible. In such case after conversion start you should check if ADIF flag is set or react with interrup when ADEN is set. Refer to datasheet for details.
I am having a problem with programming the serial for Arduino in C. The application demands speed, so I need to do it in C. I am using Codeblocks for it as it is easy to compile.
As I also want the serial feature not blocking the application and giving debug capability, I am trying to write a circular buffer type of serial library. The application does not need to receive data, only print.
The problem begins with the serial interrupt apparently not firing and the program blocks, the compiler does give a warning:
main.c|11|warning: ‘UART_UDRE_vect’ appears to be a misspelled signal handler|
The test program is as follows:
#include<avr/io.h>
#include<avr/interrupt.h>
#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU/(USART_BAUDRATE*16UL)))-1)
char ok = 0;
ISR(UART_UDRE_vect) {
ok = 1;
UCSR0B &= ~(1<<5);
}
int main(void) {
UBRR0H = (BAUD_PRESCALE >> 8);
UBRR0L = BAUD_PRESCALE;
UCSR0B |= (1<<TXEN0);
UCSR0C |= (1<<UCSZ00) | (1<<UCSZ01);
sei();
while(1){
// write the byte to the serial port
UDR0 = '0';
UCSR0B |= (1<<5);
while(ok != 1){}
ok = 0;
UDR0 = '\n';
UCSR0B |= (1<<5);
while(ok != 1){}
ok = 0;
}
return 0;
}
The configuration and baud rates are correct because the echo example found here does work.
Also other examples does not use UDRE, only RX interruption and that is not what I am looking for.
Am I forgetting something?
Two problems:
According to the documentation you linked, the interrupt vector for your particular part ATmega328p should be named USART_UDRE_vect.
Variables shared with an interrupt must always be declared as volatile or the compiler might not understand that they are used and therefore break your program upon optimization. Basically the compiler thinks that the variable ok can never get another value than 0, since it doesn't see a function call to the ISR anywhere (since it is called by hardware, not by the program). Change the declaration to static volatile char ok;
I want to set up the UART on a ATmega88-PA. First I was trying to set an interrupt on UDRE register but this was not working, so for the transmission I use normal polling.
Because the code was not working I started again from 0 with a basic program.
#define F_CPU 1000000UL
#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 8UL))) - 1)
char ReceivedByte = '#';
int main (void)
{
UCSR0A = (1 << U2X0);
/* Turn on the transmission and reception circuitry. */
UCSR0B = (1 << RXEN0) | (1 << TXEN0);
/* Use 8-bit character sizes. */
//UCSR0C = (1 << UCSZ00) | (1 << UCSZ01);
/* BAUD prescale */
UBRR0 = 12;
/* Load upper 8-bits of the baud rate value into the high byte of the UBRR register. */
//UBRR0H = (BAUD_PRESCALE >> 8);
/* Load lower 8-bits of the baud rate value into the low byte of the UBRR register. */
//UBRR0L = BAUD_PRESCALE;
UCSR0B |= (1 << RXCIE0);
sei();
DDRB |= 0x04;
PORTB &= ~0x04;
for (;;)
{
/* Do nothing until data have been received and is ready to be read from UDR. */
//while ((UCSR0A & (1 << RXC0)) == 0) {};
/* Fetch the received byte value into the variable "ByteReceived". */
//ReceivedByte = UDR0;
if(ReceivedByte == '1')
PORTB |=0x04;
else
PORTB &=~0x04;
/* Do nothing until UDR is ready for more data to be written to it. */
while ((UCSR0A & (1 << UDRE0)) == 0) {};
/* Echo back the received byte back to the computer. */
UDR0 = ReceivedByte;
}
}
ISR(USART_RX_vect)
{
ReceivedByte = UDR0;
}
And the code is working but when I open an arduino serial monitor and connect my module to that, I receive my poor # but alog with some garbage. Not all the time but mostly, the garbage is 1 or 2 byte. Can someone help me?
EDIT: It seams that when I send from my bleutooth data to a Samsung galaxy S3 the data is perfect...I do not have any clue why on serial monitor, and also when sending data using the same bluetooth to laptop I got a lot of garbage along with the data. If this helps you
answearing my qestion, will be great.
EDIT: sorry forget the last edit, it is send only a char ok, I change the char and also garbage is there. When I send a string is unreadable.
EDIT : As I commneted on the post below of embedded_guy , I solve the problem inserting a _delay_ms(1) after sending each byte. and it is working right now. I believe the statement
while ((UCSR0A & (1 << UDRE0)) == 0) {};
is not doing its job. Hope this will help others.
I don't know if this will work for you, but I really only see a couple of things that could be an issue.
First, all of the examples of setting the BAUD prescale that I could find used two instructions with the high and low registers of UBRR0. If you have already stepped through your code and examined that register to ensure that it is correctly configured, then that is not the issue. Otherwise, I would recommend setting it like this for the value you have it set to in your code:
UBRR0H = 12;
UBRR0L = 0;
The other thing I see is that you are never setting UCSR0C. You have it commented out and I would expect it to operate correctly with its default (reset condition) settings, but it is always good to be explicit just in case.
Finally, you may want to take a look at this page on Simple Serial Communications.
EDIT
Based on your most recent edit, I would take the bluetooth out of the picture. I would recommend connecting a logic analyzer to the UART transmit pin of your microcontroller and see if the data coming out of the atmega is what you expected. If that data is good, I would begin looking at why the bluetooth is not working as I anticipated.
try to use F_CPU with at least 2MHz
make your ReceivedByte volatile, try it like this:
volatile unsigned char ReceivedByte;
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)
{...
...}