There are three ways of coding DELAYS in a C Program, one of which is an
assembler delay loop:
In this page I'm going to look at 1 and 2 and save interrupt driven code for
another day. The first two methods provide an easy (and quick) way of
generating a delay time whereas
when you use interrupts you need to consider the system as a whole i.e. which
timer to use how to integrate with other interrupts etc.
These are the easiest delays to create and at first sight they seem like the
ideal solution since all you do is write a loop as follows:
...with the semicolon acting as the body of the delay i.e. do nothing. And that is one of the problems - An optimising compiler will see a 'do nothing' loop and may optimize it away completely and you won't be told.
After all the function of a compiler is to save space to make your program fit into the maximum memory. It will see a useless function and say 'right lets remove this'.
To get around this there are usually two methods:
Both these methods require experimentation and for you to look at the
assembler output code to find out if the compiler did what you thought it should have done.
The problem with these methods is that you are having to get involved understanding the operation of the compiler and it means that you do not have portable code. Also an update to the compiler may change the output optimization.
In addition to this compiler changes may change how the loop is coded (e.g. if going beyond a memory bank limit (16F) - may make the compiler add more complex jump code) and so change the loop time that you use - all in all it's a poor coding solution for creating delay.
In this method you import assembler code and stitch it into a C function. OK
it is non-portable but it is guaranteed to operate with the delay time you
specify and it won't change if the compiler changes.
The key is getting hold of the assembler code and a good tool to do it is picloops.
This is the sort of assembler code you get:
So this is the PIC loop code that will generate a 0.001001s delay i.e. approx 1ms. There's lots of variables to play with in Picloops to get a better result but note that the higher the main clock (finer resolution) the closer you will get to the exact time. Also interrupts can generate an exact time (subject to the tolerance on the Xtal of course) - also subject to how much the interrupt has to do i.e. keep the interrupt code small!
This code is normally used only with assembler so now comes the tricky part - you want to convert it to a C function so for SDCC there are two commands
Place your assembler between these C commands (each compiler will use different commands for this action but the basic principle is the same).
Ok that's easy now comes the difficult bit getting access to variables.
When you think about compilers, in the end, all of them must convert all
code into assembler - it's the only way that the microcontroller works so all
compilers must obey assembler coding rules.
This means at the lowest level ALL variables are global (as much as a C purist hates that !) this means that every variable must be uniquely identified in the assembler. In C the details of variable names are hidden from view at the C level but they do exist and most compiler documentation (including SDCC) will tell you how they work so you can do what we are doing here i.e. obtain the assembler coding name for a variable.
If you don't have documentation the look at the assembler output to see how they do it.
Here's the final code for the C function delay_ms that uses the above assembler:
There's a few things to note
The function _delay_ms is static and the variable coding name will change if
declared non static - it is legal to do this just be aware that the function
(assembler code) will fail unless you match the variable name at the assembler
The final variable name is __delay_ms_CounterA_1_1
|Breakdown of the SDCC variable|
|1||An additional underscore prefix||_|
|2||then the function name itself||_delay_ms|
|3||it then appends the variable name||_CounterA|
|4||and then some identifiers||_1_1.|
|Final variable : __delay_ms_CounterA_1_1|
That's it really - you can now code up delays that won't change if you C code does!
Note: Remember you need to change the delay code depending on the clock frequency you use.
Assembler delay loop Disadvantages :
Assembler delay loop Advantages :
Note: There are much more complex coding methods involving #defines to
specify the clock speed. The delay routine is then created using this
information which means the delay routine will work for any clock speed. The
disadvantage is that it takes quite a lot of effort to generate this code in
the first place - you can see examples in commercial compilers such as mikroC
How to use the BMP280 for weather pressure measurement or altitude change detection.
Easily use the SSD1306 in any of your projects.
How to easily make a logic level converter e.g. between 3V and 5V.