| Author |
Message |
|
|
Posted: Jul 14, 2012 - 08:18 AM |
|

Joined: Jun 30, 2011
Posts: 46
Location: La Habra, CA
|
|
Hey Kartman,
Obviously, this post is old so my thanks on your wonderful tutorial are a bit behind the times, but I figured I'd put them here anyways. I've not yet had time to read (and digest) Dean's timer tutorial fully, so the values you use to setup the timer interrupt are kind of a mystery. Perhaps you might consider commenting those, or lines like it, in future tutorials for the slowpokes in the pack like me . |
|
|
| |
|
|
|
|
|
Posted: Jul 14, 2012 - 11:14 AM |
|


Joined: Jul 18, 2005
Posts: 62246
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
|
Code:
TCCR0B = 0x00; //stop
TCNT0 = 0x00; //set count
TCCR0A = 0x02; //CTC mode
OCR0A = 0x9C;
TCCR0B = 0x05; //start timer
Are the comments not enough then? That's almost as simple a timer setup as you can get and the key comment is "// CTC mode". A quick look at any AVR datasheet will show you that is the mode where the timer counts up to the value in a compare register (OCR0A in this case) and then resets to 0 (possibly generating an interrupt as it does so). So this timer is going to count 0x00, 0x01, 0x02... 0x93, 0x94, 0x95 (interrupt), 0x00, 0x01, 0x02..
As the count of 0x00 is included in that it's really counting 150 (0x95+1) timer "ticks" between overflows.
Also the "// start timer" comment shows 0b101 being loaded in to the B register. That is going to be /1024 so the timer will be running at 1/1024th of the CPU speed. It will therefore count 150*1024 = 153,600 CPU cycles between compare match interrupts. The comment at the top of the code say 16MHz so it executes 16,000,000 in 1 second therefore the interrupts will be 153600/16000000 apart that is 0.96ms - so close to 1ms. |
_________________
|
| |
|
|
|
|
|
Posted: Jul 15, 2012 - 05:19 PM |
|

Joined: Jun 30, 2011
Posts: 46
Location: La Habra, CA
|
|
| After finally getting through the Timers document in this forum they all make perfect sense. I think the names of the registers and such were more confusing than anything, but I see now that the comments are appropriate if you reference any AVR datasheet you have handy. |
|
|
| |
|
|
|
|
|
Posted: Jul 21, 2012 - 10:21 PM |
|

Joined: Jul 01, 2012
Posts: 23
|
|
Hi, Kartman, this is an awesome tutorial! Many many thanks!
I have two questions.
1) I noticed this portion of code:
Code:
while (task <= NUM_TASKS )
{
if ((task_bits & pgm_read_byte(&bit_mask[task])))
{
break; /* if activate task found..*/
}
task++; /* else try the next one */
}
Shouldn't it be "<" in stead of "<="? Because, after task=7 and task++, it'll be task=8 and program will enter the loop and try to find bit_mask[8], but bit_mask is only 0:7.
2) When you're writing "set_task(6)", it's only writing 64 to task_bits, right? The "task6()" function will actually start (I mean, the timer for task 6 and its LED blinking) in the "task_dispatch()" function, right? The reason I'm asking this is this portion of code:
Code:
set_task(7); //task7 runs
set_task(6); //task6 runs
What I've understood is happening here is "task_bits" is assigned 128 first, then without actually starting "task7()" (I mean, the timer for task 7 and its LED blinking), "task_bits" is assigned 64. And then the main loop starts and "task_dispatch()" is called which starts "task6()". And I didn't find "task_bits" being assigned 128 afterwards which would start "task7()" in ""task_dispatch()".
It's probably my mistake in understanding. But I'll appreciate your or anyone else's help very much in helping me with this. |
|
|
| |
|
|
|
|
|
Posted: Jul 22, 2012 - 07:06 AM |
|


Joined: Mar 27, 2002
Posts: 18537
Location: Lund, Sweden
|
|
|
Quote:
Shouldn't it be "<" in stead of "<="?
If you read/search through the whole thread you will find that there has been two such remarks prior to yours. (And unless I am missing something, you are correct.)
Quote:
What I've understood is happening here is "task_bits" is assigned 128 first, then without actually starting "task7()" (I mean, the timer for task 7 and its LED blinking), "task_bits" is assigned 64.
No. The set_task function uses the |= operator, not the = operator. Thus, first call ( set_task(7) ) sets bit 7 (the task_bits gets the value 128), Next call ( set_task(6) ) sets bit 6 without setting or clearing any other bits, so now the task_bits has the value 192.
If you are unfamiliar with bit manipulating operators then there is an excellent tutorial here at AVRfreaks. Mandatory read. |
|
|
| |
|
|
|
|
|
Posted: Jul 22, 2012 - 07:46 AM |
|

Joined: Jul 01, 2012
Posts: 23
|
|
| Thanks, Johan!! I've read the bit manipulation tutorial recently, and haven't got very used to it. So, I misunderstood that part. Thanks for clearing it up! |
|
|
| |
|
|
|
|
|
Posted: Jul 22, 2012 - 09:08 AM |
|


Joined: Dec 06, 2007
Posts: 2512
Location: Redmond, WA USA
|
|
|
JohanEkdahl wrote:
Quote:
Shouldn't it be "<" in stead of "<="?
If you read/search through the whole thread you will find that there has been two such remarks prior to yours. (And unless I am missing something, you are correct.)
I agree, although Kartman's response to the first remark was:
Kartman wrote:
Why? What benefit would the change make?
|
_________________ Larry
Those afraid to embrace the future will quickly fade into the past. - larryvc
|
| |
|
|
|
|
|
Posted: Jul 23, 2012 - 02:11 PM |
|

Joined: Dec 30, 2004
Posts: 8760
Location: Melbourne,Australia
|
|
| That portion of code is not ideal, but the later switch() relies on the value 8 being 'no task'. Therefore even though i go over the end of the bit_mask table, nothing comes of it. In retrospect i should've coded things clearer. Pascal would've given a runtime error! The challenge is to rewrite that section of code so it doesn't overflow the table and doesn't rely on a side-effect. The change is not simply using < as opposed to <= |
|
|
| |
|
|
|
|
|
Posted: Jul 24, 2012 - 05:38 AM |
|

Joined: Dec 30, 2004
Posts: 8760
Location: Melbourne,Australia
|
|
This method saves some bytes as well as avoiding the abovementioned problems.
Code:
if (task_bits & 0x01)
{
task0();
}
else if (task_bits & 0x02)
{
task1();
}
else if (task_bits & 0x04)
{
task2();
}
else if (task_bits & 0x08)
{
task3();
}
else if (task_bits & 0x10)
{
task4();
}
else if(task_bits & 0x20)
{
task5();
}
else if (task_bits & 0x40)
{
task6();
}
else if(task_bits & 0x80)
{
task7();
}
No one seemed to answer my question:
Quote:
I agree, although Kartman's response to the first remark was:
Kartman wrote:
Why? What benefit would the change make?
The answer I was looking for was, well, it would break the code as I described in my previous post. |
|
|
| |
|
|
|
|
|
Posted: Jul 24, 2012 - 08:02 AM |
|


Joined: Dec 06, 2007
Posts: 2512
Location: Redmond, WA USA
|
|
I saw your previous post last night but was too tired to reply. Good explanation. I also like the very simple fix above.
Thanks Kartman. |
_________________ Larry
Those afraid to embrace the future will quickly fade into the past. - larryvc
|
| |
|
|
|
|
|
Posted: Jul 24, 2012 - 08:41 AM |
|

Joined: Dec 30, 2004
Posts: 8760
Location: Melbourne,Australia
|
|
| Cheers Larry. That code had remained unchanged for 15 years or so. My new attempt just felt 'clunky' as there was no looping but it does seem to be much simpler and more obvious. I was modifying some code at the office today and thought I'd try it. Imagecraft reported around 20 bytes less code for the new method and one less byte of ram. So win-win. I wasn't motivated enough to measure the execution time, but I'd expect it is probably a few cycles less worst case especially since the compiler should be able to cache the task_bits variable in a register. I have had part two written for the past two years but never got around to proof reading it to make it suitable for publication. Not enough time since children and home construction projects eat up my time. |
|
|
| |
|
|
|
|
|
Posted: Aug 22, 2012 - 09:01 PM |
|


Joined: Aug 22, 2012
Posts: 1
|
|
Hi Kartman,
First of all, thanks for the inspiring code.
If I understand your code correctly, when two task timers run to zero at the same time, only the task with the lowest number will be executed in the current time slice. The other task will be executed 10 ms later. This would also imply that the task timer of this second task will be re-initialized 10 ms later. It will never make up for the lost time will it? So basically only task0 would be really deterministic...
By the way, same here with kids and construction,
Cheers. |
|
|
| |
|
|
|
|
|
Posted: Aug 23, 2012 - 04:11 AM |
|

Joined: Dec 30, 2004
Posts: 8760
Location: Melbourne,Australia
|
|
| RedDuck, you understand correctly. In many cases hard real time is not necessary. If you're just controlling relays and a lcd, such anomalies are not apparent or cause a problem. Obviously, if you really needed hard timing, then you would have that done via an interrupt or hardware (timer etc). |
|
|
| |
|
|
|
|
|
Posted: Aug 26, 2012 - 02:41 PM |
|

Joined: Jan 30, 2011
Posts: 1
|
|
| thanks, is part 2 completed? |
|
|
| |
|
|
|
|
|
Posted: Aug 27, 2012 - 04:51 AM |
|

Joined: Dec 30, 2004
Posts: 8760
Location: Melbourne,Australia
|
|
| Can you find it?... No. Maybe soon. |
|
|
| |
|
|
|
|
|
Posted: Sep 28, 2012 - 05:54 PM |
|


Joined: Jul 13, 2008
Posts: 146
Location: Roswell, New Mexico - US
|
|
Kartman
Informative tutorial... thanks.
sundownr |
|
|
| |
|
|
|
|
|
Posted: Nov 04, 2012 - 06:11 AM |
|

Joined: Mar 07, 2005
Posts: 77
Location: Mashhad, Iran
|
|
Thanks a lot Kartman. You made my work very easy and
I learned other things aside from multitasking.
Please find a free time to complete second part. Many of us are waiting. |
|
|
| |
|
|
|
|
|
Posted: Nov 14, 2012 - 11:37 AM |
|

Joined: Mar 04, 2008
Posts: 1
|
|
instead of
Code:
pgm_read_byte(&bit_mask[task])
can and it's just a simple
Code:
(1<<task)
|
|
|
| |
|
|
|
|
|
Posted: Nov 14, 2012 - 11:55 AM |
|


Joined: Jul 18, 2005
Posts: 62246
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
| Yeah that'd work but one would have to study the generated Asm to see which is more space/time efficient. |
_________________
|
| |
|
|
|
|
|
Posted: Nov 15, 2012 - 05:21 AM |
|

Joined: Dec 30, 2004
Posts: 8760
Location: Melbourne,Australia
|
|
There's little doubt that (1<<task) is probably more obvious. As for efficiency, I agree with Cliff. Nevertheless, the net result is not going to be much of an efficiency gain methinks.
The source of that code goes back to the early days of an IAR 8051 compiler where I would look carefully at the generated code to coax the compiler into doing its best. If I was doing a code review and came across this, the questions I would ask:
1. does it work? No potential side effects?
2. is the intention reasonably obvious?
3. what would be gained by changing it?
4. Is the use consistant?
5. Is it MISRA compliant?
Granted it's a lot more verbose, but again, the pgm_read_byte() is a avr-gcc - ism. given another compiler, it would probably read a lot simpler. Does less source == less object?? (that was a rhetorical question). Less chance for typing errors though! |
|
|
| |
|
|
|
|
|