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.
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.