ATmega644 USART software/programming error - Page 1
Forum section: electronics / general

author: MrAureliusR
author_rank: Unknown
timestamp: October 08, 2013, 06:41:09 am
content: I was supposed to go to bed by midnight, but when I went out for a smoke I noticed that my package from SparkFun had (FINALLY) gotten here... how I didn't notice it the rest of the day is a question in itselfMy new Olimex 40-pin AVR dev board was in there, which I'm excited about because it's got RS-232 built in, level-shifter and all, and it should make serial communication between MCU and any peripheral easy... or so I thought. The other cool thing is it has two headers, one for ICSP and one for JTAG. I'm using the JTAG header, just for reference.I popped an ATmega644 into the socket, fired up Atmel Studio, wrote a quick LED blink/button press program to test everything out and it worked without a hitch. Excellent, I thought, let's get something cooking here. I soldered the RX/TX pins to the inputs of the MAX232, hooked a serial cable up to my computer, and wrote this program:http://aurelius.pastebay.net/1319247(Note: the PRR line that is commented out is part of my debugging... wanted to see if the power reduction feature had anything to do with it)The strange thing is, it will program the MCU, but then I get a strange error I've never seen before: "Timed out waiting for device to reset". I've been trying to figure out why the MCU wouldn't reset... it was able to hold it in reset before, when programming the LED blink program before. I'm fairly sure my code is all accurate, as I spent quite a while reading about the USART registers and how to set them up... (spent much time reading when I should be sleeping!) Then I check my serial terminal, and nothing is happening. It's almost like the MCU is freezing up? Or getting stuck in a loop? I don't know how that's possible though. Perhaps I set up a register wrong?? I'm really stumped. It should be so simple. I was reading a tutorial regarding the ATmega32 but I double-checked all the register names as most of them were different. And I of course adjusted all the values for my crystal (14.7456, which is also supposed to make USART easy as it cleanly divides by most baud rates -- I'm using 19200, which when you go through the formula, yields 47 as the UBRR0).Anyway, if someone spots a glaring error, please let me know! I'm stuck already, and I've only had the board a couple hours. Now I need to go sleep! Thanks in advance for any help offered

author: Psi
author_rank: Super Contributor
timestamp: October 08, 2013, 06:55:57 am
content: I'm just about to go out, but here's two things that occurred to me while reading your post.1) You say the dev board has "RS-232 built in" and you "soldered the RX/TX pins to the inputs of the MAX232, hooked a serial cable up to my computer"If it has RS-232 built in then all it needs is a direct connection from the board TX/RX to a PC com port, it almost sounds like your using a max232 when it already has one itself? Which of cause wont work because you'd be feeding +/-12 into the 5v max232 inputs.Also check the directions of your connections. ie TX -> RX and not TX -> TX etc..2) Check the MCU is getting proper 5V power on VCC. I know its a simple thing but MCUs are notorious for powering up and running purely from the power coming in on their i/o (like the programming pins).Also, an unpowered MAX232 will leak power from its inputs onto its VCC which then gets onto the VCC of everything else.It can easy fool you. The chip will sometimes program fine and even flash an led and other times it will fail to flash correctly and throw timeout errors.

author: RMS95
author_rank: Contributor
timestamp: October 08, 2013, 06:59:59 am
content: 2 things:Check if the reset pin is actually pulled down, best way is to check all the programming pins with a logic analyzerSet everything (hardware and software) back to the state you received it.QuoteMeans, it has pulled the reset pin, nothing seems to happen. (But I guess you already knew that.)

author: MrAureliusR
author_rank: Unknown
timestamp: October 08, 2013, 07:02:00 am
content: 1) the olimex 40-pin dev board has a MAX232 connected to a DB9. All you have to do is solder wires from the RX/TX of the micro to the RX/TX 5V input of the MAX232. It seems strange that they don't have these connections already made on the board but I triple checked and they aren't by default. Which explains why they have holes waiting for you to solder to beside the MAX labelled rx, tx etc.2) I'm %1000 sure its getting VCC. This board has a separate DC power barrel for 6-9VAC or 9-12VDC. I'm using 12VDC and its regulating it down to 5V no problem.@RMS95 yeah I figured it was pulling the line low and nothing was happening. But it programs it successfully if I use the separate programming window - which is strange because I thought it had to be held in reset while programming. It's when I try to hit the "play" button (start debugging) that it fails.. However even when it successfully programs it still sits there doing nothing. Ill fire up my digital channels tomorrow and check that to be sure but when I push the included reset button on board it resets the other programs fine...

author: MrAureliusR
author_rank: Unknown
timestamp: October 09, 2013, 02:51:44 am
content: Strange computers...When I got home today, I restarted my computer, compiled the EXACT SAME code, loaded it, and it worked. First time. Bizarre...

author: dannyf
author_rank: Super Contributor
timestamp: October 09, 2013, 10:33:39 am
content: Quote"Bugs are rarely where you think they are."A smart programmer told me that.

author: MrAureliusR
author_rank: Unknown
timestamp: October 10, 2013, 09:55:07 pm
content: Okay, so after getting the USART working, my next step is to interface the ATmega644 to both my serial terminal on the PC, and also to a real-time clock I2C chip I have. It seems simple enough, and luckily I've discovered more and more about how awesome it is to have a JTAG debugger (AVR Dragon) which is helping. I'll post a link to a paste of my whole code, but the problem I'm having is in the following section.The main() function calls the init functions for both USART and I2C (to set up the baud rates, and prescalers, etc etc) which works fine. I checked the registers while it was running just to be sure that's not the problem, and it isn't. Then (because I'm also learning interrupts for the first time) I call sei() to enable global interrupts, and then it just sits there until something is received on the USART (ISR(USART0_RX_vect) as seen below. This is where there's problems. The interrupt works fine -- if I send a character over the serial port, it immediately calls the interrupt. But then it only seems to do part of the code. It will read the data out of the USART data buffer, then I call a routine to check and see what instruction was sent, and depending on that instruction it either reads the date or debug info from the RTC. It gets to the if statement (and because the received data from the USART is always 0 (another bug I'm trying to solve)) it jumps to the else{}, which is then proceeds to do. It calls IIC_read, but once it's done that, it doesn't do any of the instructions after that! I know that obviously there's more problems than just this, but I've been debugging for a while now and I really can't get my head wrapped around what's happening. But I'm trying -- I really am!!!Any and all help is, as usual, greatly appreciated! Please point out any glaring errors. I'm pretty confident my UART code is correct, fairly confident the IIC code is correct, but I'm still learning about interrupts and how they're handled so I'm probably missing something. The volatile tags are just for debugging, so the variable doesn't get optimized away and then I can't watch what's happening.Psate:http://aurelius.pastebay.net/1320021Code:ISR(USART0_RX_vect){volatile int input = 0;int action;volatile int output = {0, 0, 0, 0, 0};input = USART_read();action = decode(input);if (action == 50) // if time is requested then get register 0x02{output = IIC_read(0x02); // hoursoutput = IIC_read(0x01); // minutesoutput = IIC_read(0x00); // seconds}else{output = IIC_read(0x07);output = IIC_read(0x06);output = IIC_read(0x05);}USART_write(output); // send whatever we got to USART (still debugging this at this point)USART_write(output);USART_write(output);UCSR0B |= (1<<RXCIE0); // re-enable UART receive interruptsei();}Datasheet for the Timekeeper RAM:https://my.st.com/st-extranet-web-active/static/active/en/resource/technical/document/datasheet/CD00189496.pdf

author: Jope
author_rank: Regular Contributor
timestamp: October 10, 2013, 10:32:12 pm
content: In yourmain(), put thesei()call after theIIC_init().After you enable the UART RX interrupt inUSART_init(), it is possible that an interrupt occurs before yourIIC_init()is called, so your two-wire interface is never enabled and yourIIC_read()never returns because it hangs while waiting in one of the while loops.You can also delete thesei()andUCSR0B |= (1<<RXCIE0)from your USART ISR. The global interrupt bit is set again automatically when returning from the ISR (as part of the C library code) and the RXCIE0 bit is not cleared upon entering the ISR, so you don't need to reenable it.

author: dannyf
author_rank: Super Contributor
timestamp: October 10, 2013, 10:40:28 pm
content: QuoteThe interrupt doesn't work. It is supposed to be quick / fast, and typically without calling other functions. Typical usage is to set a flag and process the flag in the main loop.

author: Psi
author_rank: Super Contributor
timestamp: October 10, 2013, 10:48:37 pm
content: Quote from: dannyf on October 10, 2013, 10:40:28 pmIf all you do is set a flag on RX char you might as well check it from the main loop and forget the interrupt.You want some code to store the value, perhaps append it to an array if its a multi-byte message etc..Then flag when the message is complete and ready for processing.

author: elgonzo
author_rank: Unknown
timestamp: October 10, 2013, 11:30:46 pm
content: In your IIC_read(...) function, after loading the byte to send into TWDR, you seemed to forget to load the appropriate control signals into TWCR,clearing the TWINT flag(and TWSTA, if necessary).The TWINT flag is cleared by writing a "1" to the respective bit in TWCR.See details in the Atmega644 data sheet, chapter"19.6. Using the TWI".I quote from this chapter:However, it is important that the TWINT bit is set in the value written. Writing a one toTWINT clears the flag. The TWI will not start any operation as long as the TWINT bit inTWCR is set. Immediately after the application has cleared TWINT, the TWI will initiatetransmission of the address packet.

author: MrAureliusR
author_rank: Unknown
timestamp: October 11, 2013, 01:44:35 am
content: Quote from: Jope on October 10, 2013, 10:32:12 pmThanks, I made both of these changes. I wasn't sure whether or not global interrupts were re-enabled, because it seems like they weren't being re-enabled. After the ISR is called once, and then it exits, the I bit in the MCU status register never seems to get set again. However that's more likely due to whatever the major problem is that's causing the ISR to exit prematurely.Quote from: Psi on October 10, 2013, 10:48:37 pmAgain, I wasn't sure about any of this. As I mentioned before, this is the first time I've programmed using interrupts. I suppose most of what I have in the ISR could get moved to main(). Should I simply declare a global variable and have main() check to see if it's set? And then have the ISR set it whenever it's done copying the input from UDR0? Or is there a better way (I'm sure there probably is)?All I'm trying to do right now is get this simple program running. Then I'll optimize it and add more usefulness. I will have a Bus Pirate soon so that will help in learning more about I2C. I need to add a function to program the current date and time to the RTC on reset. I used to be very good at writing code to manipulate/concatenate/edit strings, but that was in C++. I'm not really sure what the macros/functions are in C. For example, string doesn't seem to be a valid variable type. What should I use for multiple characters? A char array? That seems a bit clunky.I'm going to do some more debugging and I'll keep an eye on this thread. Thanks a bunch everyone -- I truly appreciate the help! I'm always trying to learn, and most of the websites I've found have either bad, contradictory or incomplete info... so I'm trying to do the best I can with what I have.EDIT: I just remembered what you said about IIC_read(). Do you mean that I should add TWCR |= (1<<TWINT); before every byte is sent? I re-read that part of the datasheet (believe me, I've been reading that datasheet a lot!!) but it's confusing because at the beginning of the function I DO have a 1 written to TWINT. Does it get cleared after every send? Not to mention, that paragraph seems contradictory. "Writing a one to TWINT clears the flag." "The TWI will not start any operation as long as the TWINT bit in TWCR is set." Doesn't "is set" usually mean true/written to 1?? And then it says after the application has cleared TWINT it will initiate transmission. A little clarification would help a lot here...

author: elgonzo
author_rank: Unknown
timestamp: October 11, 2013, 02:12:27 am
content: Quote from: MrAureliusR on October 11, 2013, 01:44:35 amYou should reset the TWINT flag after TWDR is set, otherwise the TWI will not start with the transmission. Just setting TWDR doesn't start the data transfer, if the datasheet is correct.You should try doing something in the order like this in your IIC_Read function:TWCR |= (1<<TWINT) | (1<<TWSTA)| (1<<TWEN)while (!(TWCR & (1<<TWINT)));TWDR =slave address + write bitTWCR = (1<<TWINT) | (1<<TWEN)while (!(TWCR & (1<<TWINT)));TWDR =address to read fromTWCR = (1<<TWINT) | (1<<TWEN)while (!(TWCR & (1<<TWINT)));... and so on and so forth...I guess, you would need to adapt IIC_Write similarily.(The Atmega644 datasheet i used is the curren revision 2593O-AVR-02/12)

author: elgonzo
author_rank: Unknown
timestamp: October 11, 2013, 02:18:25 am
content: Quote from: MrAureliusR on October 11, 2013, 01:44:35 amYes it is counter-intuitive and confusing for beginners to reset a bit by writing an "1" into it.But actually i am quite used to it from the IRQ programming of the old home computers of yore...

author: MrAureliusR
author_rank: Unknown
timestamp: October 11, 2013, 02:25:26 am
content: EDIT: OH MY GOD I'M A FLAMING IDIOT... when I started debugging an hour ago I forgot to turn the power supply on!!! So the ATmega has power, the AVR dragon has power, but the Timekeeper doesn't ... DOH... even after turning the power on, it's still getting stuck at the same spot as mentioned below... lol that's strange. I guess it doesn't even need the RTC there to screw up...so here's the revised IIC_read):int IIC_read(int addr){TWCR |= (1<<TWINT)|(1<<TWSTA)|(1<<TWEN); // enable TWINT and generate a start conditionTWDR = 0b11010000; // send slave address plus write bitTWCR = (1<<TWINT)|(1<<TWEN);while (!(TWCR & (1<<TWINT))); // wait for byte to sendTWDR = addr; // send address that we are reading fromTWCR = (1<<TWINT)|(1<<TWEN);while (!(TWCR & (1<<TWINT))); // wait for byte to sendTWCR |= (1<<TWSTA); //repeated startTWCR = (1<<TWINT)|(1<<TWEN);while (!(TWCR & (1<<TWINT))); // wait for byte to sendTWDR = 0b11010001; // slave address plus READ bitTWCR = (1<<TWINT)|(1<<TWEN);while (!(TWCR & (1<<TWINT))); // wait for byte to sendTWDR = 0xFF; // shift junk data out to shift in the data byte requestedTWCR = (1<<TWINT)|(1<<TWEN);while (!(TWCR & (1<<TWINT))); // wait for byte to sendTWCR |= (1<<TWSTO); // generate a STOP condition to bring bus back to restTWCR = (1<<TWINT)|(1<<TWEN);while (!(TWCR & (1<<TWINT))); // wait for byte to sendreturn TWDR;}Now, it's getting stuck on the first "while (!(TWCR & (1<<TWINT)))", the one right after the slave address and write bit. It just sits there forever. I'm trying to understand what this while loop actually does. Obviously part of it is waiting for TWINT to be cleared, but why is TWCR in there? Why is it evaluating the entire register ANDed with TWINT?!? I know the whole statement is ! but still... confusing.EDIT #2: So I busted out the oscilloscope. I am probing SDA, and when I send a character across the UART, the SDA line goes low... and just stays low. No action whatsoever. So even though the code has gotten to the while loop, the hardware hasn't even sent the first byte. I think it's sent the start condition but that's it. Strange. I guess I'll bust out the logic analyzer and watch everything a bit more closely.

author: elgonzo
author_rank: Unknown
timestamp: October 11, 2013, 02:42:13 am
content: Quote from: MrAureliusR on October 11, 2013, 02:25:26 amIt seems, the I2C slave device doesn't send a ACK signal.The reason why there is no ACK signal can be manifold: wrong slave address, wiring mistake, defective part, electrical issues, violation of the I2C protocol from either of the involved parts, etc...etc...Do you have a logic analyzer to see what is happening on the wires?Without some test gear to watch the action on the wires, troubleshooting will become random poking in the dark.Regarding the syntax of the source code, you have to understand that TWCR is a register, whereas TWINT is just a number (the number 7, which is the bit position of the TWINT bit in TWCR).Code:while (!(TWCR & (1<<TWINT)))would actually be equivalent to:Code:while (!(TWCR & (1<<7)))TWCR & (1<<7) obviously yields the value of bit 7 in TWCR (which is the TWINT bit). Since the bit position is 7, possible values for this expression are 128 when the bit is set, and 0 when the bit is not set.

author: elgonzo
author_rank: Unknown
timestamp: October 11, 2013, 03:08:04 am
content: Perhaps it's not working because you missed a while loop after the START condition has been set...int IIC_read(int addr){TWCR |= (1<<TWINT)|(1<<TWSTA)|(1<<TWEN); // enable TWINT and generate a start conditionwhile (!(TWCR & (1<<TWINT))); // wait for START condition transmittedTWDR = 0b11010000; // send slave address plus write bitTWCR = (1<<TWINT)|(1<<TWEN);[...snip...]

author: MrAureliusR
author_rank: Unknown
timestamp: October 11, 2013, 03:23:32 am
content: I added that while loop to the start condition, by the way. Which is probably why it's getting as far as described below...So I thought that when I send a character over UART, that the lines were just going low and staying low (and they are staying low) BUT now that I'm using the digital channels (logic analyzer feature) on my scope, I can see that in the time I thought it was just going low, the ATmega sends the start condition, then the slave address and read bit, it receives the ACK from the slave, then it starts clocking for the slave. The slave sends what looks like a full byte (0b00000111) but then there seems to be an extra clock cycle, and then a couple weird pulses on the data line (they're not interference though, because they're there every time). I'm not entirely sure what an ACK should look like, but from what I've read (I have a copy of free_electron's Mastering the I2C Bus) the master should let the data line go high, and the slave should pull it low and then let it go high again, almost like a little nod of the head. If that's what's happening at the end (which I think it is) then it seems that the ATmega is the one screwing up, because it's either not recognizing the ACK, or something else is happening. After that ACK, the ATmega stops clocking, and nothing else happens. The lines go low and stay low.By the way, I thought I did my math correctly to set the I2C speed at ~150kHz, but I'm getting a clock speed of almost 400kHz, which is the maximum speed that the RTC chip can handle. I'm not sure what I did wrong, I followed the formula correctly (or so I thought)... perhaps I implemented it wrong? I was thinking maybe if I bring the I2C clock speed down it's likely to be more reliable. I'll try fiddling with that now.See attached a picture of what I'm seeing on the bus.

author: elgonzo
author_rank: Unknown
timestamp: October 11, 2013, 04:06:18 am
content: An ACK (or NACK) will appear on the Logic Analyzer as 9th bit. ACK is a "0" bit, NACK is a "1" bit.However for the ACK (or NACK) master and slave swap the roles (which you cannot see on the LA). So far so good.The problem with the TWI "dying" when trying to do a repeated START, is that the restart code is faulty. ReplaceCode:TWCR |= (1<<TWSTA); //repeated startTWCR = (1<<TWINT)|(1<<TWEN);withCode:TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN); // START repeatand that should work.Regarding the bit rate. I see in your source (IIC_init function) that you enable the TWI first, and then try to set the bit rate.Perhaps, the clock generation is locked when the TWI is enabled, and changes of TWBR don't have effect; but i am not sure about that.Anyway, you can safely comment out the line "TWCR |= (1<<TWEN);", because TWEN is and should be handled in the transmission routine(s).

author: elgonzo
author_rank: Unknown
timestamp: October 11, 2013, 04:23:14 am
content: Looking at the read function, is it correct, that you throw away the 1st byte sent from the RTC?

author: MrAureliusR
author_rank: Unknown
timestamp: October 11, 2013, 04:31:02 am
content: Quote from: elgonzo on October 11, 2013, 04:06:18 amThanks for all the tips! Those couple of changes seem to have made a difference. The logic analyzer is now seeing 4 full bytes sent/received, and decoding them it's slowly starting to work. There's still a few problems (UART doesn't seem to be sending) but I should be able to iron them out.Quote from: elgonzo on October 11, 2013, 04:23:14 amNo, why would I throw away the first byte sent from the RTC? I'm confused

author: elgonzo
author_rank: Unknown
timestamp: October 11, 2013, 04:44:33 am
content: Quote from: MrAureliusR on October 11, 2013, 04:31:02 amAh, okay, never mind. I got confused over the TWDR = 0xff line. Brain fart on my side...Anyway, i am glad that i could help.