How to generate note without library? - c

I'm trying to generate note for example Do , do's frequency is 523.
I wrote some codes but, i did not work
Systick 8 mhz
void note1(void){ // Note Do
for (int i = 0; i < 523; i++){
GPIOE->ODR = 0x4000;
delay_ms(1);
GPIOE->ODR = 0x0000;
delay_ms(1);
}
}
How can we solve this problem ?
EasyMx Pro v7
I'm calling the function like that
void button_handler(void)
{
note1();
// Clear pending bit depending on which one is pending
if (EXTI->PR & (1 << 0)){
EXTI->PR = (1 << 0);
}
else if (EXTI->PR & (1 << 1)){
EXTI->PR = (1 << 1);
}
}
523 times sending 1 and 0 and delay_ms 1 = 1 ms
1000 = 1 sec

On STM32 (as I can see you have it) you have timers which can be configured as PWM output.
So use timer, set period and prescaler values according to your needed frequency and set duty cycle on channel to 50%.
If you need 523Hz PWM output, then set your timer PWM to 523Hz using prescaler and period value:
timer_overflow_frequency = timer_input_clock /
(prescaler_value + 1) /
(period_value + 1) ;
Then, for your output channel set value half of timer period value.
For standard peripheral library, tutorial can be used from here:
https://stm32f4-discovery.net/2014/05/stm32f4-stm32f429-discovery-pwm-tutorial/
Link from unwind for Cube https://electronics.stackexchange.com/questions/179546/getting-pwm-to-work-on-stm32f4-using-sts-hal-libraries

You appear to have a fundamental misunderstanding. In your code note1(), the value 523 will affect only the duration of the note, nit its frequency. With 1ms high, 1ms low repeated 523 times you will generate a tone of approximately 500Hz for approximately 1.43 seconds. I say "approximately" because there will be some small overhead in the loop other then the time delays.
A time delay resolution 1ms is insufficient to generate an accurate tone in that manner. To do it in the manner you have, each delay would need to be 1/2f seconds, so for 523Hz approximately 956ms. The loop iteration count would need to be ft, so for say .25 seconds, 131 iterations.
However if button_handler() is as it appears to be an interrupt handler, you really should not be spending 1.46 seconds in an interrupt handler!
In any event this is an extraordinarily laborious, CPU intensive and inaccurate method of generating a specific frequency. The STM32 on your board is well endowed with hardware timers with direct GPIO output that will generate the frequency you need accurately with zero software over head. Even if none of the timers map to a suitable GPIO output that you need to use, ou can still get one to generate an interrupt at 1/2f and toggle the pin in the interrupt handler. Either way that will leave the processor free to do useful stuff while the tone is being output.

Related

I can't understand how to set period of PWM signal

i would like to create a PWM signal. And i want the frequency to be close to 38 khz. My theoretical calculation for period is 26.3 microseconds. So i choose 26 microseconds. And i can observe my signal.
But i don't understand how my code works properly :)
(My clock frequency is 1MHz so my clock signal is 1 microseconds )
if((P1IN & BIT3)!=BIT3) { // if button is pressed
for(i=0;i<692;i++){ // pwm signal's duration is 9ms
P2OUT^=0x01; // switch from 1 to 0 or vice versa
__delay_cycles(4);
}
P2OUT=0x00;
}
my calculation is:
i <692,i++,P2OUT^=0x01; // total 3 cycles
__delay_cycles(4); //total 4 cycles
so 4+3=7. but i'm confused because i think it should be 13 not 7
(here is my signal)
https://e2e.ti.com/cfs-file/__key/communityserver-discussions-components-files/166/f0fd36b0_2D00_bebd_2D00_4a31_2D00_b564_2D00_98962cf4749e-_2800_1_2900_.jpg
You can not calculate cycles based on C or C++ code. you need to check the assembly file(s) generated during the compilation of the program. Based on your compiler (which you did not mention) you can pass some some compiler parameters/switches to ask the compiler to leave the generated assembly file(s) in place for you to check the generated assembly instructions. but basically the for loop would have a jump instruction which may take 2/3 cycles and you did not calculate that.
I recommend that you later check the number of cycles of each instruction from the micro controller datasheet.
The posted code (per your calculations) switches the output every ~7cycles. and does this 692 times. For a total of 346 total cycles, however the total pulse ON time is only ~7cycles. Suggest:
if((P1IN & BIT3)!=BIT3)
{ // if button is pressed
// start pwm signal
P2OUT = 0x01;
for(int i=0; i< (9*1000);i++) // may need to be adjusted
{ // so pwm signal's duration is 9ms
_delay( 1 );
}
// stop pwm signal
P2OUT=0x00;
// wait for button to be released
while( P1IN & BIT3)!=BIT3 ){;}
}
I'm not familiar with your microcontroller's PWM details, However, most have an initialization to set how fast the PWM timer counts and its start/termination count and if it repeats and if the output is a square wave or a step up or a step down signal and the percentage of ON .vs.OFF time.
However, the posted code indicates the PWM is only a regular GPIO bit.
The posted code indicates the PWM on percentage is to be 50percent. Is this what you want?

Erroneous Pulsetrain Timing

I am having some trouble with a timer on my Arduino atmega328p-pu with 16MHz clock.
I have a really simply program with only one timer, two ISRs, and one pin.
The program does the following:
Iterates through the bits of 'sequence' and sets pin4 high or low respectively. However it doesnt set the bit high for the entire period, only 1/12 of it. What you see below is a single timer that counts up from 0 to 340. There is ISRB at 28, and then ISRA happens at 340, then it loops (that is what CTC mode does, loops after ISRA). ISRB always turns off the pin, and ISRA handles whether or not the pin should be high.
Now then the problem. All the timing works for each bit, but for some reason the loopover event causes the pulse spacing to SHORTEN. Yes shorten, not widen (which if anything was what I would expect because of extra clock cycles for executing the loop event).
it makes waveforms that look like this.
_|_|_|_|_ _ _ _ _|_|_|_||_|_|_|_ _ _ _
You can see that the problem resides in the junction between two packets, but the rest of the timing is good. I cant seem to track down why.
#include <stdint.h>
uint32_t sequence =0b111100001111; // example data sequence
uint8_t packetlength = 12;
uint8_t index = 0;
void setup(){
DDRD = 0xFF; // all port D as input
bitSet(DDRD, 4); // board pin 4 output
bitSet(PORTD, 4); // start high
// initialize timer1
TCCR1A = 0; // zeros timer control register
TCCR1B = 0;
TCNT1 = 0; // sets timer counter to 0
OCR1A = 340; // compare match register 340*62.5ns = 21.25us
OCR1B = 28; // 28*62.5ns = 1.75us
TIMSK1 = 0;
TCCR1B |= (1 << WGM12); // CTC mode
TCCR1B |= (1 << CS10); // CS10 no prescaler (use CS12 for 256 prescaler for testing)
TIMSK1 |= (1 << OCIE1A); // enable timer compare A interrupt
TIMSK1 |= (1 << OCIE1B); // enable timer compare B interrupt
}
ISR(TIMER1_COMPA_vect){ // controls bit repeat rate
if (bitRead(sequence,index) == 1){
bitSet(PORTD, 4); //set high
}
index ++;
if (index == packetlength){ //loop over when end reached.
index = 0;
}
}
ISR(TIMER1_COMPB_vect){ // controls duty cycle
bitClear(PORTD, 4); // set low
}
void loop(){
//nothing
}
Edit: April 5. Scope photos demonstrating inter pulsetrain period shortening.
The important measurement value is BX-AX
Normal. 340 + 6 calculation clock cycles (best estimate from scope)
Bad. Timer only counting 284 cycles before interrupt is firing.
Also Bad, but not a huge problem. This pulse is far to wide to be reasonably explained by the clock cycles needed set the bit low. It appears to take 17, I would expect 3.
I do not see why you should expect precise timing at the bit output. Interrupts begin after a delay once requested which will vary depending on instruction execution time for each instruction being run in whatever is being interrupted. I suspect (without seeing evidence in your report of the problem) that the variation you see is identical to instruction execution time variation.
If you want precise hardware output timing, you must either use never-interrupted programmed I/O or use the various flip-bit-upon-timer-compare features of the uP's hardware peripheral set. The ISR can be used to set things up for the next compare, but not to directly flip output bits.
Once you've figured out how to setup the action to be performed by the hardware upon comparitor matches, it will be simpler to do it all in a single ISR. That service routine can arrange for both the conditional bit set and the following unconditional bit clear. You probably want the ISR to run during the lengthier part of the cycle so that latency in the actual running of your [a] ISR code does not cause the setup to be too late.
[a. In addition to your ISR code, the programming environment is causing some context save a restore to wrap what you wrote. This can add execution cycles that might not be expected. Auto-generated context save/restore often is extravagant about tucking away state so that naive programmers are not puzzled by strange foreground-background interactions. ]

How to get millisecond resolution from DS3231 RTC

How to get accurate milliseconds?
I need to calculate the delay of sending data from Arduino A to Arduino B. I tried to use DS3231 but I cannot get milliseconds. What should I do to get accurate milliseconds from DS3231?
The comment above is correct, but using millis() when you have a dedicated realtime clock makes no sense. I'll provide you with better instructions.
First thing in any hardware interfacing project is a close reading of the datasheet. The DS3231 datasheeet reveals that there are five possible frequencies of sub-second outputs (see page 13):
32 KHz
1 KHz
1.024 KHz
4.096 KHz
8.192 KHz
These last four options are achieved by various combinations of the RS1 and RS2 control bits.
So, for example, to get exact milliseconds, you'd target option 2, 1KHz. You set RS1 = 0 and RS2 = 0 (see page 13 of the datasheet you provided) and INTCN = 0 (page 9). Then you'd need an ISR to capture interrupts from the !INT/SQW pin of the device to a digital input pin on your Arduino.
volatile uint16_t milliseconds; // volatile important here since we're changing this variable inside an interrupt service routine:
ISR(INT0_vect) // or whatever pin/interrupt you choose
{
++milliseconds;
if(milliseconds == 999) // roll over to zero
milliseconds = 0;
}
OR:
const int RTCpin = 3; // use any digital pin you need.
void setup()
{
pinmode(RTCpin, INPUT);
// Global Enable INT0 interrupt
GICR |= ( 1 < < INT0);
// Signal change triggers interrupt
MCUCR |= ( 1 << ISC00);
MCUCR |= ( 0 << ISC01);
}
If these commands in setup() don't work on your Arduino, google 'Arduino external interrupt INT0'. I've shown you two ways, one with Arduino code and one in C.
Once you have this ISR working and pin3 of the DS3231 connected to a digital input pin of your choosing, that pin will be activated at 1KHz, or every millisecond. Perfect!
// down in main program now you have access to milliseconds, you might want to start off by setting:
// When 1-second RTC changes seconds:
milliseconds = 0; // So you can measure milliseconds since last second.
That's all there is to it. All you need to learn now is how to set the command register using I2C commands and you're all set.
The C code example gains 1ms every second. Should be:
{
if (milliseconds == 999) // roll over to zero
milliseconds = 0;
else
++milliseconds;
}

How to control a motor with atmega 32 pwm

I have been for some time on how to control a motor (control its speed) with fast pwm mode with my atmega32. I need to use the 8-bit Timer0, because i have other uses for the other counters. I think I know how to inialise the timer for this task:
void initial_io (void){
DDRC = 0xFF;
DDRB = 0xFF;
PORTA = (1<<PA4)|(1<<PA5);
TCCR0 = (1<<WGM01)|(1<<WGM00); // PWM mode : Fast PWM.
TCCR0 = (1<<CS02)|(1<<CS00); // PWM clock = CPU_Clock/1024
}
But then comes the problem. I simply don't know what to do next, what to do on my main.
My exact project is to drive a remote controlled car with acceleration. So when I ask from the car to go forward it must accelerate from stop to maximum speed whith fixed acceleration. I don't know any Assembly, so if you can help me please do it in C. Any help will be much appreciated.
Well, I guess you're okay with all port B and C pins being outputs. Also, you shouldn't need to do anything in assembly on AVR. The assembly-only stuff is available as macros in avr-libc.
Setup
First off, you're setting up TCCR0 wrong. You have to set all the bits at once, or you have to use a read-modify-write operation (usually TCCR0 |= _BV(bit_num); to set a bit or TCCR0 &= ~_BV(bit_num); to clear it). (_BV(N) is an avr-libc macro that's more legible than the (1<<N) stuff you're using, but does the same thing.) Also, you're missing the polarity of your PWM output, set by the COM00 and COM01 bits. Right now you have them (implicitly) disabled PWM output (OC0 disconnected).
So I'm going to assume you want a positive-going PWM, i.e. larger PWM input values result in larger high-output duty cycles. This means COM01 needs to be set and COM00 needs to be cleared. (See pp. 80-81 of the ATmega32(L) data sheet.) This results in the setup line:
TCCR0 = _BV(WGM01) | _BV(WGM00) // PWM mode: Fast PWM.
| _BV(COM01) // PWM polarity: active high
| _BV(CS02) | _BV(CS00); // PWM clock: CPU_Clock / 1024
Duty Cycle
Now we get to the actual duty cycle generation. Timer 0 is pretty stupid, and hard wires its BOTTOM to 0 and TOP to 0xFF. This means that each PWM period is PWM_Clock / 256, and since you set PWM_Clock to CPU_Clock / 1024, the period is CPU_Clock / 262144, which is about 33 ms for an 8 MHz CPU clock. So each PWM clock, this counter counts up from 0 to 255, then loops back to 0 and repeats.
The actual PWM is generated by the OC circuit per Table 40. For the COM0* setting we have, it says:
Clear OC0 on compare match, set OC0 at BOTTOM
What this means is that each time the counter counts up, it compares the count value to the OCR0 register, and if they match it drives the OC0 output pin to GND. When the counter wraps around to 0, it drives the pin to VCC.
So to set the duty cycle, you just write a value corresponding to that duty cycle into OCR0:
OCR0 = 0; // 0% duty cycle: always GND.
OCR0 = 64; // 25% duty cycle
OCR0 = 128; // 50% duty cycle
OCR0 = 172; // 67% duty cycle
OCR0 = 255; // 100% duty cycle; always VCC. See below.
That last case is there to resolve a common problem with PWM: the number of possible duty cycle settings is always one more than the number of count steps. In this case, there are 256 steps, and if the output could be VCC for 0, 1, 2, … 256 of those steps, that gives 257 options. So rather than prevent either the 0% or 100% case, they make the one-shy-of-100% case disappear. Note 1 on Table 40 says:
A special case occurs when OCR0 equals TOP and COM01 is set. In this case, the compare match is ignored, but the set or clear is done at BOTTOM.
One more thing: if you write to OCR0 in the middle of a PWM cycle, it just waits until the next cycle.
Simulating Acceleration
Now to get the "constant acceleration" you want, you need to have some kind of standard time base. The TOV0 (timer 0 overflow) interrupt might work, or you could use another of the timers or some kind of external reference. You'll use this standard time base to know when to update OCR0.
Constant acceleration just means that the speed changes linearly with time. Taking this a step further, it means that for each update event, you need to change the speed by a constant amount. This is probably nothing more than saturation arithmetic:
#define kAccelStep 4
void accelerate_step() {
uint8_t x = OCR0;
if(x < (255 - kAccelStep))
OCR0 = x + kAccelStep;
else
OCR0 = 255;
}
Just do something like this for each time step and you'll get constant acceleration. A similar algorithm can be used for deceleration, and you could even use fancier algorithms to simulate nonlinear functions or to compensate for the fact that the motor does not instantly go to the PWM-specified speed.
As you seem to be a beginner on AVR programming, I suggest you go the easy way: start with Arduino.
The Arduino environment offers simple functions so you don't need to manipulate the processor registers directly. For instance, to control a PWM output, you simply have to call analogWrite() (documentation here)
Here is a tutorial to hook up a motor to an Arduino.
Here is a tutorial to program a ATMega32 from Arduino IDE

Setting up multiple timers with AVR

I am trying to set up two timer interrupt routines with Teensy 2.0 Microcontroller (which is based on ATMEGA32U4 8 bit AVR 16 MHz) for independent control of two servo motors
After much trial - I was able to set one up on pin 7 of port C, but
How do I set up the second ISR to be initialized and called independently of the first?
Do I need to setup the second timer and, if so, what would such code look like?
Here is the setup code:
int main(void)
{
DDRE = 0xFF;
TCCR1A |= 1 << WGM12; // Configure timer 1 for CTC mode
TCCR1B = (1<<WGM12) | (1<<CS11) ;
OCR1A = 1000; // initial
TIMSK1 |= 1 << OCIE1A; // Output Compare A Match Interrupt Enable
sei(); // enable interrupts
// ...code that sets pulseWidth based on app logic variable.
// Not showing as its not important
}
ISR(TIMER1_COMPA_vect)
{
if (0 == pulseWidth)
{
return;
}
static uint8_t state = 0;
int dutyTotal = 20*1000;
if (0 == state)
{
PORTC |= 0b10000000;
OCR1A = pulseWidth;
state = 1;
}
else if (1 == state)
{
PORTC &= 0b01111111;
OCR1A = dutyTotal - pulseWidth;
state = 0;
}
}
While it's difficult to give a definitive answer without knowing more about your application (e.g. what kind of servo/motor, - I'm guessing model RC type with 1-2ms pule?) there are two approaches to solving the problem:
Firstly, In your code you seem to be manually generating a PWM signal by toggling PC7. You could add another output by increasing your number of states - you need one more than the number of servos to give the gap which sets the pulse repetition frequency. This is a common technique when you need to drive a lot of servos, since most RC servos don't care about pulse phasing or frequency (within limits), only the pulse width, so you can generate a bunch of different pulses one after the other on different outputs while only using one timer like this (in a sort of pseudo-code state diagram):
State 0:
Turn on output 1
Set timer TOP to pulse duration 1.
Go to state 1:
State 1:
Turn off output 1
Turn on output 2
Set timer TOP to pulse duration 1.
Go to state 2:
State 2:
Turn off output 2
Set timer TOP to pulse duration 3.
Go to state 0:
"Pulse duration 3" sets the PRF (pulse repetition frequency). If you want get fancy,
you can set this to 1/f-pd1-pd2, to give a constant frequency.
[ "TOP" is AVR-speak for the thing that sets the wrap (overflow) rate of the timer. See data sheet. ]
Secondly, there is a much easier way if you're only using two servos - use the hardware PWM functionality of the timer. The AVR timers have a built-in PWM function to do the pin-toggling for you. Timer1 on the mega32 has two PWM output pins, which could work great for your two servos and you then don't (necessarily) need an interrupt handler at all.
This is also the right solution if you are PWM driving motors directly (e.g. through an H-Bridge.)
To do this, you need to put the timer into PWM mode and enable the OC1A and OC1B output pins, e.g.
/*
* Set fast PWM mode on OC1A and OC1B with ICR1 as TOP
* (Mode 14)
*/
TCCR1A = (1 << WGM11) | (1 << COM1B1) | (1 << COM1A1);
TCCR1B = (3 << WGM12);
/*
* Clock source internal, pre-scale by 8
* (i.e. count rate = 2MHz for 16MHz crystal)
*/
TCCR1B |= (1 << CS11);
/*
* Set counter TOP value to set pulse repetition frequency.
* E.g. 50Hz (good for RC servos):
* 2e6/50 = 40000. N.B. This must be less than 65535.
* We count from t down to 0 so subtract 1 for true freq.
*/
ICR1 = 40000-1;
/* Enable OC1A and OC1B PWM output */
DDRB |= (1 << PB5) | (1 << PB6);
/* Uncomment to enable TIMER1_OVF_vect interrupts at 50Hz */
/* TIMSK1 = (1 << TOV1); */
/*
* Set both servos to centre (1.5ms pulse).
* Value for OCR1x is 2000 per ms then subtract one.
*/
OCR1A = 3000-1;
OCR1B = 3000-1;
Disclaimer - this code fragment compiles but I have not checked it on an actual device so you may need to double-check the register values. See the full datasheet at http://www.atmel.com/Images/doc7766.pdf
Also, you perhaps have some typos in your code, bit WGM12 doesn't exist in TCC1A (you've actually set bit 3, which is FOC1A - "force compare", see datasheet.) Also, you are writing DDRE to enable outputs on port E but toggling a pin on port C.
Halzephron, thank you so much for your answer. I dont have the high enough reputation to mark your answer, hopefully someone else will.
Details below:
You are absolutely right about being able to use a single IRS to control a number of servos - embarrassing I did not think of that, but given how well your simpler solution worked - I'll just use that.
... Also, you are writing DDRE to enable outputs on port E but toggling a pin on port C.
Thanks, I commented out that line, and it works the same - (so I dont need to enable output at all?)
Bit WGM12 doesn't exist in TCC1A (you've actually set bit 3, which is FOC1A - "force compare", see datasheet.)
I removed that too, but leaving the rest of my code unchanged - it results in servos moving slower, with way less torque and jittering as they do, even after arriving at desired position. Servo makes a weird "shaky" noise (frequency ~10-20, I'd say) and the arm it trembling, so for the reasons I don't understand - setting this bit seems necessary.
I suspected that a timer per motor is highly inelegant, and so I really like your second approach (built - in timer-generated PWM),
... This is also the right solution if you are PWM driving motors directly (e.g. through an H-Bridge.)
Very curious, why? I.e. what's the difference between using this method vs, say, timer-generated PWM.
In your code, I had to change below line to get 50HZ, otherwise I was getting 25HZ before (verified with a scope)
ICR1 = 20000-1; // was 40000 - 1;
One other thing I noticed with scope was that the timer-generated PWM code I have - produces less "rectangular" shape than the PWM code snippet you attached. It takes about 0.5 milliseconds for the signal to drop off to 0 with my code, and its absolutely instantaneous with yours (which is great). This solved another problem I had been bashing my head against: I could get analog servos to work fine with my code (IRS-generated PWM), but any digital servo I tried - just did not move, as if it was broken. I guess the shape of the signal is critical for digital servos, I never read this anywhere. Or maybe its something else I dont know.
As a side note, another weirdness I spent a bunch of time on - I uncommented the TIMSK1 = (1 << TOV1); line, thinking I always needed it - but what happened, my main function would get frozen, blocked forever upon first call to delay_ms(...) not sure what it is - but keeping it commented out unblocks my main loop (where I read the servo positin values from the USB HID using Teensy's sample code)
Again, many thanks for the help.

Resources