2018-03-06 07:22 AM
Hello,
i am working on a application where i try to measure a motor speed and current, and then send the data to the PC using serial uart.
The device used is a STM32F427
For controlling the motor i used a timer1 6pwm channels triggered by timer2 hall xor event.
The motor runs with no problems.
For uart transition i use UART3 with DMA TX
- Timer2 CC1 IRQ event occurs at every change in the state of the 3 hall sensors, here i store the CNT value to the rpm variable.
- in the main program loop i check if a previously DMA transfer has ben made ( flag = 0?)
- if yes, then i set the flag , and read the current value and store to a variable called current , and convert the data to string using sprintf in the following format 1234,5678\r\n (11 chars in lenght)
- after i converted the data, i enable the DMA USART TX request
- when the DMA TC completes, i copy the previously converted data to a temporary string buffer for sending out to the USART, afterwards the DMA TX is disabled
The problem i am facing is when displaying the data on the PC i get very often garbage data.
I have attached a small terminal data showing with red marking the garbage, and a simplified version of the code
How to overcome this?
char tmp[2]='''';
char Buffer[20]='''';
uint16_t buflen=11;
void DMA1_Stream3_IRQHandler(void){ if (DMA_GetITStatus(DMA1_Stream3, DMA_IT_TCIF3)) { DMA_ClearITPendingBit(DMA1_Stream3, DMA_IT_TCIF3);USART_DMACmd(USART3, USART_DMAReq_Tx, DISABLE); // disable DMA USART TX request
for(int k=0; k<buflen; k++)
tmp[k]=Buffer[k];
flag=0; // set }}void TIM2_IRQHandler(){ if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET) {TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);
rpm = calculate_rpm(); // store the time between hall sensor state change
}
}
int main(void){
init_usart_with_dma();
while(1){
if(flag==0){
flag=1; // set the DMA flag// read external current sensor
GPIOA->BSRRH = GPIO_Pin_3; // set CS Low SPI1->DR = 0x00;/// write data to be transmitted to the SPI data register while( !(SPI1->SR & SPI_I2S_FLAG_RXNE) ); // wait until receive complete while( SPI1->SR & SPI_I2S_FLAG_BSY ); // wait until SPI is not busy anymore current =SPI1->DR; // return received data from SPI data register GPIOA->BSRRL = GPIO_Pin_3; // set CS Highsprintf(Buffer,''%04d,%04d\r\n'',current,rpm); // 12chars - convert uint to string
USART_DMACmd(USART3, USART_DMAReq_Tx, ENABLE); // re-enable DMA USART TX request
}
}
}
void init_usart_with_dma(void){ DMA_InitTypeDef DMA_InitStructure; USART_InitTypeDef USART_InitStructure; GPIO_InitTypeDef GPIO_InitStruct2; // USART 3 TX - is tied to DMA1 Stream3//PB 10 - TX
//PB 11 - RXGPIO_InitStruct2.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11; // Pins 10 (TX) and 11 (RX) are used
GPIO_InitStruct2.GPIO_Mode = GPIO_Mode_AF; // the pins are configured as alternate function so the USART peripheral has access to them GPIO_InitStruct2.GPIO_Speed = GPIO_Speed_50MHz; // this defines the IO speed and has nothing to do with the baudrate! GPIO_InitStruct2.GPIO_OType = GPIO_OType_PP; // this defines the output type as push pull mode (as opposed to open drain) GPIO_InitStruct2.GPIO_PuPd = GPIO_PuPd_UP; // this activates the pullup resistors on the IO pins GPIO_Init(GPIOB, &GPIO_InitStruct2);GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_USART3); //
GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_USART3);RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);USART_InitStructure.USART_BaudRate = 38400;// 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART3, &USART_InitStructure); USART_ITConfig(USART3, USART_IT_RXNE, ENABLE); USART_Cmd(USART3, ENABLE); //++++++++++++++++ SETUP DMA ++++++++++++++++++++++++//NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);DMA_DeInit(DMA1_Stream3);DMA_InitStructure.DMA_Channel = DMA_Channel_4;DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; // TransmitDMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)tmp;DMA_InitStructure.DMA_BufferSize = (uint16_t)sizeof(tmp) - 1;DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART3->DR;DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;DMA_InitStructure.DMA_Priority = DMA_Priority_High;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(DMA1_Stream3, &DMA_InitStructure);// Enable the USART Tx DMA request
USART_DMACmd(USART3, USART_DMAReq_Tx, ENABLE);// Enable DMA Stream Transfer Complete interrupt
DMA_ITConfig(DMA1_Stream3, DMA_IT_TC, ENABLE);// Enable the DMA RX Stream
DMA_Cmd(DMA1_Stream3, ENABLE);}
2018-03-06 11:15 AM
IMHO the DMA is perfectly doing its job. To make sure, replace rpm as sprintf argument with a dummy incrementing value, so you will easily sort out which of DMA or calculate_rpm() is wrong.
2018-03-06 02:07 PM
char tmp[2]='';
char Buffer[20]='';
Is that just a typo in your forum post? tmp looks too small if I have understood how you are using it.
2018-03-06 10:49 PM
Hello,
regarding tmp[2] , this is 1 char + space for NULL , it is used to send one char on every dma transfer
i have tried to use a fixed value for rpm in the sprintf function, it seems stable.
After doing more debugging, i have moved most of the code inside the timer2 irq handler, now my DMA irq code looks like this
The DMA now works with the Buffer, i have removed tmp
The DMA when finishes the transfer, sets the flag to 0, normaly when the CC1 IRQ is triggering again, it will check for that flag, if the flag is reset, will process the new data, and prepare for a new DMA trigger, if not it will move on with the rest of the tasks
void DMA1_Stream3_IRQHandler(void)
{if (DMA_GetITStatus(DMA1_Stream3, DMA_IT_TCIF3))
{DMA_ClearITPendingBit(DMA1_Stream3, DMA_IT_TCIF3);
USART_DMACmd(USART3, USART_DMAReq_Tx, DISABLE); flag=0; }}It seems my problem is in the timer2 irq handler.
To sumarize my settings
- timer2 has a input clock (PCLK) of 50 MHZ
- timer2 prescaler is set to 699 ( 700-1)
- period set to 65535-1
- motor RPM min 50, max 5000
so timer2 is ticking with a speed of (1/50MHZ) * 699 = 13.98 uS
It will overflow at 13.98us x 65534 , which is 0.91 seconds, seems long enough
At low speed i should have a time between IRQ calls of 6.9 mS
At high speed i should have a time between IRQ calls of 0.666 ms
Who is storing the number of ticks? TIM2->CNT or TIM2->CCR1 ?
char Buffer[11+1] = ''; // holding buffer for DMA -> USART TX , 11 chars + room for NULL
uint32_t t_sum,t,current;
void TIM2_IRQHandler(){ if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_CC1); GPIOB->BSRRL = GPIO_Pin_12; // set Hi for logic analyzer probecurrent = (GPIO_ReadInputData(GPIOA)& 0x0007); // read current position of the rotor HALL sensor
if(current !=last)
{ last = current; pdata = current; }else
{
return;
}
UH = FORWARD_TABLE_DATASHEET[pdata][0];
UL = FORWARD_TABLE_DATASHEET[pdata][1]; VH = FORWARD_TABLE_DATASHEET[pdata][2]; VL = FORWARD_TABLE_DATASHEET[pdata][3]; WH = FORWARD_TABLE_DATASHEET[pdata][4]; WL = FORWARD_TABLE_DATASHEET[pdata][5];comutate_phases(); // Switch ON/OFF corespondent FET transistors
if(flag==0){
GPIOE->BSRRL = GPIO_Pin_12; // set Hi for logic analyzer probe
flag=1;
GPIOA->BSRRH = GPIO_Pin_3; // set CS4 Low SPI1->DR = 0x00;/// write data to be transmitted to the SPI data register while( !(SPI1->SR & SPI_I2S_FLAG_RXNE) ); // wait until receive complete while( SPI1->SR & SPI_I2S_FLAG_BSY ); // wait until SPI is not busy anymore current =SPI1->DR; // return received data from SPI data register GPIOA->BSRRL = GPIO_Pin_3; // set CS4 High// calculate RPM from ticks
t_sum = (uint16_t)TIM2->CCR1;
t = (uint32_t)(t_sum *14); t= t * 18; t = t/1000; t= 60000/t;sprintf(Buffer,'%04d,%04d\r\n',current,t); // 11 chars - sprintf takes arround 60us to execute, its enough time between irq calls
USART_DMACmd(USART3, USART_DMAReq_Tx, ENABLE);GPIOE->BSRRH = GPIO_Pin_12; // set J17 Lo
}}
}
2018-03-06 10:51 PM
This code is a little incorrect;
EDIT:
I wrote the basis of this code some time ago. // but some important steps have been moved around
:(
Your Code:
while(1){
if(flag==0){
flag=1; // set the DMA flag// read external current sensor
GPIOA->BSRRH = GPIO_Pin_3; // set CS Low SPI1->DR = 0x00;/// write data to be transmitted to the SPI data register while( !(SPI1->SR & SPI_I2S_FLAG_RXNE) ); // wait until receive complete while( SPI1->SR & SPI_I2S_FLAG_BSY ); // wait until SPI is not busy anymore current =SPI1->DR; // return received data from SPI data register GPIOA->BSRRL = GPIO_Pin_3; // set CS Highsprintf(Buffer,'%04d,%04d\r\n',current,rpm); // 12chars - convert uint to string
USART_DMACmd(USART3, USART_DMAReq_Tx, ENABLE); // re-enable DMA USART TX request
}
}
}
My code:
you should first
wait for TxEmpty
while( SPI1->SR & SPI_I2S_FLAG_BSY ); // wait until SPI is not busy anymore
remove any old Rx data
while( !(SPI1->SR & SPI_I2S_FLAG_RXNE) )
; // dumping old rx data here
send your byte/s
SPI1->DR = 0x00;/// write data to be transmitted to the SPI data register
wait for it to be sent totally
while( SPI1->SR & SPI_I2S_FLAG_BSY )
; // wait until SPI is not busy anymore
Your byte should be ready to read;
current =SPI1->DR; // return received data from SPI data register