Society of Robots - Robot Forum
Software => Software => Topic started by: newInRobotics on February 01, 2012, 05:40:15 PM
-
####################################################
Solved!
All that data misinterpretation was due to comm error as I tried Baud Rate of 9600 running uC @ 1MHz. I looked it up in ATmega48 datasheet and it stated that 9600 Baud Rate produces -7.0% Error when running @ 1MHz. Then I changed uC clock to 8MHz and Baud Rate (in uC and PC) to 250k (which according to datasheet produces 0.0% - and that solved the problem ;D
Thanks for Your help joe61 :)
####################################################
Hello everyone
Recently I started playing around with ATmega48 USART to USB comms. Microcontroller is programmed via AVR Studio 5 and PC side comms are driven by simple C# Form.
Microcontroller is supposed to get byte from PC and pass it back.
#define F_CPU 1000000UL
#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
#include <avr/io.h>
int main(void)
{
UCSR0B |= (1 << RXEN0) | (1 << TXEN0); //Turn ON transmission and reception circuitry
UCSR0C |= (1 << UCSZ01) | (1 << UCSZ00); //Data length of 8-Bit
UBRR0H = (BAUD_PRESCALE >> 8); //Load upper 8-bits of BAUD_PRESCALE value
UBRR0L = BAUD_PRESCALE;
void sendByte(uint8_t inputChar)
{
while((UCSR0A & (1 << UDRE0)) == 0){} //Do nothing until SEND register is empty
UDR0 = inputChar; //Send value of "inputChar"
}
uint8_t receiveByte()
{
while((UCSR0A & (1 << RXC0)) == 0){} //Do nothing until data is received
return UDR0;
}
while(1)
{
uint8_t newChar = receiveByte();
sendByte(newChar);
}
}
PC side (C# Form driven) sends data from TextBox(converted to decimal and then to byte) with a press of a button and puts received data when DataReceived event is detected.
[All GUI stuff goes here]
private void sendBtn_Click(object sender, EventArgs e)
{
if ((sendBox.Text != "") && (serialPort1.IsOpen))
{
byte[] newByte = { Convert.ToByte(Convert.ToDecimal(sendBox.Text)) };
serialPort1.Write(newByte, 0, 1);
System.Console.WriteLine("Byte sent!");
}
}
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
System.Console.WriteLine("Byte received!");
byte[] newByte = new byte[1];
serialPort1.Read(newByte, 0, 1);
getBox.Text = ((int)newByte[0]).ToString();
}
My inputs come back wrong, have a look:
Input Output
0 --> 128
1 --> 129
2 --> 130
3 --> 131
4 --> 132
5 --> 133
... --> ...
up to input = 127 and then:
Input Output
128 --> 128
129 --> 129
130 --> 130
131 --> 131
132 --> 132
133 --> 133
... --> ...
up to input = 255 and then ir crashes on input = 256, as well as input = -1
Can someone please help me understand the reason of such wierd bahavior? Thanks in advance.
N.I.R.
-
void sendByte(uint8_t inputChar)
{
while((UCSR0A & (1 << UDRE0)) == 0){} //Do nothing until SEND register is empty
UDR0 = inputChar; //Send value of "inputChar"
}
uint8_t receiveByte()
{
while((UCSR0A & (1 << RXC0)) == 0){} //Do nothing until data is received
return UDR0;
}
[ ... ]
Why are you defining functions inside of main(). Does that actually compile?
Anyway, the only other thing I noticed is that the busy test in the transmit and receive code looks to be inverted. Try
while(! (UCSR0A & (1 << RXC0)) == 0)
;
I can't speak to the C# code though.
EDIT: Sorry, I've been working too much today. You are testing for it to be false. Never mind. I'd guess the C# code is doing the odd stuff. I have done some C# programming, and remember it to be very quirky about redefining types, or representing one type as another. It was a while ago though. Have you tried just communication with hyperterm?
Joe
-
Hi joe61,
thanks for reply :)
Why are you defining functions inside of main(). Does that actually compile?
It does compile. You can write functions in a function making them accessible only to the parent function and not whole class. And yes - it does compile :) I also tried to write everything in a traditional way without "functions in a function" - results were the same.
Have you tried just communication with hyperterm?
I have tried it with RealTerm (http://realterm.sourceforge.net/) (free alternative of Hyperterminal) and output was the same.
Any other suggestions?
-
Why are you defining functions inside of main(). Does that actually compile?
It does compile. You can write functions in a function making them accessible only to the parent function and not whole class
I was surprised because that's not standard C. Turns out it's a gcc extension, so it likely won't work with other compilers.
Have you tried just communication with hyperterm?
I have tried it with RealTerm (http://realterm.sourceforge.net/) (free alternative of Hyperterminal) and output was the same.
Any other suggestions?
I really don't see anything offhand that looks like a problem. I'll play with it when I get home tonight. The only suggestion I could make at this point is to check the wiring and make sure both sides are using the same bitrate, parity, etc.
The results make me wonder if there's not a type mismatch involved somehow. Like one side using signed chars while the other is using unsigned. I'm too tired to suggest that seriously right now though.
Joe
-
Actually, I would be interested in the output if you print the byte received and sent in hex. I notice you're converting the byte to an int (which is signed in C# if I remember right).
Can you print the raw input and output bytes in hex?
Joe
-
Hi joe61
Thank's for spending Your time to help me. Since getting home from work I tried many different things. I made a conclusion that ATmega somehow sends wrong data back. When C# Form (in debug mode) is paused just before sending data out - byte[] holds correct value; when Form is paused just after getting data from uC - byte[] holds that "weird 128 to 255" value; that only means that data is received the way it is (as opposed to data being misshaped in convertion). Also, when Tx and Rx coming from PC are shorted - data comes back as it should.
Here are HEX values You've asked for:
Byte sent = 0 --> in HEX = 00
Byte received = 128 --> in HEX = 80
Byte sent = 1 --> in HEX = 01
Byte received = 129 --> in HEX = 81
Byte sent = 2 --> in HEX = 02
Byte received = 130 --> in HEX = 82
Byte sent = 3 --> in HEX = 03
Byte received = 131 --> in HEX = 83
Byte sent = 4 --> in HEX = 04
Byte received = 132 --> in HEX = 84
Byte sent = 5 --> in HEX = 05
Byte received = 133 --> in HEX = 85
[...]
Byte sent = 127 --> in HEX = 7F
Byte received = 255 --> in HEX = FF
and then..
Byte sent = 128 --> in HEX = 80
Byte received = 128 --> in HEX = 80
Byte sent = 129 --> in HEX = 81
Byte received = 129 --> in HEX = 81
Byte sent = 130 --> in HEX = 82
Byte received = 130 --> in HEX = 82
[...]
Byte sent = 255 --> in HEX = FF
Byte received = 255 --> in HEX = FF
Another strange thing - the first time PC and uC starts exchanging data, receive event is detected before data is being sent out; have a look at this (exact copy from CMD Window (first and second transmissions)):
Byte received = 128 --> in HEX = 80
Byte sent = 0 --> in HEX = 00
Byte sent = 0 --> in HEX = 00
Byte received = 128 --> in HEX = 80
And another thing, here's samo data converted to binary:
Byte sent = 0 --> in HEX = 00 --> in BIN = 00000000
Byte received = 128 --> in HEX = 80 --> in BIN = 10000000
Byte sent = 1 --> in HEX = 01 --> in BIN = 00000001
Byte received = 129 --> in HEX = 81 --> in BIN = 10000001
Byte sent = 2 --> in HEX = 02 --> in BIN = 00000010
Byte received = 130 --> in HEX = 82 --> in BIN = 10000010
Byte sent = 3 --> in HEX = 03 --> in BIN = 00000011
Byte received = 131 --> in HEX = 83 --> in BIN = 10000011
Byte sent = 4 --> in HEX = 04 --> in BIN = 00000100
Byte received = 132 --> in HEX = 84 --> in BIN = 10000100
It appears that ATmega sets 7thBit to 1. Why that might be?
-
Solved!
All that data misinterpretation was due to comm error as I tried Baud Rate of 9600 running uC @ 1MHz. I looked it up in ATmega48 datasheet and it stated that 9600 Baud Rate produces -7.0% Error when running @ 1MHz. Then I changed uC clock to 8MHz and Baud Rate (in uC and PC) to 250k (which according to datasheet produces 0.0% - and that solved the problem ;D
Thanks for Your help joe61 :)
-
I just compiled your code as posted, and wrote a C program to do the same thing you did. I got results similar to what you show, however I believe the problem was in the driver program. Here's the program I ran on my linux box
#include <stdio.h>
#include <fcntl.h>
int main()
{
int i;
int fd = open ("/dev/ttyUSB0", O_RDWR);
if (fd == -1)
{
perror ("open");
return 1;
}
for (i=30; i<255; ++i)
{
char b;
printf ("writing %d\n", i);
write (fd, (unsigned char*)&i, 1);
read (fd, &b, 1);
printf ("read %d\n", b);
}
}
There are a couple things potentially messing things up there. One is that I'm receiving into a char variable. Whether a char is signed or unsigned is implementation defined, which means you have to check the compiler documentation to see what you're getting. Or declare it as signed char or unsigned char.
The other thing is that I'm printing it out using %d (as a signed int). I'm too tired to remember the promotion rules, but it would have been better for me to specify it precisely.
So I started receiving into an unsigned char (change the declaration of the variable b in the receive loop), and things printed out correctly.
I'd suggest you check how C# changes things in the program that's printing the results.
The results you show of this type
Byte sent = 2 --> in HEX = 02
Byte received = 130 --> in HEX = 82
I suspect are also related to how they're being printed out rather than what's being sent or received. Like I say, I ran the code exactly as you posted it, and I can see that I'm always getting back what I send. What changes is the way it's getting printed out.
-
Solved!
All that data misinterpretation was due to comm error as I tried Baud Rate of 9600 running uC @ 1MHz. I looked it up in ATmega48 datasheet and it stated that 9600 Baud Rate produces -7.0% Error when running @ 1MHz. Then I changed uC clock to 8MHz and Baud Rate (in uC and PC) to 250k (which according to datasheet produces 0.0% - and that solved the problem ;D
Thanks for Your help joe61 :)
Ok, I'm more tired than I thought then. I should have looked at that.
Actually I realize now that I didn't really run your code unmodified. I'm using a board that has a 20MHz resonator on it, and I changed F_CPU to match that.
There's still a lesson in there though about representation vs reality. Stuff like that will bite you sooner or later.
Glad you got it working
Joe
-
Actually I realize now that I didn't really run your code unmodified. I'm using a board that has a 20MHz resonator on it, and I changed F_CPU to match that.
According to datasheet 9600 Baud Rate @ 20MHz produces only 0.2% Error, that's why it gave good results.
Again, thanks for Your help.
-
According to datasheet 9600 Baud Rate @ 20MHz produces only 0.2% Error, that's why it gave good results.
Except that it didn't, until I got the sign right on the char. Still a lesson there.
Again, thanks for Your help.
I wasn't much help unfortunately.
Joe
-
Except that it didn't, until I got the sign right on the char.
In C one unsigned byte is char or uint8_t, in C# it's simply called byte and that was what I was using from the start, so binary data interpretation was not the issue here. In Your case - it was and had nothing to do with Baud Rate Error, still giving "similarly wrong" results.
-
In C one unsigned byte is char or uint8_t,
No, it's implementation defined whether a char is signed or unsigned. To say that an unsigned char (which is, by definition, unsigned) is equivalent to a char may or may not be true depending on the compiler being used. If it's important that it be one or the other then it should be declared as signed char or unsigned char (or one of the many typdefs that are used).
in C# it's simply called byte and that was what I was using from the start, so binary data interpretation was not the issue here.
Again, I'm not sure you understood my point. Is a byte in C# signed or unsigned? I'm saying that needs to be taken into account as well.
I'm happy you got it working, I'm not arguing the fact that it turned out to be the baud rate. I'm only mentioning these other things because it's not unusual for programs to give confusing results when they're not taken into account. It's something to keep in mind.
Joe
-
No, it's implementation defined whether a char is signed or unsigned. [...] It's something to keep in mind.
I don't disagree :) That was the first thing checked I when it failed to output expected results.
In GNU GCC uint8_t is unsigned byte and int8_t is signed byte.
In C# byte is unsigned byte and sbyte is signed byte.