MSP430 TI while loop duration - c

I am programming a simple program on the TI MSP430.
I have a counter set up in C:
while (P1IN & BIT1)
{
counter++;
}
So when the pin is high, it counts up by one. I am wondering how long this takes?
I need to do some calculations with counter and need the duration of one while loop. In other words, say counter = 1234 in the end, how can I get a value of seconds?
How can I get this? Should I export the ASM code and see how long each instruction set takes? This seems tedious.

You can try:
1. Toggle any free port pin at the start and end of the loop and monitor the duration on CRO(If you have necessary equipment).
OR
2.Look into disassembly listing(ASM code),read instruction manual and based on CPU clock calculate the loop time.

Related

PIC Assembly Language - decfsz loop

I am working with a PIC 18F microcontroller from Microchip to continuously generate a rectangular signal. The code for the signal itself is at label5.
I need to generate 255*20 pulses of this signal. So basically, I need to repeat the instructions from the first 4 lines at label 5 for 255*20 times. Because I cannot have numbers higher than 2^8, I needed to write the number this way.
label5 BSF portd,5
call timer1
BCF portd,5
call timer2
In the code below I tried to achieve this behavior. I gave variable1 the value of 255 and I decremented from this value until variable1 was zero, in which case I returned at label2 and restarted the program. Everytime I decremented the variable1 I called label4. A similar things happens at label4. Here I have another variable, variable2, that is also decremented until it hits zero (and here comes the main signal generation program, repeated with each decrement operation), in which case, the program returns.
Can someone please tell me if I am on the right track ?
label2 movlw .255
movwf variable1
label3 call label4
decfsz variable1,1
goto label3
goto label2
; """"""""""""""
label4 movlw .20
movwf variable2
label5 BSF portd,5
call timer1
BCF portd,5
call timer2
decfsz variable2,1
goto label5
return
end
```
The general recommendation is to use timers to burn time, some would argue interrupts to have a possibility of putting the chip in a lower powered mode. But with processors like the PIC18 where you can count instructions and very accurately from that determine execution time to use simple loops to burn time.
Two ways to make a loop take longer and I am very rusty on my PIC coding so consider this psuedo-code:
variable2 = 0
label:
decfsz variable2,1
goto label
That essentially is 256 loops yes? and you can count instructions including the extra clock or whatever for the time that it is zero...
variable2 = 0
label:
nop
nop
decfsz variable2,1
goto label
Adding nops can burn more time (yes I may still not understand if it is time you are burning or simply want more loops).
Or if you want to make it more loops and you only have 8 bits to count with then nest the loops
variable1 = 20
variable2 = 0
outer:
inner:
; other stuff goes here?
decfsz variable2,1
goto inner
decfsz variable1,1
goto outer
the inner loop will count 256 times, the outer loop will count 20 so you get 20*256 total loops
I have used this type of approach to make very accurate signals that couldn't be made by using a timer with this processor a much more efficient instruction set and faster processor would need to be used to have done the same thing with a timer if even possible. But you would instead buy a product that has a timer peripheral that does what you are trying to do or a portion of it, for example infrared remote you can get some ST products that take two timer outputs and have the and gate in the chip, so you can have a hardware generated carrier signal and a hardware generated gate, but generate the duration of the gate via software. with the pic I just had some small loops to do the same thing and it was all timed by counting instructions.
I would not use this approach on a cortex-m, maybe an msp430, maybe an avr, but not something pipelined and not something that was purchased IP from someone else (arm doesn't make chips, st and nxp and others make chips and simply purchase IP from arm as well as most of the rest of the chip is not arm IP and each vendor can tweak the ip when the get it so the same core (cortex-m0+ rev x.y for example) in different chips does not necessarily behave the same).
Another way would be to use a 16-bit loop counter that has a value of 255*20.
Something like this:
;
;
;
TIMER1_CODE code
timer1:
return
;
;
;
TIMER2_CODE code
timer2:
return
;
; main application
;
MAIN_CODE code
main:
bcf TRISD,5 ; make RD5 an output
ProcessLoop:
movlw D'255' ; Compute loop count
movwf PRODL
movlw D'20'
mulwf PRODL ; PRODH:PRODL = 255*20 = 5100
OutBitLoop:
movlw 0xFF ; Decrement loop count
addwf PRODL,F
addwfc PRODH,F
bnc Stop ; Stop when done enough loops
bsf LATD,5 ; Set output bit high
call timer1
BCF LATD,5 ; Set output bit low
call timer2
bra OutBitLoop
bra ProcessLoop
Stop:
bra Stop
end
Note that the code you posted uses the PORTD register to set or clear an output bit with an opcode that does a Read-Modify-Write. This is a bad choice.
For the PIC18F always use the output latch register (LATD) when changing the state of output bits.

Best way to add delay/do nothing for n cpu cycles

I need to add a delay into my code of n CPU cycles (~30).
My current solution is the one below, which works but isn't very elegant.
Also, the delay has to be known at compile time. I can work with this, but it would be ideal if I could change the delay at runtime.
(It is OK if there is some overhead, but I need the 1 cycle resolution.)
I do not have any peripheral timers left, that I could use, so it needs to be a software solution.
do_something();
#define NUMBER_OF_NOPS (SOME_DELAY + 3)
#include "nops.h"
#undef NUMBER_OF_NOPS
do_the_next_thing();
nops.h:
#if NUMBER_OF_NOPS > 0
__ASM volatile ("nop");
#endif
#if NUMBER_OF_NOPS > 1
__ASM volatile ("nop");
#endif
#if NUMBER_OF_NOPS > 2
__ASM volatile ("nop");
#endif
...
In the cortex devices NOP is something which literally means nothing. There is no guarantee that the NOP will consume any time.They are used for padding only. I you will have several consecutive NOPs they will just be flushed from the pipeline.
For more information refer to the Cortex-M0 documentation. http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0497a/CHDJJGFB.html
software delays are quite tricky in the Cortex devices and you should use other instructions + possibly barrier instructions instead.
use ISB instructions 4 clocks + flash access time which depend what speed the core is running. For very precise delays place this part of code in the SRAM
Edit: There is a better answer from another SO Q&A here. However it is in assembly, AFAIK using a counter like SysTick is the only way to guarantee any semblance of cycle accuracy.
Edit 2: To avoid a counter overflow, which would result in a very, very long delay, clear the SysTick counter before use, ie. SysTick->VAL = 0;
Original:
Cortex-Ms have a built in timer called SysTick which can be used for cycle accurate timing purposes.
First enable the timer:
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_ENABLE_Msk;
Then you can read the current count using the VAL register. You can then implement a cycle accurate delay this way:
int count = SysTick->VAL;
while(SysTick->VAL < (count+30));
Note that this will introduce some overhead because of the load, compare and branch in the loop so the final cycle count will be a little off, no more than a few ticks in my estimation.
You can use a free-running up-counter as follows:
uint32_t t = <periph>.count;
while ((<periph>.count - t) < delay);
As long as delay is less than half the period of the counter, this is unaffected by wrapping of the counter value - the unsigned arithmetic produces the correct time delta.
Note that since you don't need to control the counter's value in any way, you can use any such counter in the system - even if it's being used for another purpose (as long, of course, as it really is running continuously and freely, and at a rate that gives you the timing resolution that you require).

Duration for Sound on the 8051

I am trying to create a short tune using timers on the 8051. I am trying to send a square wave with a specified frequency to create the notes.
However, with my current code all I am getting is one infinite note, that never stops playing. Any help figuring out how to stop the note, and create a duration function would be greatly appreciated.
#include<reg932.h>
sbit speaker=P1^7;
void tone(unsigned char, unsigned char);
void main()
{
P1M1 = 0;
P1M2 = 0;
tone(0xC8, 0xF3);
}
void tone(unsigned char highval, unsigned char lowval)
{
TMOD=0x01;
TL0=lowval;
TH0=highval;
TR0=1;
while(TF0==0);
speaker=0;
TR0=0;
TF0=0;
}
I haven't programmed 8051's devices in a long time, but here's what I'd do:
1.a. figure out if it's tone() that never exits
1.b. if it is, I'd make sure that the while loop is indeed in there (see the disassembly of tone()), if it's not, the compiler optimizes the check out and it needs fixing (e.g. declaring TF0 as volatile)
1.c. see if the check is correct (the right bit in the right register, etc)
write an assembly routine to waste N CPU clocks, use the slowest instruction (was MUL or DIV the slowest?) in a loop or simply repeated M times so you get like a 10 ms delay or something, write a C function to call that routine as many times as necessary (e.g. 100 times for 1 second). (You could use a timer here, but this may be the simplest)

Audio tone for Microcontroller p18f4520 using a for loop

I'm using C Programming to program an audio tone for the microcontroller P18F4520.
I am using a for loop and delays to do this. I have not learned any other ways to do so and moreover it is a must for me to use a for loop and delay to generate an audio tone for the target board. The port for the speaker is at RA4. This is what I have done so far.
#include <p18f4520.h>
#include <delays.h>
void tone (float, int);
void main()
{
ADCON1 = 0x0F;
TRISA = 0b11101111;
/*tone(38.17, 262); //C (1)
tone(34.01, 294); //D (2)
tone(30.3, 330); //E (3)
tone(28.57, 350); //F (4)
tone(25.51, 392); //G (5)
tone(24.04, 416); //G^(6)
tone(20.41, 490); //B (7)
tone(11.36, 880); //A (8)*/
tone(11.36, 880); //A (8)
}
void tone(float n, int cycles)
{
unsigned int i;
for(i=0; i<cycles; i++)
{
PORTAbits.RA4 = 0;
Delay10TCYx(n);
PORTAbits.RA4 = 1;
Delay10TCYx(n);
}
}
So as you can see what I have done is that I have created a tone function whereby n is for the delay and cycles is for the number of cycles in the for loop. I am not that sure whether my calculations are correct but so far it is what I have done and it does produce a tone. I'm just not sure whether it is really a A tone or G tone etc. How I calculate is that firstly I will find out the frequency tone for example A tone has a frequency of 440Hz. Then I will find the period for it whereby it will be 1/440Hz. Then for a duty cycle, I would like the tone to beep only for half of it which is 50% so I will divide the period by 2 which is (1/440Hz)/2 = 0.001136s or 1.136ms. Then I will calculate delay for 1 cycle for the microcontroller 4*(1/2MHz) which is 2µs. So this means that for 1 cycle it will delay for 2µs, the ratio would be 2µs:1cyc. So in order to get the number of cycles for 1.136ms it will be 1.136ms:1.136ms/2µs which is 568 cycles. Currently at this part I have asked around what should be in n where n is in Delay10TCYx(n). What I have gotten is that just multiply 10 for 11.36 and for a tone A it will be Delay10TCYx(11.36). As for the cycles I would like to delay for 1 second so 1/1.136ms which is 880. That's why in my method for tone A it is tone(11.36, 880). It generates a tone and I have found out the range of tones but I'm not really sure if they are really tones C D E F G G^ B A.
So my questions are
1. How do I really calculate the delay and frequency for tone A?
2. for the state of the for loop for the 'cycles' is the number cycles but from the answer that I will get from question 1, how many cycles should I use in order to vary the period of time for tone A? More number of cycles will be longer periods of tone A? If so, how do I find out how long?
3. When I use a function to play the tone, it somehow generates a different tone compared to when I used the for loop directly in the main method. Why is this so?
4. Lastly, if I want to stop the code, how do I do it? I've tried using a for loop but it doesn't work.
A simple explanation would be great as I am just a student working on a project to produce tones using a for loop and delays. I've searched else where whereby people use different stuff like WAV or things like that but I would just simply like to know how to use a for loop and delay for audio tones.
Your help would be greatly appreciated.
First, you need to understand the general approach to how you generate an interrupt at an arbitrary time interval. This lets you know you are able to have a specific action occur every x microseconds, [1-2].
There are already existing projects that play a specific tone (in Hz) on a PIC like what you are trying to do, [3-4].
Next, you'll want to take an alternate approach to generating the tone. In the case of your delay functions, you are effectively using up the CPU for nothing, when it could be done something else. You would be better off using timer interrupts directly so you're not "burning the CPU by idling".
Once you have this implemented, you just need to know the corresponding frequency for the note you are trying to generate, either by using a formula to generate a frequency from a specific musical note [5], or using a look-up table [6].
What is the procedure for PIC timer0 to generate a 1 ms or 1 sec interrupt?, Accessed 2014-07-05, <http://www.edaboard.com/thread52636.html>
Introduction to PIC18′s Timers – PIC Microcontroller Tutorial, Accessed 2014-07-05, <http://extremeelectronics.co.in/microchip-pic-tutorials/introduction-to-pic18s-timers-pic-microcontroller-tutorial/>
Generate Ring Tones on your PIC16F87x Microcontroller, Accessed 2014-07-05, <http://retired.beyondlogic.org/pic/ringtones.htm>http://retired.beyondlogic.org/pic/ringtones.htm
AN655 - D/A Conversion Using PWM and R-2R Ladders to Generate Sine and DTMF Waveforms, Accessed 2014-07-05, <http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1824&appnote=en011071>
Equations for the Frequency Table, Accessed 2014-07-05, <http://www.phy.mtu.edu/~suits/NoteFreqCalcs.html>
Frequencies for equal-tempered scale, A4 = 440 Hz, Accessed 2014-07-05, <http://www.phy.mtu.edu/~suits/notefreqs.html>
How to compute the number of cycles for delays to get a tone of 440Hz ? I will assume that your clock speed is 1/2MHz or 500kHz, as written in your question.
1) A 500kHz clock speed corresponds to a tic every 2us. Since a cycle is 4 clock tics, a cycle lasts 8 us.
2) A frequency of 440Hz corresponds to a period of 2.27ms, or 2270us, or 283 cycles.
3) The delay is called twice per period, so the delays should be about 141 cycles for A.
About your tone function...As you compile your code, you must face some kind of warning, something like warning: (42) implicit conversion of float to integer... The prototype of the delay function is void Delay10TCYx(unsigned char); : it expects an unsigned char, not a float. You will not get any floating point precision. You may try something like :
void tone(unsigned char n, unsigned int cycles)
{
unsigned int i;
for(i=0; i<cycles; i++)
{
PORTAbits.RA4 = 0;
Delay1TCYx(n);
PORTAbits.RA4 = 1;
Delay1TCYx(n);
}
}
I changed for Delay1TCYx() for accuracy. Now, A 1 second A-tone would be tone(141,440). A 1 second 880Hz-tone would be tone(70,880).
There is always a while(1) is all examples about PIC...if you just need one beep at start, do something like :
void main()
{
ADCON1 = 0x0F;
TRISA = 0b11101111;
tone(141,440);
tone(70,880);
tone(141,440);
while(1){
}
}
Regarding the change of tone when embedded in a function, keep in mind that every operation takes at least one cycle. Calling a function may take a few cycles. Maybe declaring static inline void tone (unsigned char, int) would be a good thing...
However, as signaled by #Dogbert , using delays.h is a good start for beginners, but do not get used to it ! Microcontrollers have lots of features to avoid counting and to save some time for useful computations.
timers : think of it as an alarm clock. 18f4520 has 4 of them.
interruption : the PIC stops the current operation, performs the code specified for this interruption, erases the flag and comes back to its previous task. A timer can trigger an interruption.
PWM pulse wave modulation. 18f4520 has 2 of them. Basically, it generates your signal !

Making a delay in C with 20MHz crystal(beginner level)

I've recently started with C and Im trying to figure out how to make a 10ms delay for
PIC16F884. From the formulas in the datasheet I've managed to create the following:
fosc = 20MHz and Toscx4 = 200ns
If I put a preset to 100 => t=100x200ns=20us and to get a 10 ms delay
10ms/20us = 500
Will the following code in C give me what Im looking for (10ms delay)? Assume I have all the initiating code and variables.
void interrupt ISR(void){
if(TMR0IF){
TMR0IF=0;
counter++;
}
if(counter==100){
delay++;
counter=0;
}
}
int main(void){
TMR0=155;
if(delay>4){
//any code
delay=0;
}
}
Might be a bad example but hopefully you understand
No, since the code in main() never actually waits, it won't implement a delay.
Assuming the interrupt and timer logic is properly set up, you're still going to need to loop:
delay = 0;
TMR0 = 155; /* Start timer. */
while(delay < 4)
; /* Do nothing */
/* More code here, delay has expired */
Also, remember to make delay a volatile variable since you're accessing it from multiple parallel threads of execution.
If you're a really beginner and want a tool that can help you calculate timer settings I advise you to look at this link :
http://www.mikroe.com/timer-calculator/
It generate the ccode for settiing properly a lot of different microcontroller

Resources