cancel
Showing results for 
Search instead for 
Did you mean: 

serial data + clock + timer + dma 17 bit

vasilius
Associate II
Posted on August 03, 2016 at 00:06

Hello

need to implement such a protocol

0690X0000060MnUQAU.gif

0690X000006037PQAQ.jpg

i need to make solution without interrupts and with DMA

the main idea is send bufer with timer

like here

0690X00000603AcQAI.jpg

but cant conigure timer

may be exist better idea than using timer or may be somebody can help to configure timer

thx

10 REPLIES 10
Posted on August 03, 2016 at 04:06

The fact it uses both clock phases, and that the data leads the clock means you need to drive the DMA with one timer channel, and then toggle another channel with delayed phase.

The alternative is to drive the clock as part of the GPIO DMA, at perhaps 4x the bit/clock rate, from a pattern buffer.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on August 03, 2016 at 12:19

I would use a timer only to generate clocks - one channel in toggle mode for the clock to the device; other in PWM mode to generate a double-frequency clock, which I would then feed externally to SCK of a SPI, which in turn in slave mode would provide the data, feeding itself through DMA.

JW
vasilius
Associate II
Posted on August 03, 2016 at 14:00

need ''mask'' clock also - bcs need only 17 clocks for each bit transfer, and with latch no need clock , need drop down line

for example clock mask in bit ''101010101010101010101010101010101010''+''0''latchbit

data any 17 bit ''101010101010101010101010101010101010''+''1''latchbit

and clock run after data

Posted on August 03, 2016 at 14:08

I did not understand your point.

You can try it in Russian if that's a language of your preference, I speak it a little bit.

JW

mark239955_stm1
Associate II
Posted on August 03, 2016 at 15:42

I'm reasonably sure that you will be able to do this using GPIO and DMA; I've used this approach in an STM32F405 to read a synchronous serial stream on both rising and falling edges.  I didn't try to write to the stream, but I see no reason why it couldn't be done.

I should add that you CAN NOT use DMA1 to do this (on an F4 part, at least) because DMA1 cannot access the entire memory space; the GPIO registers are inaccessible to it.  You can do it with DMA2 though because DMA2 can access the entire memory space; this means you have to use TIM1 or TIM8 because they're the only timers that can interact with DMA2.

vasilius
Associate II
Posted on August 03, 2016 at 19:36

cut task on two part:

1. Make Data signal

/* TIM3 init function */
void MX_TIM3_Init(void)
{
TIM_MasterConfigTypeDef sMasterConfig;
TIM_OC_InitTypeDef sConfigOC;
htim3.Instance = TIM3;
htim3.Init.Prescaler = 0;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 168;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&htim3);
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig);
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_3);
HAL_TIM_MspPostInit(&htim3);
}

void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef* htim_pwm)
{
if(htim_pwm->Instance==TIM3)
{
/* USER CODE BEGIN TIM3_MspInit 0 */
/* USER CODE END TIM3_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_TIM3_CLK_ENABLE();
/* Peripheral DMA init*/
hdma_tim3_ch3.Instance = DMA1_Stream7;
hdma_tim3_ch3.Init.Channel = DMA_CHANNEL_5;
hdma_tim3_ch3.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_tim3_ch3.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_tim3_ch3.Init.MemInc = DMA_MINC_ENABLE;
hdma_tim3_ch3.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_tim3_ch3.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_tim3_ch3.Init.Mode = DMA_NORMAL;
hdma_tim3_ch3.Init.Priority = DMA_PRIORITY_VERY_HIGH;
hdma_tim3_ch3.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
HAL_DMA_Init(&hdma_tim3_ch3);
__HAL_LINKDMA(htim_pwm,hdma[TIM_DMA_ID_CC3],hdma_tim3_ch3);
/* USER CODE BEGIN TIM3_MspInit 1 */
/* USER CODE END TIM3_MspInit 1 */
}
}

run :

uint32_t buf[4] = {84, 84, 84, 0};
HAL_TIM_PWM_Start_DMA(&htim3, TIM_CHANNEL_3, buf, 4);

0690X00000605QrQAI.png seems like ok, but i need 100% filling

uint32_t buf[4] = {168, 84, 84, 0};
HAL_TIM_PWM_Start_DMA(&htim3, TIM_CHANNEL_3, buf, 4);

i must see 3usec at HIGH 1usec LOW and 1 usec HIGH but i got : 0690X00000605L9QAI.png try to decreese value

uint32_t buf[4] = {162, 84, 84, 0};
HAL_TIM_PWM_Start_DMA(&htim3, TIM_CHANNEL_3, buf, 4);

a got 0690X00000605NtQAI.png but its also fake do :

uint32_t buf[4] = {161, 84, 84, 0};
HAL_TIM_PWM_Start_DMA(&htim3, TIM_CHANNEL_3, buf, 4);

0690X00000605R1QAI.png its looks like OK but its not 100% filled PWM How to get 100% PWM in timer DMA mode? Thx
Posted on August 04, 2016 at 13:04

I don't use HAL so can't help you with the code, but I would trigger DMA from the update rather than from the CC channel, and make sure preload is enabled for the given CC register.

JW
vasilius
Associate II
Posted on August 05, 2016 at 01:23

If i run timer in

Output Compare -> mode Toggle on match

uint32_t clk[34] = {167, 0, 167, 0, 167, 0, 167, 0, 167, 0, 
167, 0, 167, 0, 167, 0, 167, 0, 167, 0, 
167, 0, 167, 0, 167, 0, 167, 0, 167, 0, 
167, 0, 167, 169}; 
HAL_TIM_OC_Start_DMA(&htim3, TIM_CHANNEL_3, clk, 34);

In this mode cant get two HIGH impulses side by side. But for CLK its OK 0690X00000603ASQAY.jpg If i run timer in PWM mode

uint32_t data[35] = { 161, 0, 161, 0, 161, 0, 161, 0, 161, 0, 
161, 0, 161, 0, 161, 0, 161, 0, 161, 0, 
161, 0, 161, 0, 161, 0, 161, 0, 161, 0, 
161, 0, 161, 161, 0}; 
HAL_TIM_PWM_Start_DMA(&htim3, TIM_CHANNEL_4, data, 35);

i cant get 100% PWM 0690X00000603AXQAY.jpg 0690X00000603BpQAI.jpg But in PWM mode i can make two HIGH impulses - its OK for DATA Also i try to redirect DMA to

(GPIOC->BSRR) but no luck - always get

HAL_DMA_STATE_ERROR.:

uint32_t Length = 34;
htim3.State = HAL_TIM_STATE_BUSY;
htim3.hdma[TIM_DMA_ID_CC3]->XferCpltCallback = TIM_DMADelayPulseCplt;
htim3.hdma[TIM_DMA_ID_CC3]->XferErrorCallback = TIM_DMAError ;
HAL_DMA_Start_IT(htim3.hdma[TIM_DMA_ID_CC3], (uint32_t)pData1, (uint32_t)&(GPIOC->BSRR), Length);
__HAL_TIM_ENABLE_DMA(&htim3, TIM_DMA_CC3);
TIM_CCxChannelCmd(htim3.Instance, TIM_CHANNEL_3, TIM_CCx_ENABLE);
if(IS_TIM_ADVANCED_INSTANCE(htim3.Instance) != RESET) 
{
__HAL_TIM_MOE_ENABLE(&htim3);
} 
__HAL_TIM_ENABLE(&htim3);

Also have questions about synchronizing Channels in Timer. I think its not possible? Need devide CLK and Data to separate timers, one in master and CLK in slave mode? Or use third Timer to run this both? How make delay between CLK and Data?
mark239955_stm1
Associate II
Posted on August 06, 2016 at 00:13

Use a timer with multiple OC channels - you must use TIM1 or TIM8 anyway, because of the DMA issue that I've already mentioned. Do not try to use the timer to directly drive the clock signal.

e.g. If TIM1 is clocked at 100MHz, and you want to work at the maximum bus frequency of 250kHz, set PSC = 0, ARR = 400, CCR1 = 100, CCR2 = 200, CCR3 = 300.

For each transmission, construct an array of data and latch bits.  The last latch bit must be '0' and I presume that the others must be all '1'.

Data:  0b10101010101011

Latch: 0b11111111111110

Configure the TIM and DMA to:

Write your data pin to the next data bit value on TIMx update trigger.

Write your clock pin to '1' on TIMx->CH1 trigger.

Write your data pin to the next latch bit value on TIMx->CH2 trigger

Write your clock pin to '0' on TIMx->CH3 trigger

Use bitbanding to target the DMA transfers at particular bits in the data and latch arrays, and the GPIO ports.