I'm implementing PWM on ATmega16 in order to be able to control 20 concurrent servos. I've successfully implemented 16 servos on timer 1, while timer 2 has problems, changing the values of output angles during runtime. i.e. when using pre-initialized angles for Timer 2, PWM signals are correct, while changing the angle values in real time causes modified angle values NOT to be output (no signal is output)
ISR(TIMER2_OVF_vect) {
TCNT2 = 0xFF - (SERVO_TIME*(F_CPU/1000000))/256 - truncf(50/8); // set max duty cycle , 50 added to sum up the 20 ms period
servo_index_2++;
if (servo_index_2 > 7)
servo_index_2 = 0;
OCR2 = TCNT2 + angles0[servo_index_2];
PORTC |= 1 << servo_index_2 ;
}
why ISR is not setting angles on runtime
Related
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;
}
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.
The problem is that the timing behavior is totally wrong (factor 30).
I have an ATmega 644PA which is running with 8 Mhz (CKDIV8 fuse is not set).
I wanted an interrupt every 1 ms. I use Timer1 (16 bit timer) in the compare mode.
The prescaler was choiced by the timing range which i need.
Here 1 ms to ~2s. (limited through the 16 bit timer reg.)
Calculation
CPU: 8.000.000 Hz
Prescaler: 256
8.000.000 Hz / 256 = 31250 Cycles / s (Hz)
1000 ms = 31250 Cycles
1 ms = 30.25 Cycles (31.25 - 1, timer reg. start by 0)
From here i can calculate it up, for example:
100 ms = 3025 Cycles
2s = 60500 Cycles
Code
Timer Init
cli(); // disable global interrupts
TCCR1A = (1 << WGM01); // CTC ON
TCCR1B = 0x04; // Prescaler 256
OCR1A = 30; // set compare reg.
TIMSK1 = (1 << OCIE1A); // set interrupt mask
TCNT1 = 0x00; // set counter reg. to zero
sei(); // enable global interrupts
ISR:
ISR(TIMER1_COMPA_vect)
{
// start own code
...
// end own code
TCNT1 = 0x00; // reset counter reg. to zero after match (same which should CTC do)
}
Now i have for example a volatile variable which will be incremented in the ISR function.
I'm polling the variable and wait so long till it reach 60500, which is equal to 2s.
After it i just set an led on.
I measured it by phone and it takes 1 minute till the led goes on.
It takes to long with the factor 30 => 60s divided by 2s are 30.
I also tried to use an external program to calculate it, it came to the same results. (0x1E = 30)
Someone an idea where my mistake is ?
2 seconds is 60500 cycles. But your ISR is incrementing the volatile variable every 1 millisecond. Naturally, it will take 60.5 seconds for the variable to reach 60500.
As a side note, it is better to change the compare value, rather than reset TCNT, to avoid timing drift. That is, let the counter run freely, and use OCR1A += 31; to hit the next interrupt. Resetting TCNT as you do will ignore the time inside the ISR.
I tried controlling the servo with softPwm using the wiringPi Library but this made the servo stutter.
Therefore I want to use the hardware PWM pin on the Raspberry Pi (GPIO18) with the wiringPi library.
But I don't understand how to set the frequency to 50 Hz and change the duty cycle to have a pulse width ranging from 0.8 ms to 2.5 ms.
I found the following relationship on the internet (i dont know if it is correct):
pwmFrequency in Hz = 19.2e6 Hz / pwmClock / pwmRange.
i know the clock divisor max value is something around 4000 and the Raspberry Pi PWM clock has a base frequency of 19.2 MHz. so this gives me ~4,8KHz.
i already got these settings which should give me ~50Hz using the following relationship:
//put PWM in mark-space mode, which will give you
//the traditional (and easily predictable) PWM
pwmSetMode(PWM_MODE_MS);
//setting ping GPIO 18 as a pwm output
pinMode(18,PWM_OUTPUT);
//Set clock divisor to 4000
pwmSetClock(4000);
pwmSetRange (10) ;
I dont got a oscilloscope to test the output signal to check what setting changes what. this makes it hard to find it out myself.
Long story short:
Can anyone tell me how I can achieve a duty cycle with a pulse width of 0,8ms to 2,1ms for controlling a servo using the hardware PWM on the Raspberry Pi.
I'm a complete newby to Pi and to Servo's. But I got it to work with wiringPi.
It says here that we're looking to create pulse of 1ms to 2ms in length, every 20ms or so. Assuming this 19.2Mhz base clock is indeed correct, setting pwm clock to 400 and pwm range to 1000, should give a pulse at 48Hz or every 20.8 ms. Then setting pwm value to 48 should give you a 1ms long pulse and a pwm value of 96 should give you a 2ms long pulse.
But you need to set the chip in pwm-ms mode.
(Lots of shoulds here, since I do not have an osciolloscope either)
So to set it up:
gpio mode 1 pwm
gpio pwm-ms
gpio pwmc 400
gpio pwmr 1000
And then you can turn the servo from left to right via
gpio pwm 1 48
gpio pwm 1 96
(Actually, the servo I got worked from 28 up to 118; could be the servo)
(The setup sequence seems important; could be a bug)
if (wiringPiSetup () == -1) //using wPi pin numbering
exit (1) ;
pinMode(1, PWM_OUTPUT);
pwmSetMode(PWM_MODE_MS);
pwmSetClock(384); //clock at 50kHz (20us tick)
pwmSetRange(1000); //range at 1000 ticks (20ms)
pwmWrite(1, 75); //theretically 50 (1ms) to 100 (2ms) on my servo 30-130 works ok
return 0 ;
Make sure you are using correct gpio pins!
Models A and B have one hardware PWM on pin 18 BCM (1 wPi).
Models A+ and B+ can output second hardware pwm on pins 13 and 19 BCM (23, 24 wPi)
How about using RPIO instead? Here's the link to the library: http://pythonhosted.org/RPIO/index.html
Here's the PWM example: http://pythonhosted.org/RPIO/pwm_py.html
And you may also use the C source directly: https://github.com/metachris/RPIO/tree/master/source/c_pwm
I got wiringPi to do it through software bit-banging. I might have tried RPIO but my particular application requires that the audio output works, and I understand that RPIO's DMA makes audio go away. I might have also tried wiringPi's softPwm or even softServo, but I also require to run a DC motor through PWM, and I don't want to bring the whole system down to 50Hz just for the servo.
This program worked as a demonstration, has no jitter (because it doesn't continuously drive the positioning pulses), and lands on its target each time with a visibly indistinguishable error. Granted the Pi isn't doing much else at the time to interfere with the program's timing (except running an X server through SSH, gedit, terminal session, everything in top, etc.).
// Servo trial 11/15/14 by SLC
#include <wiringPi.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
wiringPiSetup();
pinMode( 6, OUTPUT );
digitalWrite( 6, HIGH );
int idx = 0, tries = 0;
while ( tries++ < 30 )
{
int i;
const int period[] = { 500, 1500, 2500 };
printf( "Setting period to %i ms\n", period[idx] );
for ( i = 0; i < 20; ++i )
{
// Output going through an inverter (to convert 3.3V to 5V)
digitalWrite( 6, LOW );
usleep( period[idx] );
digitalWrite( 6, HIGH );
usleep( 20 * 1000 );
}
++idx;
idx %= 3;
sleep( 2 );
}
}
My servo is a Radio Shack Micro Servo, which appears identical to the other "micro" servos out there. I also found I could ditch the inverter and just drive the signal using the 3.3V GPIO.
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.