2021-12-03 07:58 AM
Goodmorning to everyone,
I read a lot about this argument, and I know that with HAL libraries this topic is really easy to do. Unfortunately due to project request i need to use STL libraries. I create a pwm signal with TIM1. This signal is about 1MHz and i need to change every period the DC of the signal due to transmision protocol needs.
I drop here my program in order to let you understand better what I am doing.
I never used DMA using this libraries and I have totally no ideas where to start, could someone help me?
thank you!
If you need more details this program doesn't works only because every 13 period sent, it stops for about 2 period. This probably because some main interrupts have the priority on the exti one. I thought that this problem could be solved using DMA.
static __IO uint32_t TimingDelay;
volatile int LedOff = 0;
volatile int DataSend = 0; //variable used to avoid strange Tim3 ISR executions
volatile uint8_t LED_Data[MAX_LED][4];
volatile uint8_t LED_Mod[MAX_LED][4]; //used to programme leds brightness
volatile uint16_t pwmData[MAX_BITLED+STOP_BITS];
volatile uint32_t DataIndx = 0;
volatile uint32_t TimIndx = 0;
/* Private function prototypes -----------------------------------------------*/
static void Delay(uint32_t nCount);
#define WAIT_1s 1000 //ticks to get about a second of pause
#define WAIT_50ms 50 //ticks to get about 50ms of pause
#define NEW_FREQUENCY 4200U
/* Led Control constants -----------------------------------------------------*/
#define MAX_LED 8
#define PI 3.14159265
#define USE_BRIGHTNESS 0
#define STOP_BITS 50
#define HIGH_VALUE 8
#define LOW_VALUE 4
#define MAX_BITLED 24*MAX_LED
/* PWM_TIM and PWM values ----------------------------------------------------*/
#define PWM_PSC 1
#define PWM_GPIO GPIOA
#define PWM_PIN_SOURCE GPIO_PinSource8
#define PWM_PIN GPIO_Pin_8
#define PWM_AF_TIMER GPIO_AF_TIM1
#define PWM_TIMER TIM1
#define PULSE_VALUE 12
void TIM_PWMConfig(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_OCInitTypeDef TIM_OCInitStruct;
/* Connect the involved PWM pins to AF -------------------------------------*/
GPIO_PinAFConfig(PWM_GPIO, PWM_PIN_SOURCE, PWM_AF_TIMER);
/* Configure these PWM pins in alternate function mode ---------------------*/
GPIO_StructInit(&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStruct.GPIO_Pin = PWM_PIN;
GPIO_Init(PWM_GPIO, &GPIO_InitStruct);
/* Configure timer ---------------------------------------------------------*/
TIM_TimeBaseStructInit(&TIM_TimeBaseInitStruct);
TIM_TimeBaseInitStruct.TIM_Prescaler = PWM_PSC;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_Period = PULSE_VALUE;
TIM_TimeBaseInit(PWM_TIMER, &TIM_TimeBaseInitStruct);
TIM_ARRPreloadConfig(TIM1, ENABLE);
/* Configure the channels for PWM MODE -------------------------------------*/
TIM_OCStructInit(&TIM_OCInitStruct);
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse = 0;
TIM_OCInitStruct.TIM_OutputNState = TIM_OutputNState_Disable;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStruct.TIM_OCNPolarity = TIM_OCNPolarity_High;
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStruct.TIM_OCIdleState = TIM_OCIdleState_Reset;
TIM_OCInitStruct.TIM_OCNIdleState = TIM_OCNIdleState_Set;
TIM_OC1Init(PWM_TIMER, &TIM_OCInitStruct);
/* Turning on the TIM counter ----------------------------------------------*/
TIM_CtrlPWMOutputs(TIM1, ENABLE);
TIM_Cmd(PWM_TIMER, ENABLE);
/* Enable ARR Prereload ----------------------------------------------------*/
TIM_OC1PreloadConfig(PWM_TIMER, TIM_OCPreload_Enable);
}
void SetPWM(double Duty)
{
TIM_OCInitTypeDef TIM_OCInitStruct;
TIM_OCStructInit(&TIM_OCInitStruct);
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_OutputNState = TIM_OutputNState_Disable;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStruct.TIM_OCNPolarity = TIM_OCNPolarity_High;
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OCIdleState = TIM_OCIdleState_Reset;
TIM_OCInitStruct.TIM_OCNIdleState = TIM_OCNIdleState_Set;
TIM_OCInitStruct.TIM_Pulse = Duty;
TIM_OC1Init(PWM_TIMER, &TIM_OCInitStruct);
TIM_OC1PreloadConfig(PWM_TIMER, TIM_OCPreload_Enable);
}
void PWM_ControlConfig(void)
{
/* EXTI Configuration ------------------------------------------------------*/
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource9);
EXTI_InitTypeDef EXTI_PWM_Struct;
EXTI_StructInit(&EXTI_PWM_Struct);
EXTI_PWM_Struct.EXTI_Line = EXTI_Line9;
EXTI_PWM_Struct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_PWM_Struct.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_PWM_Struct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_PWM_Struct);
/* NVIC Configuration ------------------------------------------------------*/
NVIC_InitTypeDef NVIC_PWM_Struct;
NVIC_PWM_Struct.NVIC_IRQChannel = EXTI9_5_IRQn;
NVIC_PWM_Struct.NVIC_IRQChannelPreemptionPriority = 0x00;
NVIC_PWM_Struct.NVIC_IRQChannelSubPriority = 0x00;
NVIC_PWM_Struct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_PWM_Struct);
}
void EXTI9_5_IRQHandler()
{
if (EXTI_GetITStatus(EXTI_Line9) != RESET)
{
if (TimIndx < DataIndx && DataSend) //If data signal has not finished yet
{
if(TimIndx == 0)
{
TimIndx++; //The first data as already been sent when the trasmission started
}
else if (TimIndx < MAX_BITLED)//after the first data sent, the cycle start the normal behavior
{
SetPWM(pwmData[TimIndx]);
TimIndx++;
}
else //all the data as been sent, now silence as to be delivered in order to let leds know that the signal has ended
{
SetPWM(0);
for(int i = 0; i<350; i++); //set silence for about 50us
//Setting all the values in order to be ready to start another data transmission
DataIndx = 0;
TimIndx = 0;
DataSend = 0;
}
}
}
EXTI_ClearITPendingBit(EXTI_Line9);
}
void Set_LED (int LEDnum, int Red, int Green, int Blue)
{
LED_Data[LEDnum][0] = LEDnum;
LED_Data[LEDnum][1] = Green;
LED_Data[LEDnum][2] = Red;
LED_Data[LEDnum][3] = Blue;
}
void DataLed_Send (void)
{
volatile uint32_t color;
for (int i= 0; i<MAX_LED; i++)
{
color = ((LED_Data[i][1]<<16) | (LED_Data[i][2]<<8) | (LED_Data[i][3]));
for (int i=23; i>=0; i--)
{
if (color&(1<<i)) pwmData[DataIndx] = HIGH_VALUE; // 2/3 of PWM Period
else pwmData[DataIndx] = LOW_VALUE; // 1/3 of PWM Period
DataIndx++;
}
}
for (int i=0; i<STOP_BITS; i++)
{
pwmData[DataIndx] = 0;
DataIndx++;
}
DataSend = 1;
TimIndx = 0;
SetPWM(pwmData[0]);
while(DataIndx);
}
int main(void)
{
/* Setting up the SysTick routine ------------------------------------------*/
SysTick_Config(SystemCoreClock / NEW_FREQUENCY);
/* Peripheral clock enables ------------------------------------------------*/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
/* Setting up peripherals --------------------------------------------------*/
TIM_PWMConfig();
PWM_ControlConfig();
/* Leds initialization -----------------------------------------------------*/
while(1)
{
for(int k = 0; k < MAX_LED; k++) {Set_LED(k, 0, 255, 0);}
DataLed_Send();
Delay(WAIT_1s);
}
}
2021-12-03 08:23 AM
I can't see DMA being mentioned in this code.
OTOH I see EXTI you don't mention in the narrative.
JW
2021-12-03 08:31 AM
.
2021-12-03 08:31 AM
You are right, in this code I change the PWM DC using EXTI interrupt, in fact the output pin of the PWM signal of the TIM1 is physically connected with the EXTI_line_9 pin. I need to change this model into another one with DMA. I cannot find any material that could help me to configure and use DMA, neither an example.
2021-12-03 09:07 AM
> How to use PWM with DMA (STL Libraries)
It seems like you mean the Standard Peripheral Library (SPL), yes?
If you download the SPL, there are examples within the zip file. For example, here is a list of examples for the F4 version:
In addition to this example (DMA\FLASH_RAM), other examples use the DMA:
* <ul>
* <li><B> Complete List of DMA Examples </B>
* - @subpage ADC_DMA
* - @subpage ADC_DualModeInterleaved
* - @subpage ADC_DualModeRegulSimu
* - @subpage ADC_TripleModeInterleaved
* - @subpage ADC_VBATMeasurement
* - @subpage CRYP_AES_DMA
* - @subpage CRYP_TDES_DMA
* - @subpage DAC_SignalsGeneration
* - @subpage DCMI_CameraExample
* - @subpage HASH_SHA1MD5_DMA
* - @subpage I2C_DataExchangeDMA
* - @subpage NVIC_DMAWFIMode
* - @subpage SPI_DataExchangeDMA
* - @subpage TIM_DMA
* - @subpage TIM_DMABurst
* - @subpage USART_DataExchangeDMA
* </ul>
2021-12-03 02:05 PM
Honestly I always heard it call STL (its only few weeks that i'm working with this libraries), but looking for this SPL seems the same, so probably i was just using the wrong name! I'll download this examples and give them a try, in case i will not succeed i'll came back asking for more advice!
TIll now, thank you so much!
2021-12-08 11:16 AM
Here i am again!
Goodevening to everyone, after some experimental days i come back to you for more help.
I tried lots of examples but for some reason noone seems doing the desidered purpose.
Basically when i start the comunication seems that it isn't working, it got stucked waiting the end of the transmission that never come.
I put here my modified code
/* Private typedef -----------------------------------------------------------*/
TIM_OCInitTypeDef TIM_OCInitStruct;
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static __IO uint32_t TimingDelay;
volatile int LedOff = 0;
volatile int DataSentFlag = 0;
volatile uint8_t LED_Data[MAX_LED][4];
volatile uint8_t LED_Mod[MAX_LED][4]; //used to programme leds brightness
volatile uint16_t pwmData[MAX_BITLED + STOP_BITS];
volatile uint16_t DataIndx = 0;
/* Delay constants -----------------------------------------------------------*/
#define SYSTICK_FREQUENCY 1000U
#define WAIT_1s 1000 //ticks to get about a second of pause
#define WAIT_50ms 50 //ticks to get about 50ms of pause
#define NEW_FREQUENCY 4200U
/* Led Control constants -----------------------------------------------------*/
#define MAX_LED 8
#define PI 3.14159265
#define USE_BRIGHTNESS 0
#define STOP_BITS 50
#define MAX_BITLED 24*MAX_LED
/* PWM_TIM and PWM values ----------------------------------------------------*/
#define PWM_PSC 0
#define PWM_GPIO GPIOA
#define PWM_PIN_SOURCE GPIO_PinSource8
#define PWM_PIN GPIO_Pin_8
#define PWM_AF_TIMER GPIO_AF_TIM1
#define PWM_TIMER TIM1
#define PWM_FREQUENCY 850000
/* DMA Values ----------------------------------------------------------------*/
#define TIMEOUT_MAX 10000 //Maximum timeout value
#define DMA_STREAM DMA2_Stream6
#define DMA_CHANNEL DMA_Channel_6
#define BUFFER_SIZE MAX_BITLED + STOP_BITS
static void TIM_PWMConfig(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
/* Enable TIM Clock --------------------------------------------------------*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
/* Connect the involved PWM pins to AF -------------------------------------*/
GPIO_PinAFConfig(PWM_GPIO, PWM_PIN_SOURCE, PWM_AF_TIMER);
/* Configure these PWM pins in alternate function mode ---------------------*/
GPIO_StructInit(&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStruct.GPIO_Pin = PWM_PIN;
GPIO_Init(PWM_GPIO, &GPIO_InitStruct);
/* Configure timer ---------------------------------------------------------*/
TIM_TimeBaseStructInit(&TIM_TimeBaseInitStruct);
TIM_TimeBaseInitStruct.TIM_Prescaler = PWM_PSC;
TIM_TimeBaseInitStruct.TIM_ClockDivision = 0;
TIM_TimeBaseInitStruct.TIM_Period = (SystemCoreClock / PWM_FREQUENCY) - 1;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 1;
TIM_TimeBaseInit(PWM_TIMER, &TIM_TimeBaseInitStruct);
/* Configure the channels for PWM MODE -------------------------------------*/
TIM_OCStructInit(&TIM_OCInitStruct);
TIM_OCInitStruct.TIM_Pulse = 0;
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_OCNPolarity = TIM_OCNPolarity_High;
TIM_OCInitStruct.TIM_OCIdleState = TIM_OCIdleState_Reset;
TIM_OCInitStruct.TIM_OutputNState = TIM_OutputNState_Disable;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStruct.TIM_OCNIdleState = TIM_OCNIdleState_Set;
TIM_OC1Init(PWM_TIMER, &TIM_OCInitStruct);
/* Turning on the TIM counter ----------------------------------------------*/
TIM_Cmd(PWM_TIMER, ENABLE);
/* TIM1 Update DMA Request enable ------------------------------------------*/
TIM_DMACmd(PWM_TIMER, TIM_DMA_CC1, DISABLE);
/* Enable ARR Prereload ----------------------------------------------------*/
TIM_OC1PreloadConfig(PWM_TIMER, TIM_OCPreload_Enable);
/* Main Output Enable ------------------------------------------------------*/
TIM_CtrlPWMOutputs(PWM_TIMER, ENABLE);
}
static void DMA_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
__IO uint32_t Timeout = TIMEOUT_MAX;
/* DMA clock Enable --------------------------------------------------------*/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
DMA_DeInit(DMA_STREAM);
/* Check if the DMA Stream is disabled before enabling it.
Note that this step is useful when the same Stream is used multiple times:
enabled, then disabled then re-enabled... In this case, the DMA Stream disable
will be effective only at the end of the ongoing data transfer and it will
not be possible to re-configure it before making sure that the Enable bit
has been cleared by hardware. If the Stream is used only once, this step might
be bypassed. */
while (DMA_GetCmdStatus(DMA_STREAM) != DISABLE)
{
}
DMA_InitStructure.DMA_Channel = DMA_CHANNEL;
DMA_InitStructure.DMA_PeripheralBaseAddr = TIM_DMABase_CCR1;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)pwmData;
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA_STREAM, &DMA_InitStructure);
/* DMA enable --------------------------------------------------------------*/
DMA_Cmd(DMA_STREAM, ENABLE);
/* Check if the DMA Stream has been effectively enabled.
The DMA Stream Enable bit is cleared immediately by hardware if there is an
error in the configuration parameters and the transfer is no started (ie. when
wrong FIFO threshold is configured ...) */
Timeout = TIMEOUT_MAX;
while ((DMA_GetCmdStatus(DMA_STREAM) != ENABLE) && (Timeout-- > 0))
{
}
// Check if a timeout condition occurred
if (Timeout == 0)
{
// Manage the error: to simplify the code enter an infinite loop
while (1)
{
GPIO_Toggle(GPIOA, GPIO_Pin_5); //toggle pin PA5
Delay(WAIT_1s /5);
}
}
// Enable DMA Stream Transfer Complete interrupt
DMA_ITConfig(DMA_STREAM, DMA_IT_TC, ENABLE);
// Enable the DMA Stream IRQ Channel
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream6_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void DMA2_Stream6_IRQHandler()
{
if (DMA_GetITStatus(DMA_STREAM, DMA_IT_TC) != RESET)
{
/* Clear DMA Stream Transfer Complete interrupt pending bit */
DMA_ClearITPendingBit(DMA_STREAM, DMA_IT_TC);
/* TIM1 Update DMA Request enable ------------------------------------------*/
TIM_DMACmd(PWM_TIMER, TIM_DMA_CC1, DISABLE);
DataSentFlag = 1;
}
}
void SetPWM(double Duty)
{
TIM_OCInitStruct.TIM_Pulse = (Duty / 10) * ((SystemCoreClock / PWM_FREQUENCY) - 1);
TIM_OC1Init(PWM_TIMER, &TIM_OCInitStruct);
TIM_OC1PreloadConfig(PWM_TIMER, TIM_OCPreload_Enable);
}
void DataLed_Send (void)
{
volatile uint32_t color;
for (int i= 0; i<MAX_LED; i++)
{
#if USE_BRIGHTNESS
color = ((LED_Mod[i][1]<<16) | (LED_Mod[i][2]<<8) | (LED_Mod[i][3]));
#else
color = ((LED_Data[i][1]<<16) | (LED_Data[i][2]<<8) | (LED_Data[i][3]));
#endif
for (int i=23; i>=0; i--)
{
if (color&(1<<i)) {pwmData[DataIndx] = (uint16_t)(2/3 * ((SystemCoreClock / PWM_FREQUENCY) - 1));} // 2/3 of PWM Period
else {pwmData[DataIndx] = (uint16_t)(1/3 * ((SystemCoreClock / PWM_FREQUENCY) - 1));} // 1/3 of PWM Period
DataIndx++;
}
}
for (int i=0; i<STOP_BITS; i++)
{
pwmData[DataIndx] = 0;
DataIndx++;
}
/* TIM1 Update DMA Request enable ------------------------------------------*/
TIM_DMACmd(PWM_TIMER, TIM_DMA_CC1, ENABLE);
SetPWM(1); //small value in order to let the interrupt turn on (should not be read as a bit)
while(!DataSentFlag);
GPIO_Toggle(GPIOA, GPIO_Pin_5); //toggle pin PA5
DataSentFlag = 0; //ready for a new transmission
}
int main(void)
{
/* Setting up the SysTick routine ------------------------------------------*/
SysTick_Config(SystemCoreClock / NEW_FREQUENCY);
/* Peripheral clock enables ------------------------------------------------*/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
/* Setting up peripherals --------------------------------------------------*/
TIM_PWMConfig();
PWM_ControlConfig();
/* Leds initialization -----------------------------------------------------*/
while(1)
{
for(int k = 0; k < MAX_LED; k++) {Set_LED(k, 0, 255, 0);}
DataLed_Send();
Delay(WAIT_1s);
}
2021-12-08 11:16 AM
What are your thoughts? basically it sticks in the while at line 218, this because it doesn't never goes inside the handler of the DMA and so doesn't put DatSentFlag = 1.
Could be because it doesn't end the transmsion due to some DMA configuration errors?