Arduuino-atmega8 timers differences- porting foreign code - Page 1
Forum section: electronics / general

author: kokodin
author_rank: Regular Contributor
timestamp: April 09, 2023, 08:17:13 pm
content: Hello. i am here with rather newbie problem, probably my own creation. mostly related to downloading untested arduino code from the internet and trying to shoehorn it to a different microcontroller just because i have those not arduino :].I never really worked with timers that much so i can't really decipher why it no work. if the oruginal code used 16 bit timer it would probably have no consequences which atmel chip it works on, because by datasheet both arduino (atmega 328) and atmega 8 have it configured in exact the same way. sadly project used timer 0 which in atmega 8 is vastly cut down. long story short i don't understand the code well enough to come up with the alternative solution.Code:#define TIMER_PRESCALER 64#define DccBitTimerCount (F_CPU * 80L / TIMER_PRESCALER / 1000000L)// 16000000 * 80 / 64 / 1000000 = 20; 20 x 4usecs = 80us//========================void beginBitDetection() {TCCR0A &= B11111100;attachInterrupt(0, startTimer, RISING);}//========================void startTimer() {OCR0B = TCNT0 + DccBitTimerCount;TIMSK0 |= B00000100;TIFR0 |= B00000100;}//========================ISR(TIMER0_COMPB_vect) {byte bitFound = ! ((PIND & B00000100) >> 2);TIMSK0 &= B11111011;byte nbs = nextBitSlot(bitBuffHead);if (nbs == bitBuffTail) return;else {bitBuffHead = nbs;bitBuffer = bitFound;}}//========================Those are parts of code of the program i have problems with. i was able to convert the rest with no issues, since it is a simple terminal talking program collecting informations from incoming model train controller taking up as a whole only half of atmega 8 flash. A bit of a waste of arduino really, which i don't have anyway.I am actually compiling and uploading this in arduino ide via usbasp after installing minicore boards. i am not sure if that is a good idea or would winavr be a bit faster after compiling, anyways, when i randomly change timers to ones existing on my ic without even understanding what they do code compiles, but also crashes right after sending the setup message.if someone kindly explain to me how this code work i probably would be able to climb my way back studying atmega 8 datasheet. i am actually not interested in solution alone, because then i will kind of be out of the loop and still learn nothing.If i understand it corectly porting it to timer1, the 16 bit one would make the code more universal, because both ic's use the same 16 bit counters but it would bee too slow if it have to work with full count and would have to be heavyly prescaled. but that just my ignorance talking.

author: Ian.M
author_rank: Super Contributor
timestamp: April 09, 2023, 08:50:46 pm
content: Its attaching the functionstartTimer()to the digital pin 2 INT0 interrupt, rising edge triggered. When that interrupt event occurs, it sets OCR0B to the current timer count + the #defined calculated constant DccBitTimerCount so that a different interrupt (TIMER0_COMPB, OCR0B compare match) will occur that many timer ticks later. When that ISR executes it polls digital pin 2 (as PD2) and if there's space, stuffs it into the next free location in a circular bit bufferbitBuffer[](which seems to be organised on a one byte per bit basis as C pointers cant access individual bits). Presumably,bitBuffer[]and its head and tail indexesbitBuffHeadandbitBuffTailare globals or file scope variables, and volatile, so they can also be accessed from the main program outside of any ISR context.

author: kokodin
author_rank: Regular Contributor
timestamp: April 09, 2023, 11:38:53 pm
content: well i can sort of follow the program structure, so that only vaugly clear things up.I am more concerned about that masked binary values for the registers and i feel like i need a drawing to understand what is going on with those, or a video toutorialsyntax examplesanyway what i get from going over manuals a bit is that atmega8 timer 0 has only overflow trigger, timer 1 would have everything pretty much the same but it is 16 bit so bitmasking registers of what it should do would be wrong and timer 2 has only one compare register anda bit different prescaler options and topology but is again 8 bit.sorry it is almost 2 am here so the question here might be a bit dumbWas ocr0b register chosen for specyfic reason over ocr0a or was it a preference? Because if it is just a preference i should probably use timer 2 with one cor2 register because that should be enough if i am not mistaken, otherwise i need to somehow decode those binary masks to what they do and try that with 16 bit timer?besides i am still unsure how it is getting the interupts to do anything on pin 2 if pin 2 is where i input the signal , which is continues stream in a loop

author: Ian.M
author_rank: Super Contributor
timestamp: April 10, 2023, 12:40:32 am
content: The ATmega328P full datasheet has what you need to decode those register manipulation bitmasks. |= SETS any bit that's 1 in the mask and &= CLEARS any bit that's 0 in the mask.e.g.TCCR0A &= B11111100;is clearing bits 0 and 1 of TCCR0A, and searching for that register in the datasheet eventually locates the register description, (Section 19.9.1. TC0 Control Register A, page 138), which tells be the bottom two bits are WGM00 and WGM01, and together with WGM02 (in TCCR0B) select the PWM mode from table 19-9. I assume WGM02 is either set to 0 elsewhere in the code or has that state from reset, which would make the WGM bits 0b000, which disables Timer 0 PWM (the normal state).Plow through the rest of them to figure out what's actually being set/cleared, then hit the datasheet for the timer 0 block diagrams to figure out the timer tick rate, and compare mode settings. Then its simply a matter of matching them up with the capabilities of the ATmega8 timers. There's no reason not to use a 16 bit timer - as the compare register is being set from the timer value in an ISR, and AVR 16 bit timers have a buffer register to capture the high byte to get a clean 'snapshot' read of the whole 16 bit timer, and hardware to update the whole of each 16 bit OCR register atomically, by deferring the high byte write till the low byte is written - as long as some care is taken with the byte order of the reads and writes, it will 'just work'. The execution time of extra few instructions required for 16 bit vs 8 bit handling instartTimer()should be negligible unless its handling very high baud rate serial input.

author: westfw
author_rank: Super Contributor
timestamp: April 10, 2023, 08:00:21 am
content: QuoteIIRC, the main difference between the mega8 and the mega328 is that timer0 on the mega8 does NOT have the "output compare registers." For Arduino, that means two less PWM outputs.The program you're trying to run is clearly using the compare registers (and the interrupt) of TIMER0, so you'll probably have to modify it to use one of the other timers.

author: Ian.M
author_rank: Super Contributor
timestamp: April 10, 2023, 09:27:41 am
content: Yes. The Arduino framework (on AVRs) makes heavy use of TIMER0 and its overflow interrupt, for themillis()time anddelay()functions, and in the standard Arduino AVR core, you cant intercept or add your own code to the TIMER0_OVF ISR.If you are coding 'bare metal' without the Arduino framework, to eliminate the need for output compare, in thestartTimer()function you *could* preload Timer 0 with 256-DccBitTimerCount, so it overflows after the required no. of ticks, and start the timer. Use TIMER0_OVF for the bit handling ISR.However if you *are* using the Arduino framework, that isn't possible, so your only alternatives are to port the code to a timer that *has* output compare or switch to a different AVR MCU with Timer 0 output compare.

author: kokodin
author_rank: Regular Contributor
timestamp: April 10, 2023, 10:19:31 am
content: i could just go and buy knokof arduino on ebay and not learn anything usefull.yes i am using arduino ide and yes milis function is used all over the code to gauge 0's and 1's from dcc signal stream because the have different lengthand i belive minicore uses them normally. but who knows i might be totaly wrong, that wouldn't be the first time.the biggest question now is what arduino ide does with the other 2 timers, especially what mini core for atmega does with them?is there a way to check all the board specyfic hidden code that programs all the functions for arduino ide?edit********i just feel like reading all the examples makes me even dumber than i was before because every code is different, doing things in completly different way and even if commented what that particular register mask does it dos not specyfi anything else, no other options no alternative syntax for doing the same and i feel like i am learning interupts and timers 3 times separately instead of just once as a whole. it is so frustrating to not know what you don't know. but i always hated examples that only showed you one way of doing things without even telling about other methods. it is like showing you the most convoluted way to get from point a to point b without telling you the shortest route is blocked but instead informing tou this is "the way"i will go back to reading manuals and perhaps find something that could replace timer0 implementation using arduino ide

author: kokodin
author_rank: Regular Contributor
timestamp: April 10, 2023, 07:42:52 pm
content: i am thinking out loud so please corect me if i am wrongi want to use timer 2 of atmega8 as a closest to functional equivalentit has THE compare match register ocr2 , timsk is shared register between all interupts so i only need to set bits i want to compare match, and do the same for tifr so start timer would end up something like thisCode:void startTimer() {OCR2 = TCNT2 + DccBitTimerCount;TIMSK |= B10000000;TIFR |= B10000000;}but that was the easy partSetting the interupt would now look something like thisCode:ISR(TIMER2_COMP_vect) {byte bitFound = ! ((PIND & B00000100) >> 2);TIMSK &= B01111111;byte nbs = nextBitSlot(bitBuffHead);if (nbs == bitBuffTail) return;else {bitBuffHead = nbs;bitBuffer = bitFound;}}but i am still not sure about tccr registerbecause in oryginal code author often mask non writable bits with 1's and i am still not sure if timer 2 isn't used by arduino to something else plus the other register tccr0b isn't even mentioned in the code so i don't know what it was set to without checking it on arduinoanyway changing that register afect the text output the most, i must spam some debug prompts all over the code to track the branching execution but i don't think it is doing what ist suposed to just yet (but it no longer triping over itself in endless interupt and it almost work) it still hangs up after a few prompts so it may be stuck in some loop.how it works now is with no dcc bus connected it hangs after displaying setup message and branching into bit detection functions but if i feed it with dcc bus it spit out some garbage i left in bit detection functions and then stops, it might be a timing problem or anything else really

author: westfw
author_rank: Super Contributor
timestamp: April 11, 2023, 12:33:23 am
content: QuoteThe other timers are used only for PWM, but they are initialized for "phase correct" PWM, which means that they spend half their time counting down instead of up, making them unsuitable for what your code is doing UNLESS you re-initialize them for normal mode. That shouldn't be a problem if you're not using PWM on the pins it controls.AFAIK, TIMER2 is about the same as TIMER0 except for its ability to run asynchronously and use the 32kHz oscillator.

author: kokodin
author_rank: Regular Contributor
timestamp: April 11, 2023, 04:51:47 pm
content: well i actually did make it almost work , more like a beaten dog than dcc decoder, but it does spit out some nonsense data now instead of being stuck in a bit read loop.IT might sound like an excuse but i didn't code that much lately and i completly forget what logic operators do what and i didn't notice at first i am removing bits from registers instead puting them in. beside i didn't have 16mhz crystal so my board is runing from 12mhz one and probably all the timings are wrong (but there is a ballpark where it almost work around 46-49 prescaler on top of the code)to be honest if i was to write this code from scratch i would probably want to use timer 1 in normal mode with no interupts set and trigger my interupts from every logic level change on the input pin then comparing timer value with a constant value and determining bit from that, then reseting timer to 0 and incrementing loop counter. and if i did that i could become almost clock independent. well any timer would do but i would need to prescale 8 bit onesi could even fit phase correction into that based on that constant and 1 boolean bit because i don't see that in oryginal codei was wrong about use of millis in the code though , it is only used to refresh the output every set number of millisI will probably play around with this code a bit and ten try to build my own implementation from scratch to only gave things i want, for instance make it adressable dcc decoder that only spits commands adressed to it, only one step from driving a train

author: kokodin
author_rank: Regular Contributor
timestamp: April 11, 2023, 09:00:26 pm
content: I would like to thank everyone who tryed to help me. i learned a lot. But completly out of the blue, i found open source lubrary for arduino ide, that can perform every thing i wanted from this project, without using timers, on attiny84 and up, with any external clock. Plus it is very much a full fledged still maintained dcc protocol implementation project so i just build my device out of avalable lego blocks.needless to say it works out of the box from any example.so once more thanks for help but i think this thread is now closed, i will play with the timer in some other project now that i know how to use them(mostly)

author: westfw
author_rank: Super Contributor
timestamp: April 12, 2023, 05:14:57 am
content: QuoteThis is the way!It can be pretty demotivating WRT writing code when you can just "find" code that is pretty close to what you want that doesn't look too horrible.(Occasionally it looks a lot BETTER than what I think I could do. More Professional and stuff. Sigh.)