ATmega32 for loop takes forever. - Page 1
Forum section: electronics / general

author: georgian
author_rank: Regular Contributor
timestamp: December 17, 2018, 05:15:40 pm
content: Hello,The project I'm working on is an oscilloscope clock. I want to make two 8bit or 16 bit DAC using resistors and OP AMPs, so I can write a 16 bit integer on 2 ports from the ATmega. For example lower 8 Bits on PORTD and upper 8 bits on PORTB (for x axis)So far I made a simple code using atmel studio 7.0 and it's not working.So, this code should go thorugh all bits up to maximum of 2^16-1 (65535). I have a LED connected to PORTB.7 and one to PORTD.0. To my understang the LEDs should turn off after the for loop ends but it looks like it never ends. The PORTB.7 keeps blinking at about 6.6 Hz. If i have 30000 in the for loop it works. If I have 40000 it dosen't work. There must be something wrong or mabe the µC is to slow. (Atmega32 8Mhz internal)Anybody knows why it's not working? Thank you.Here is the code:Code:#ifndef F_CPU#define F_CPU8000000UL#endif#include <avr/io.h>#include <util/delay.h>int value = 32768;uint8_t lowerBits = 0;uint8_t upperBits = 0;int main(void){DDRD = 0xff;DDRA = 0xff;DDRB = 0xff;DDRC = 0xff;while (1){for (int i = 0; i < 65534; i++) {lowerBits = i & 0xff;upperBits = (i >> 8);PORTB = upperBits;PORTD = lowerBits;PORTA = upperBits;PORTC = lowerBits;}PORTA = 0;PORTB = 0;PORTC = 0;PORTD = 0;for (;;);//PORTD = 0b01110000;//_delay_ms(50);//PORTD = 0b00000000;//_delay_ms(1000);}}Here is the schematic I want to use for DAC. Not sure if its corect to connect 2 OP AMPs like that but i'll see.

author: free_electron
author_rank: Super Contributor
timestamp: December 17, 2018, 05:34:00 pm
content: use uint (unsigned integer. a normal int rolls over at 32768 ... ) . stupidity in the C language.

author: IanB
author_rank: Super Contributor
timestamp: December 17, 2018, 05:39:47 pm
content: Quote from: georgian on December 17, 2018, 05:15:40 pmAs noted above, the "int" type has a range from -32768 to +32767. So "i" will always have a value less than 65534 and the loop will never end.You need to use "unsigned int" that has a range from 0 to 65535.(Why did you put 65534 in your code? Also, why "<" instead of "<="?)

author: georgian
author_rank: Regular Contributor
timestamp: December 17, 2018, 05:44:50 pm
content: Thank you sirSeconds ago, tring to find the maximal number that works i fund that 32767 works but 32768 dosen't work anymore. I useduint16_tand it works. Awesome! Thanks again.If anybody have to say anything about the schematic or maybe a value for the R10, R11 / R30,R31 is more than welcome to post.Quoteit was 65535 before. And yes it should be <= or i < 65536.Will it be a waste of time and resources to use thepower offunction? i < pow(2, 16) ?

author: SiliconWizard
author_rank: Super Contributor
timestamp: December 17, 2018, 06:23:26 pm
content: Quote from: georgian on December 17, 2018, 05:44:50 pmI'm wondering why you are doubling each "DAC" and summing them? This summing could be done at the software level. Adding one bit of resolution will not make you lose anything and it's much simpler?Are you really going to implement DACs this way instead of using true DACs? If so, implementing an R/2R ladder will be again much simpler (no need for switches).Additionally, depending on the accuracy you're targetting, using true DACs may be cheaper than using precision resistors. If accuracy is not an issue, implement R-2R ladders.Quotepow() is a math function from the std C lib acting on doubles and returning a double. Don't use it here (you could but it would need proper casting AND rounding, if not, you'll probably get a warning AND the result will be floor'ed, most likely getting you 65535 instead of 65536...). Integer powers of two are best expressed as this in C: 1 << 16Either way, it won't waste anything as these are operations on constants that are going to be evaluated at compile-time.

author: ejeffrey
author_rank: Super Contributor
timestamp: December 17, 2018, 06:35:56 pm
content: Quote from: IanB on December 17, 2018, 05:39:47 pmNote that gcc -Wextra will warn about this by enabling the -Wtype-limits flag. This checks comparisons that can never be true (or never false) because of the data type ranges. This also includes comparing an unsigned value to >= 0. I honestly don't know why it isn't enabled by -Wall. Possibly it generates too many extra warnings when such comparisons come up in macro expansions.It's always good to use the highest warning level you can tolerate and at least understand where each warning is coming from.

author: Twoflower
author_rank: Frequent Contributor
timestamp: December 17, 2018, 06:49:08 pm
content: Maybe you should also consider using the sized types like you did on the ports (uint8_t instead of char). For the loop variable you can use uint16_t. This makes a bit clearer to identify the actual value-range of a given variable.Edit: And just now I noticed that you actually did exactly that.

author: georgian
author_rank: Regular Contributor
timestamp: December 17, 2018, 07:18:17 pm
content: Quote from: SiliconWizard on December 17, 2018, 06:23:26 pmWell, in order to have an 16 bit DAC i could go for one OP amp and 16 resistors. The value of each would go too high and i think it will require to many resistor (series and parallel). One teacher told me that i could add the outputs like this and i'm not sure how to calculate the resistors. He said something that the lower resistor(R11) should be 256 times bigger then the upper one(R10). I did some small tests with LTspice but i was not happy with the resuslts.Honestly I don't know wich is better. This way or using R-2R ledder.By true DAC i think you mean some SPI /I²C DAC? If yes, then there is not much to learn by using tham. I did some prototypes using arduino DUE and Teensy 3.6. They both have internal DAC. Makeing everything from scratch make a lot more fun than using using already made ICs.

author: IanB
author_rank: Super Contributor
timestamp: December 17, 2018, 07:35:25 pm
content: Quote from: georgian on December 17, 2018, 05:44:50 pmBe careful with this. If the type has a range of 0 to 65535, then 65536 is outside the range of values that can be represented by the type. The code may not do what you intend it to do.

author: SiliconWizard
author_rank: Super Contributor
timestamp: December 17, 2018, 07:48:18 pm
content: Quote from: IanB on December 17, 2018, 07:35:25 pmYep. If i is 16-bit unsigned, it will never be 65536. So the condition i < 65536 will still be always true (infinite loop). Note that the same holds for i <= 65535. Always true.You can't get out of such a loop this way. You have to use a different condition. Or use a larger int, but that's wasteful.

author: ejeffrey
author_rank: Super Contributor
timestamp: December 17, 2018, 07:53:36 pm
content: Yes, this is a more fundamental issue. There is no way in C for a standard(*) for loop to execute over every legal value of the iteration variable. The test is done on entry to the loop body, and at least one value must fail or the loop will never terminate. So a for loop with a 16 bit integer can't execute more than 2^16-1 times. You need to either change the index variable to a larger size or use an alternate construction. One way is to change to a do/while loop:Code:uint16_t i=0;do {// code goes herei++;} while (i != 0);This will execute once for every value of i, then terminate when it wraps back to zero. This is not idiomatic C, so I would prefer to use a 32 bit loop counter if possible.Another way is to unroll a single iteration of the loop by replicating the body before or after the loop. I don't like this as much as it is unnecessary code duplication and possibly error prone, but it will work. Example would be:Code:uint16_t i;for (i=0; i<65535; i++) {// loop body}// loop body (executes with i=65535)(*) by standard I mean a single loop variable that is incremented or decremented and tested against a constant value where the loop index is not modified within the body of the loop. Since C places no such restrictions on the use of for statements so you can always cheat.

author: iMo
author_rank: Super Contributor
timestamp: December 17, 2018, 08:33:38 pm
content: QuoteYou have to add a third inverting opamp (with 1k in feedback) connected to X or Y making the "sum".Take R11=256k and R10=1k.The output will be (doublecheck!)X_sum = ( (-1 * 5V * (1k/Rbinary_sum) / 256k) + (-1 * 5V * (1k/Rbinary_sum) / 1k) ) * (-1 * 1k)

author: georgian
author_rank: Regular Contributor
timestamp: December 17, 2018, 09:37:16 pm
content: Quote from: imo on December 17, 2018, 08:33:38 pmThank you! Great community!I don't mind have negative Voltage on the output. I can set the Oscilloscope in XY mode and the start point top right. That will be my 0,0 coords.Now with this formula:I assume that the X_sum is the highest I will get wenn al the bits are 1, right? (All switches closed)I think I understand your formula. You did it all in one go. Attached you can see my calculations so far. I will try to use your formula to calculate the X_sum. Thank you one again.

author: iMo
author_rank: Super Contributor
timestamp: December 17, 2018, 09:51:02 pm
content: The "1/Rbinary_sum" in my equation above is a parallel combination of the binary resistor weights:1/Rbinary_sum = 1 / (1/1k + 1/2k + 1/4k ......), based on which binary weights are switched on..So if I were you I would first calculate the parallel combination of switched on resistors, for example:Rpar = 1/(1/1k + 1/8k + 1/32k..)and the Vout of the first opamp will beVout = -1 * 5V * 1k / Rpar

author: georgian
author_rank: Regular Contributor
timestamp: December 17, 2018, 09:58:53 pm
content: I just check and the voltage when all the bits are set is: 9.99mV, acording to your formula.(Vout upper OP AMP / 256K) + (Vout lower OP AMP / 1K) * - 1 for Vout I took 9.960V. Is something i have done wrong?***EDIT: Total resistence is 501.960 Ohm

author: georgian
author_rank: Regular Contributor
timestamp: December 17, 2018, 10:28:52 pm
content: I did some more tests.Let's say I write 2048 to PortB un PortD 0PortB(Upper Bits) = 8 0b1000 -> PortB.3 = 1PortD(lower Bits) = 0Now for the Upper OP AMP I have the 16K and for the lower OP AMP i have nothing.So the Upper OP AMP Voltage is -1 * 5V * (1K / 16K) = -312.5mVThe lower OP AMP is 0V(-312.5mV / 1K) + (0V / 256K) = -625µV I made a quick sketch in LT spice and it something different.

author: iMo
author_rank: Super Contributor
timestamp: December 17, 2018, 10:36:34 pm
content: I've prepared quickly a small excel for you.. Double check..Yellow, Orange - inputsGreen - resultsEDIT: fixed a bug in excel

author: georgian
author_rank: Regular Contributor
timestamp: December 17, 2018, 10:52:08 pm
content: Well thanks a lot for your patience and help. I got it now. Great tool you made in excel.The problem was with the two resistors.QuoteR10 should be 256K and R11 should be 1K. A did it with LT spice and its just like in the excel. I got it wrong before becouse i swapped the Two values.Now i have to buy a lot of resistors and combine them, and hope to get a 0V from an OP AMP when no input is connected.Topic considered to be closed and the issue fully solved. Thank you guys.

author: iMo
author_rank: Super Contributor
timestamp: December 17, 2018, 10:54:17 pm
content: I fixed a bug in the excel, so download the latest one above.PS: below 1LSB value = 10V/65536