I want to receive a string(pointer to characters) by UART using ATMEGA16. I burned this code on the kit then I used hyperterminal (realterm) and made a test to input a string ("on") and if it is received then portc (LEDS) will be set to 1 but it doesn't work ... anyone!? :D
Implementation of functions
#include <avr/io.h>
#define F_CPU 8000000UL
unsigned char *x;
#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
void uartinit()
{
UCSRB |= (1 << RXEN) | (1 << TXEN);
// Turn on the transmission and reception circuitry
UCSRC |= (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1);
// Use 8-bit character sizes
UBRRL = BAUD_PRESCALE; // Load lower 8-bits of the baud rate value..
// into the low byte of the UBRR register
UBRRH = (BAUD_PRESCALE >> 8); // Load upper 8-bits of the baud rate value..
// into the high byte of the UBRR register
}
void uartsend( unsigned char *data )
{
while(*data != '\0')
{
/* Wait for empty transmit buffer */
while ( !( UCSRA & (1<<UDRE)) );
/* Put data into buffer, sends the data */
UDR = *data;
data++;
}
while ( !( UCSRA & (1<<UDRE)) );
UDR = *data;
}
unsigned char * uartrecieve()
{
//unsigned char i=0;
// unsigned char * x;
/* Wait for data to be received */
// char * ptr = &UDR;
while ( !(UCSRA & (1<<RXC)) );
while(UDR != '\0')
{
*x = UDR;
x++;
while ( !(UCSRA & (1<<RXC)) );
}
*x = UDR;
return x;
}
and this is main function
#include <avr/io.h>
#include "UARTInterface.h"
int main(void)
{
DDRC=0xFF;
uartinit();
while(1)
{
unsigned char *y;
y=uartrecieve();
if(strcmp(y,"on")==0)
{
PORTC=0xff;
}
//uartsend(y);
//TODO:: Please write your application code
}
}
There are a few problems with your code:
1. You're not allocating any space for the received characters. You have a global unsigned char *x (which is not initialised) that you dereference and assign values to, then increment - this is just overwriting random positions in memory.
You should instead assign some space by creating an array from the calling function (main in this case) and passing a pointer to uartreceive along with the size of the buffer
unsigned char y[20]; // in main
unsigned char len;
len = uartreceive(y, 20);
...
Then (note this is untested)
unsigned char uartrecieve(unsigned char *x, unsigned char size)
{
unsigned char i = 0;
if (size == 0) return 0; // return 0 if no space
while (i < size - 1) { // check space is available (including additional null char at end)
unsigned char c;
while ( !(UCSRA & (1<<RXC)) ); // wait for another char - WARNING this will wait forever if nothing is received
c = UDR;
if (c == '\0') break; // break on NULL character
x[i] = c; // write into the supplied buffer
i++;
}
x[i] = 0; // ensure string is null terminated
return i + 1; // return number of characters written
}
Each time you call this function, it will overwrite the previous contents of rx_buffer so make sure you are finished using it first. Read up on arrays, pointers and strings if you're not certain what's happening here.
Better yet would be to pass in a pointer to uartreceive so that the calling function can supply the area of memory
2. It is unlikely that your serial terminal software will be sending NULL terminated strings by default (i.e. with '\0' at the end), normally it would send a new-line ('\n') character. I believe realterm can do this, but it's worth checking.
3. Reading from UDR will clear the RXC flag, allowing the AVR to write another character into UDR, so reading from UDR twice in a row is probably a bad idea
UBBRL must be written AFTER UBRRH to ensure atomic operation (ie : copy full 16 bits of ubrr AT THE SAME TIME on baud rate divisor.
This is indicated on ATMega doc (for example ATMEGA16 doc http://ww1.microchip.com/downloads/en/devicedoc/doc2466.pdf page 168) :
Writing UBRRL will trigger an immediate update of the baud rate prescaler
Atmel example are written this way
void USART_Init( unsigned int ubrr){
/* Set baud rate */
UBRRH = (unsigned char)(ubrr>>8);
**UBRRL = (unsigned char)ubrr;**
/* Enable receiver and transmitter */
UCSRB = (1<<RXEN)|(1<<TXEN);
/* Set frame format: 8data, 2stop bit */
UCSRC = (1<<URSEL)|(1<<USBS)|(3<<UCSZ0);
}*
When you write the wrong order (UBRRH last), UBRRH is not updated until the next UBRRL write. Doesn't mater most of the time as UBRRH = 0 the most of the time (until BAUD > 100bit/s)
Related
I have been working through this problem for quite some time now and have succeeded in getting a partial mark. I would like to know what is wrong with the code that I have, that is preventing me from succeeding under certain conditions
I need one arduino to communicate with another one by sending a string of characters. So far I have succeeded in sending and receiving some data but think that I may be having an issue with the buffer I have set up in my uart_receive_string() function. I will provide all of the necessary information and code needed in order to test this, just let me know if any more info is required and Ill be happy to provide.
Here is a link to the tinkercad driver: https://www.tinkercad.com/things/eUZqkaIHp6J
Just click "Copy and Tinker" and hit the code button up top in order to paste the below code into it. You will need to paste the code into both ardunios by selecting them via the drop down box.
This is the criteria for the question I am working on:
This is the output I should receive in the test driver provided:
Here is the current code that I have implemented:
It is what needs to be copied into tinkercad for both arduino's
#include <stdint.h>
#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
void uart_putbyte(unsigned char data);
int uart_getbyte(unsigned char *buffer);
/*
** Define a function named uart_send_string which transmits the contents of
** a standard C string (i.e. a null-terminated char array) over UART. The
** function should iterate over the characters in the array, using a cast to
** convert each to an unsigned char, and transmitting the resulting byte via
** uart_putbyte. The end of the string should be signalled by sending a single
** null byte. That is, the number 0, not the character '0'.
**
** Param: str - string to be transmitted.
**
** Returns: Nothing.
*/
// vvvvvvv I need help with this vvvvvvv
void uart_send_string(char str[])
{
int i = 0;
char ch;
do{
ch = str[i];
uart_putbyte(ch);
i++;
}while(ch != '\0');
}
/*
** Define a function named uart_receive_string which uses uart_getbyte to fetch
** the contents of a standard C string (i.e. a null-terminated char array)
** from UART. The function should wait for characters, and must not return
** until a complete string has been retrieved.
**
** Note that uart_getbyte will return 1 if a byte is available, and zero
** otherwise. Therefore, to fetch a byte and store it in a variable named x,
** you will need to use a construct of the form:
** unsigned char x;
** while (! uart_getbyte(&x)) {
** // Do nothing.
** }
**
** Param: buffer - a char array which has capacity to store a string
** containing at most (buff_len-1) characters. If more than (buff_len-1)
** characters are received, the first (buff_len-1) of them should be
** stored consecutively in the buffer, and any others discarded. The
** string must be terminated correctly with a null terminator in all
** circumstances.
**
** Param: buff_len - an int which specifies the capacity of the buffer.
**
** Returns: Nothing. However, up to buff_len elements of buffer may have been
** overwritten by incoming data.
*/
//vvvvvvv I need help with this vvvvvvv
void uart_receive_string(char buffer[], int buff_len)
{
int i = 0;
unsigned char ch;
while(!uart_getbyte(&ch))
{
if(ch == 0)
{
break;
}
if(i < buff_len-1)
{
ch = buffer[i];
uart_putbyte(ch);
i++;
}
}
buffer[i]=0;
}
/*
***************************************************************************
** Initialise UART.
***************************************************************************
*/
void uart_init(void) {
UBRR0 = F_CPU / 16 / 9600 - 1;
UCSR0A = 0;
UCSR0B = (1 << RXEN0) | (1 << TXEN0);
UCSR0C = (3 << UCSZ00);
}
/*
**************************************************************************
** Send one byte, protecting against overrun in the transmit buffer.
**
** Param: data - a byte to be transmitted.
**
** Returns: Nothing.
***************************************************************************
*/
#ifndef __AMS__
void uart_putbyte(unsigned char data) {
// Wait for empty transmit buffer
while (!(UCSR0A & (1 << UDRE0)));
// Send data by assigning into UDR0
UDR0 = data;
}
#endif
/*
***************************************************************************
** Attempt to receive one byte, returning immediately to sender.
**
** Param: buffer - the address of a byte in which a result may be stored.
**
** Returns: If a byte is available returns 1 and stores the incoming byte in
** location referenced by buffer. Otherwise returns 0 and makes no other
** change to the state.
***************************************************************************
*/
#ifndef __AMS__
int uart_getbyte(unsigned char *buffer) {
// If receive buffer contains data...
if (UCSR0A & (1 << RXC0)) {
// Copy received byte from UDR0 into memory location (*buffer)
*buffer = UDR0;
//
return 1;
}
else {
return 0;
}
}
#endif
/*
***************************************************************************
** Implement main event loop.
***************************************************************************
*/
void process() {
// Use two devices, as indicated in the supplied TinkerCad model. One
// device acts as the sender (is_sender = 1), the other as receiver
// (is_sender = 0). Change this to set the role accordingly.
const int is_sender = 1;
if (is_sender) {
static char * messages_to_send[] = {
"", // Empty string
"A", // String with one symbol.
"Hello from CAB202!", // Multiple symbols
"1234567890abcdefghijklmnopqrstuvwxyz", // Longer than buffer size.
NULL, // End of list
};
static int next_message = 0;
uart_send_string(messages_to_send[next_message]);
next_message ++;
if (messages_to_send[next_message] == NULL) next_message = 0;
_delay_ms(300);
}
else {
#define BUFF_SIZE 20
char buffer[BUFF_SIZE];
uart_receive_string(buffer, BUFF_SIZE);
uart_send_string(buffer);
uart_putbyte('\r');
uart_putbyte('\n');
}
}
int main(void) {
uart_init();
while (1) {
process();
}
return 0;
}
The areas of this code that I am required to work on are these:
This is needed to send the data:
void uart_send_string(char str[])
{
int i = 0;
char ch;
do{
ch = str[i];
uart_putbyte(ch);
i++;
}while(ch != '\0');
}
This is needed to receive the data:
void uart_receive_string(char buffer[], int buff_len)
{
int i = 0;
unsigned char ch;
while(!uart_getbyte(&ch))
{
if(ch == 0)
{
break;
}
if(i < buff_len-1)
{
ch = buffer[i];
uart_putbyte(ch);
i++;
}
}
buffer[i]=0;
}
I am really sorry if this is hard to understand. Ill do my best to clarify any additional information that is needed. I just need to figure out what I am doing incorrectly.
I'm trying to debug a driver that I'm writing for a UART that reads a string of chars from a serial console until the user press 'l'. The function is called 'getstring()' below.
I want to examine the contents of a status register to see which bits are set. The status register is offset by 2. I need to print it when 'getstring()' is called. I can use printf().
This is the register map for the UART.
When I call the How could I print out the contents of a register in c?
#define UART 0x00080000
void getchar(char *str) {
volatile uint32_t *uart = (volatile uint32_t*) UART;
char c = 0;
do
{
while ((uart[2] & (1<<7)) == 0);
c = uart[0];
*str++ = c;
}
while (c!='l');
}
`
To convert from binary to an ASCII string of ones and zeroes, simply do this:
uint32_t local = *uart;
for(size_t i=0; i<32; i++)
{
*str = (local & (1u << 31-i) ? '1' : '0';
str++;
}
*str = '\0';
long time I try to implement FatFs module, but its gets crazy after every sd card formatting. (different errors each time). I've decided to go lower and check my SDCard drivers and I've noticed that when I read some (but ALWAYS THE SAME) of sector's field I receive garbage and I can't interpret it.
My driver's test looks like below:
#if 1 //SD Card Test
uint16_t i;
uint8_t CSD; //card capacity
uint8_t CID; //card id
uint8_t Buffer[512]; //this buffer is written to card
for(i=0; i<512;i++)
{
Buffer[i]=i;
}
SD_CardInit(); //card initialization
CSD = SD_Read_CSD(); //read capacity
CID = SD_Read_CSD(); //read ID
SD_WriteBlock(0x200, Buffer, 512); //write Buffer under 0x200 (512) address
SD_Read_Block(0x200); //read buffer from 0x200
CSD = 0;
#endif
I put breakpoint at CSD = 0; line. Here is what I get after writing and reading the same address:
Buffer is filled with numbers from 0 to 255 (two times, because of overflow).
To reduce noise I've minimized SPI frequency to minimum. Nothing changed. But in fact that wrong numbers appears ALWAYS under the same table index and with the same values suggest that is no problem with noise, but some error.
It looks like drivers works properly, because they write and read most of the fields, but always in the same it answer with garbage. I can't find any trace what could I do wrong. Do I write under some permitted address? When I read any random address I receive the same result.
I include functions: read and write (SD_Sector is a global buffer)
WRITE
// Write block of data to the SD card
// input:
// addr - start address of the block (must be power of two)
// pBuf - pointer to the buffer with data
// len - buffer length
// return: SDR_xxx
SDResult_TypeDef SD_WriteBlock(uint32_t addr, uint8_t *pBuf, uint32_t len)
{
uint32_t wait;
uint16_t CRC_loc; // Calculated CRC16 of the block
uint16_t i;
uint8_t cmdres, response, temp;
SDCard_privChipSelect();
// Calculate 16-bit CRC
CRC_loc = CRC16_buf(pBuf,len);
// SDSC card uses byte unit address and
// SDHC/SDXC cards use block unit address (1 unit = 512 bytes)
// For SDHC/SDXC card addr must be converted to block address
#if 0 //TODO: reconsider PiechotM, do not work with that line
if (SD_CardType == SD_HIGH_CAPACITY_SD_CARD) addr >>= 9;
#endif
response = SD_SendCmd(SD_CMD_WRITE_SINGLE_BLOCK,addr); // CMD24
if (response != 0x00)
{
// Something wrong happened, do nothing
return response; // SD_CMD_READ_SINGLE_BLOCK command returns bad response
}
else
{
wait = 0; response = 0;
while (++wait <= 512/*0x1ff*/ && response == 0xff)
{
temp = 0xFF;
SD_Send( temp );
response = SD_Recv();
}
if (wait >= 0x1ff) return 0xff;
// Send start block token
SD_Send(SD_TOKEN_START_BLOCK);
// Send data block
for (i = 0; i < len; i++)
{
uint8_t temp;
SD_Send( *pBuf++ );
}
// Send CRC
SD_Send(CRC_loc >> 8);
SD_Send((uint8_t)CRC_loc);
}
// Get response from the SD card
cmdres = SD_Recv();
cmdres &= 0x1f;
if (cmdres != SD_TOKEN_DATA_ACCEPTED)
{
// Data block rejected by SD card for some reason
// Release SD card
SD_Send(0xff);
SDCard_privChipDeSelect();
if (cmdres & SD_TOKEN_WRITE_CRC_ERROR) return SDR_WriteCRCError;
if (cmdres & SD_TOKEN_WRITE_ERROR) return SDR_WriteErrorInternal;
return SDR_WriteError;
}
// Wait while the SD card is busy by data programming
wait = 0x7fff; // Recommended timeout is 250ms (500ms for SDXC)
do
{
cmdres = SD_Recv();
} while (cmdres == 0 && --wait);
// Provide extra 8 clocks for the card (from SanDisk specification)
SD_Send(0xff);
// Release SD card
SDCard_privChipDeSelect();
// Must send at least 74 clock ticks to SD Card
for (wait = 0; wait < 10; wait++) SD_Send(0xff);
return SDR_Success;
}
READ
// return:
// 0x00 -- read OK
// 0x01..0xfe -- error response from CMD17
// 0xff -- timeout
uint8_t SD_Read_Block(uint32_t addr)
{
uint32_t wait;
uint16_t i;
uint8_t response;
SDCard_privChipSelect();
#if 0 //TODO: reconsider PiechotM, do not work with that line
if (SD_CardType != SD_HIGH_CAPACITY_SD_CARD) addr <<= 9; // Convert block number to byte offset
#endif
response = SD_SendCmd(SD_CMD_READ_SINGLE_BLOCK,addr); // CMD17
if (response != 0x00)
{
// Something wrong happened, fill buffer with zeroes
for (i = 0; i < 512; i++) SD_sector[i] = 0;
return response; // SD_CMD_READ_SINGLE_BLOCK command returns bad response
}
else
{
wait = 0; response = 0;
while (++wait <= 0x1ff && response != 0xfe) response = SD_Recv();
if (wait >= 0x1ff) return 0xff;
// Read 512 bytes of sector
for (i = 0; i < 512; i++) SD_sector[i] = SD_Recv();
}
// Receive 16-bit CRC (some cards demand this)
SD_CRC16_rcv = SD_Recv() << 8;
SD_CRC16_rcv |= SD_Recv();
// Calculate CRC16 of received buffer
SD_CRC16_cmp = CRC16_buf(&SD_sector[0],512);
SDCard_privChipDeSelect();
// Must send at least 74 clock ticks to SD Card
for (wait = 0; wait < 8; wait++) SD_Send(0xff);
return 0;
}
/* -------------------- WRITE SECTION ------------------------------*/
/* Lonely Wolf library imported */
/*------------------------PiechotM----------------------------------*/
// Send buffer to the SD card
// input:
// pBuf - pointer to the buffer
// len - length of the buffer
// return: last response from SD card
void SD_WriteBuf(uint8_t *pBuf, uint16_t len)
{
while (len--) SD_Send(*pBuf++);
}
I will be very thankful for your help!
Best regards,
Maks Piechota
I have a 3-byte array that buffers incoming bytes through the Serial port. Once it is full I want to use the bytes to call a function that takes a byte and an int as a parameter. This should theoretically not be a problem, but for some reason the bytes are not being converten into an int properly. Here is the code I have:
// for serialEvent()
uint8_t buffer[3] = {0, 0, 0};
uint8_t index = 0;
void serialEvent() {
while (Serial.available()) {
if (index > 2) {
// buffer is full so process it
uint16_t argument = (uint16_t)buffer[1];
argument <<= 8;
argument |= buffer[2];
processSerial(buffer[0], argument);
index = 0;
}
buffer[index] = Serial.read();
index++;
}
}
void processSerial(uint8_t action, uint16_t argument) { ... }
The problem appears to be in the line where the first bit is shifted to the left to make space for the second one. I have tried outputting the variable over the Serial port again and after the bit shift operation, it is 0.
The same thing happens when I try to replace the bit shift operation with a multiplication by 256 (which has the same result in theory).
Irritatingly, when I assign a static value like so, everything works fine:
uint16_t argument = 0x00CD;
argument <<= 8;
Is this a type cast problem? Am I missing something here?
Have not found the solution as to why this happens, but using the word() function does exactly what I want:
uint16_t argument = word(buffer[1], buffer[2]);
processSerial(buffer[0], argument);
Annoyingly, it is defined as:
unsigned int makeWord(unsigned char h, unsigned char l) { return (h << 8) | l; }
#include <reg51.h>
#include "_LCD_R8C.c"
#define INPUT_LENGTH 11
char input[INPUT_LENGTH]; /* The input from the serial port */
int input_pos = 0; /* Current position to write in the input buffer */
int main()
{
int i;
lcd_init();
lcd_clear();
SCON = 0x50;
TMOD = 0x20; /* timer 1, mode 2, 8-bit reload */
TH1 = 0xFD; /* reload value for 2400 baud */
TR1 = 1;
TI = 1;
RI = 1;
while (1 == 1)
{
/* read the next character from the serial port */
input[input_pos++] = getCharacter ();
/* send it back to the original sender */
for (i = 0; i <= input_pos; i++)
{
lcd_print_b(input[i]);
}
}
}
char getCharacter(void)
{
char chr[INPUT_LENGTH]; /* variable to hold the new character */
while (RI != 1) {;}
chr[input_pos++] = SBUF;
RI = 0;
return (chr);
}
I tried displaying the no's which I am receiving from rs232 which is read by the rfreader.
but I am getting the wrong value on the display i.e 002100 instead of 0016221826. but on the hyper terminal I am getting the exact correct value with a $ included at the satrting i.e $0016221826.
First of all, you really need to adopt a sane indention style, this code is very hard to read.
The problem with your code is that you read an array of user input into a local array "chr", then return the address of that array to main, instead of a character. main() does not expect an address, it expects a character. And regardless of that, the array "chr" is invalid once you have left the function anyhow.
Your printing for loop is also incorrect and doesn't make any sense. You keep printing all characters, over and over, each time you receive a new one.
There might be other issues with the hardware or MCU, I just fixed the most obvious software bugs.
#include <reg51.h>
#include "_LCD_R8C.c"
#define INPUT_LENGTH 11
int main()
{
char input[INPUT_LENGTH]; /* The input from the serial port */
int input_pos = 0; /* Current position to write in the input buffer */
lcd_init();
lcd_clear();
SCON = 0x50;
TMOD = 0x20; /* timer 1, mode 2, 8-bit reload */
TH1 = 0xFD; /* reload value for 2400 baud */
TR1 = 1;
TI = 1;
RI = 1;
while(1)
{
/* read the next character from the serial port */
if(input_pos < INPUT_LENGTH) /* check for buffer overflow */
{
input[input_pos] = getCharacter();
lcd_print_b(input[input_post]); /* only makes sense to print each character once */
input_pos++;
}
}
char getCharacter (void)
{
char chr /* variable to hold the new character */
while (RI != 1)
;
chr = SBUF;
RI = 0;
return(chr);
}