What Prescaler and Counter Period (AutoReload Register) in Timer with PWM generation in STM32CubeIDE should I set to control a DC motor? - timer

I am a begginer in STM. I have STM32 NUCLEO-F411RE, Pololu 1570 6V 2220RPM DC Motor, L298N DC Motor Driver and 6V 1,3Ah Xtreme Acumulator. I want my motor to be just rotating for example with 80% duty cycle pwm. I have it connected as in this picture:Here is picture of my connection
But instead of 2 motors I have one and instead of Arduino I have STM. In my case pin ENA from motor driver is connected to PB6 pin where I set TIM4 with Channel 1 PWM generation. And IN1 pin from motor driver is connected to PA11 and IN2 to PA12. Here is the code which I add by myself to main():
/* USER CODE BEGIN 2 */
HAL_GPIO_WritePin(GPIOA,Dir1_Pin,GPIO_PIN_SET); // Start motor clock wise rotation, Dir1_Pin is PA11 and Dir2_Pin is PA12
HAL_GPIO_WritePin(GPIOA,Dir2_Pin,GPIO_PIN_RESET);
HAL_TIM_Base_Start(&htim4);
HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_1);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
htim4.Instance->CCR1=????; //What number should I put here to have my motor rotating with 80% duty cycle?
}
/* USER CODE END 3 */
}
I have already been looking for explanations on the internet, make calculations for a week, but didn't find anything which would work for my case. Red light on motor driver is lighting. With some of results from my calculations it was making a "tick tick" sound, but the motor was not rotating. I do not know exactly what values can be send to ENA Pin for my specific DC motor.

Duty cycle is just a high-to-low ratio of the PWM clock.
To calculate value of CCR, you have to know PWM length in timer clocks, that is usually set up in ARR, that is mapped to Init.Period structure member:
void PWM_SetDutyCycle(uint16_t dc_percent) {
// recalculate into pulse width
uint16_t dc = htim1.Init.Period * ((uint32_t)dc_percent) / ((uint32_t) 100);
htim4.Instance->CCR1 = dc;
}

Related

DMA data loss when changing PWM frequency during the DMA reading

First time working with DMA here and I'm getting data loss when the frequency of a PWM signal is varied during the DMA reading. The DMA request is triggered by a 16MHz clock. I'm using DMA2 on STM32f429zi.
The DMA direction is peripheral to memory, where I try to read the whole GPIO port E to memory. The data loss is clearly visible in the beginning of the DMA reading when the PWM frequency is changed. If I comment out the code with the varying of PWM frequency (see part of the code below), there is no visible data loss in the DMA reading.
You can see in the attached image that the upper plot loses data points of the sine wave (4 points per period) and in the lower plot there is no data loss.
As I understand, DMA should work independently and not be affected by CPU, but for me it seems to not be the case. Could this be due to working with a too high clock frequency? Or am I missing something else? I'm really stuck on this problem, any help is appreciated.
Regards,
Linda
/*------------------- Comment out when not changing PWM frequency --------------------------------------------------------------------*/
// /* Start 40 MHz */
// TIM3->CCR1 = 1; // 40 MHz
/*------------------------------------------------------------------------------------------------------------------------------------*/
/* Start DMA for ping signal with length BUFFER_SIZE (PA9 (channel 2) is clockout+ of ADC, sampling on falling edge of PA9) */
if (HAL_DMA_Start_IT(htim1.hdma[TIM_DMA_ID_CC2], (uint32_t)&(GPIOF->IDR), (uint32_t)&aDST_Buffer, BUFFER_SIZE) != HAL_OK)
{
/* Transfer Error */
Error_Handler();
}
/*------------------- Comment out when not changing PWM frequency --------------------------------------------------------------------*/
// /* Continue while timer is lower than 1,3 us */
// while (__HAL_TIM_GET_COUNTER(&htim5) - timer_val1 < 13)
// {
// }
// /* Start ping, 4 MHz */
// TIM3->ARR = 20; // period
// TIM3->CCR1 = 9; // pulse
// timer_val1 = __HAL_TIM_GET_COUNTER(&htim5);
// while (__HAL_TIM_GET_COUNTER(&htim5) - timer_val1 < 9) // 5 pulses
// {
// }
//
// TIM3->ARR = 1; // 40Mhz
// TIM3->CCR1 = 1;
// while (__HAL_TIM_GET_COUNTER(&htim5) - timer_val1 < 25)
// {
// }
/*------------------------------------------------------------------------------------------------------------------------------------*/

STM32 maximum interrupt handling frequency

I am trying to implement my own SPI communication from FPGA to STM in which my FPGA serve as MASTER and generate Chip enable and clock for communication. FPGA transmit data at its rising edge and receive data at its falling edge my FPGA code works properly.
In STM side i capture this master clock on interrupts and receive data at its rising edge and transmit at its falling edge but communication not work properly if i increase clock speed from 250khz
According to my understand STM work at 168 Mega hz i set clock setting according to 168Mhz and handling of 1mhz interrupt is not a big problem so can you any guide how i handle this high speed clock in STM
My code is written below
/*
* Project name:
EXTI_interrupt (EXTI interrupt test)
* Copyright:
(c) Mikroelektronika, 2011.
* Revision History:
20111226:
- Initial release;
* Description:
This code demonstrates how to use External Interrupt on PD10.
PD10 is external interrupt pin for click1 socket.
receive data from mosi line in each rising edge.
* Test configuration:
MCU: STM32F407VG
http://www.st.com/st-web-
ui/static/active/en/resource/technical/document/datasheet/DM00037051.pdf
dev.board: EasyMX PRO for STM32
http://www.mikroe.com/easymx-pro/stm32/
Oscillator: HSI-PLL, 140.000MHz
Ext. Modules: -
SW: mikroC PRO for ARM
http://www.mikroe.com/mikroc/arm/
* NOTES:
receive 32 bit data from mosi line in each rising edge
*/
//D10 clk
//D2 ss
//C0 MOSI
//C1 FLAG
int read=0;
int flag_int=0;
int val=0;
int rec_data[32];
int index_rec=0;
int display_index=0;
int flag_dint=0;
void ExtInt() iv IVT_INT_EXTI15_10 ics ICS_AUTO {
EXTI_PR.B10 = 1; // clear flag
flag_int=1; //Flag on interrupt
}
TFT_Init_ILI9340();
void main() {
GPIO_Digital_Input(&GPIOD_BASE, _GPIO_PINMASK_10);
GPIO_Digital_Output(&GPIOD_BASE, _GPIO_PINMASK_13); // Set PORTD as
digital output
GPIO_Digital_Output(&GPIOD_BASE, _GPIO_PINMASK_12); // Set PORTD as
digital output
GPIO_Digital_Output(&GPIOD_BASE, _GPIO_PINMASK_14); // Set PORTD as
digital output
GPIO_Digital_Output(&GPIOD_BASE, _GPIO_PINMASK_15); // Set PORTD as
digital output
GPIO_Digital_Input(&GPIOA_IDR, _GPIO_PINMASK_0); // Set PA0 as
digital input
GPIO_Digital_Input(&GPIOC_IDR, _GPIO_PINMASK_0); // Set PA0 as
digital input
GPIO_Digital_Input(&GPIOC_IDR, _GPIO_PINMASK_2); // Set PA0 as
digital input
GPIO_Digital_Output(&GPIOC_IDR, _GPIO_PINMASK_1); // Set PA0 as
digital input
//interupt register
SYSCFGEN_bit = 1; // Enable clock for alternate pin
functions
SYSCFG_EXTICR3 = 0x00000300; // Map external interrupt on PD10
EXTI_RTSR = 0x00000000; // Set interrupt on Rising edge
(none)
EXTI_FTSR = 0x00000400; // Set Interrupt on Falling edge
(PD10)
EXTI_IMR |= 0x00000400; // Set mask
//NVIC_IntEnable(IVT_INT_EXTI15_10); // Enable External interrupt
while(1)
{
//interrupt is not enable until i push the button
if((GPIOD_ODR.B2==0)&&(flag_dint==0))
{ if (Button(&GPIOA_IDR, 0, 1, 1))
{
Delay_ms(100);
GPIOC_ODR.B1=1; //Status for FPGA
NVIC_IntEnable(IVT_INT_EXTI15_10); // Enable External interrupt
}
}
if(flag_int==1)
{
//functionality on rising edge
flag_int=0;
if(index_rec<31)
{
//display data on led
GPIOD_ODR.B13= GPIOC_IDR.B0;
//save data in an array
rec_data[index_rec]= GPIOC_IDR.B0;
//read data
index_rec=index_rec+1;
}
else
{
flag_dint=1;
NVIC_IntDisable(IVT_INT_EXTI15_10);
}
} // Infinite loop
}
}
Without getting into your code specific, see PeterJ_01's comment, the clock rate problem can be explained by a misunderstanding of throughput in your assumtions.
You assume that given that your STM device has a clock of 168Mhz it can sustain the same throughput of interrupts, which you seem to have conservatively relaxed to 1Mhz.
However the throughput of interrupts it will be able to support is given by the inverse of the time it takes the device to process each interrupt. This time includes both the time the processor takes to enter the service routing (ie detect the interrupt, interrupt the current code and resolve from the vector table where to jump to) plus the time taken to execute the service routine.
Lets be super optimistic and say that entering the routine takes 1 cycle and the routing itself takes 3 (2 for the flags you set and 1 for the jump out of the routine). This gives 4 cycles at 168Mhz is 23.81ns, taking the inverse 42Mhz. This can also be computed by dividing the maximum frequency you would achieve (168Mhz) by the number of cycles spent processing.
Hence our really optimistic bound is 42Mhz, but realistically will be lower. For a more accurate estimate you should test your implementation timings and dig into your device's documentation to see interrupt response times.

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;
}

Generate square wave for analog input/output on software

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;
}
}

Controlling a servo with raspberry pi using the hardware PWM with wiringPi

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.

Resources