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.
Related
I'm currently working on an ECG project and I'm having some difficulties using the Waveshare High-Precision AD/DA Board (which has an ADS1256 ADC and is meant for use with RPI) with the Terasic DE10-Nano Kit.
I'm using an Altera SPI Master peripheral in Qsys with the following settings:
SCLK rate: 20000
Data Width: 8 bits
Shift Direction: MSB first
Clock Polarity: 0
Clock Phase: 1
No synchronizer stages
For the remaining required signals I'm using Avalon PIO:
DRDY (Input)
RST (output)
PWR (output)
Additionally, there's a Nios2 CPU which makes use of alt_avalon_spi_command to send SPI commands and IOWR_ALTERA_AVALON_PIO_DATA to control the PIO.
The issue I'm experiencing, is that the DRDY signal is never asserted (it's expected to go LOW before being able to read data). I understand this signal should be used with a pull-up resistor; I've tried the following, neither of which worked:
Configure internal pull up on DE10-Nano GPIO pin
Use external pull up resistors (10K/56K)
I understand I should be able to read the Chip ID of the ADS1256, prior to configuring it. I first reset the device, as per:
https://github.com/waveshare/High-Precision-AD-DA-Board/blob/master/Jetson%20nano/ADS1256/C/obj/ADS1256.c#L39
/* RESET */
IOWR_ALTERA_AVALON_PIO_DATA( WAVESHARE_ADS_RST_BASE, HIGH );
usleep(200);
IOWR_ALTERA_AVALON_PIO_DATA( WAVESHARE_ADS_RST_BASE, LOW );
usleep(200);
IOWR_ALTERA_AVALON_PIO_DATA( WAVESHARE_ADS_RST_BASE, HIGH );
Then I wait for DRDY to go LOW and this never seems to happen, regardless of the above-mentioned setup for the pull up resistor:
alt_u8 ADS1256_wait_DRDY( void ) {
for( int i=0; i<50; i++ ) {
alt_u8 drdy = ADS1256_DRDY_get_level();
if ( drdy == LOW ) {
printf( "DRDY asserted\n" );
return 0;
} else {
usleep( DELAY_DRDY );
}
}
printf( "Timeout: DRDY not asserted.\n" );
return -1;
}
I'm probing using a Hobby Components Logic Analyser. The pin connections are elaborated below:
AD/DA RPI PIN DE10-Nano Location Standard HDL Signal Direction Logic Analyzer
==========================================================================================================
3v3 3v3 3v3
GND GND GND
MOSI 19 (GPIO 10) GPIO_0(0) PIN_V12 3.3V LVTTL SPI_MOSI OUT D0
MISO 21 (GPIO 9) GPIO_0(2) PIN_W12 3.3V LVTTL SPI_MISO IN D1
SCK 23 (GPIO 11) GPIO_0(4) PIN_D8 3.3V LVTTL SPI_SCK OUT D2
P3 (CS_PIN) 15 (GPIO 22) GPIO_0(1) PIN_E8 3.3V LVTTL SPI_SS_n(0)* OUT D3
P1 (RST) 12 (GPIO 18) GPIO_0(3) PIN_D11 3.3V LVTTL ADS_RST OUT D4
P0 (DRDY) 11 (GPIO 17) GPIO_0(6) PIN_AE15 3.3V LVTTL WAVESHARE_DRDY IN D5
The logic analyser output is as following:
PulseView
Any help on how to set this up is much appreciated. Apologies if my post is not great, I'm fairly new to posting on these forums.
Some of the pin assignments were not correct. After fixing these and testing with a different microcontroller instead of the AD/DA board, the DRDY is asserted, but the issue persists with the waveshare board. It looks like a board specific issues, so I've contacted the manufacturer for assistance.
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.
I have a device, and it has digital i/o, analog i/o. I send to device below commands for communication.The device has gpio module. My device documantation is here
Write to digital input : gpio set/clear x
Read from digital output : gpio read x
Read from digital output : adc read x
(x : pin number)
How can I create sine/square wave and calculate amplitude? To create square wave :
open device
sleep
write to device low mode(t0)
sleep
write to device high mode
sleep
write to device low mode(t1)
period = (t1 - t0)
Is this a square wave?
it seems your example is indeed square wave
if write to device low mode(t0) sets the output pin to low and write to device low mode(t1) to high or reverse then period is the sum of the sleeps + some time for setting up GPIO state. Don't know why you have times inside the GPIO set lines and not in sleeps ... (may be something platform dependent?)
To go to sin wave
use DAC or PWM + RC filter with some precomputed amplitude table in which the index is increasing periodically.
BYTE sintab[32]={ 128,...,255,...,128,...,0,....,127 };
encoded: 128 is zero, 255 is +1 and 0 is -1; now just add some index:
int ix=0'
and once in a while (on some timer perhaps) increment it and set the output to new value:
ix=(ix+1)&31;
that and 31 just cause tho cycle the index from start again if end reached (sintab must be of size power of 2). The period is timer frequency/sintab size
[notes]
You can modify this to your purpose for example make sintab[][] a 2D array where first index means amplitude and second is the ix as now. On older platforms (MCU's) you can encode the PWM sequence directly to sintab end so on ...
You can pre-compute the sintab values like this:
sintab[ix]=128.0+127.0*sin(float(2.0*M_PI*ix)/32.0);
or if your platform supports fast enough sin you can use above line directly without the actual array ...
[edit1]
for sinwave you can use just 0/1 states. If you need analog output and:
You have DAC (digital to analog converter)
then send the actual amplitude to it like dac write sintab[ix]; that will create the analog voltage on the output pin for you.
You do not have any spare DAC's use PWM Pulse Width Modulation instead
it is an old school trick to avoid the need of DAC and still have analog output from digital pin. It works like this:
The output value is the cumulative energy/voltage per time chunk so you generate square-wave signal
ratio 1:1 means that half the period is H and the rest L
ratio 2:1 means that 2/3 of the period is H and the rest L
The more time the output is H the bigger output value. This is still digital output but if you connect on it any nonlinear device like capacitor or coil then the energy inertia will cause to drop the H voltage to some level dependent on the square-wave ratio. Most common is the RC filter (R is serial and C is parallel to ground). If you want to drive some coil (motor) then you do not need the filter. This kind of use usually generate high pitch sound (the PWM frequency) often heard near machinery ...
The PWM frequency has to be high enough (many times higher then the sinwave frequency)
Some code for PWM with amplitude and frequency setting:
const int timer_T=1; // used timer interval [ms]
const int PWM_max=10; // PWM max amplitude+1
int PWM_s=0; // PWM actual step
int PWM_t=0; // PWM actual time
int PWM_a=3; // PWM amplitude <0,PWM_ratio_max)
int PWM_T=200; // PWM period [ms]
void OnTimer()
{
int PWM_T0=PWM_T/PWM_max; // PWM step period must be >=1 !!!
PWM_t+=timer_T;
if (PWM_t>=PWM_T0)
{
if (PWM_s<=pwm_a) gpio set x; else gpio clear x;
PWM_s++; if (PWM_s>=PWM_max) PWM_s=0;
PWM_t-=PWM_T0;
}
}
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