Author Topic: STM32 One Pulse  (Read 8414 times)

0 Members and 1 Guest are viewing this topic.

Offline matt09Topic starter

  • Regular Contributor
  • *
  • Posts: 194
  • Country: gb
STM32 One Pulse
« on: October 10, 2018, 09:50:22 pm »
Hi all,

I'm currently trying to create a software pulse for an ADC with an stm32 device which I'm pretty new to.
I have everything working and receive data clocked via HAL_SPI_Receive, but the pulse is created via a hardware trigger and this is the only configuration I can find regarding one pulse mode.

I realise this might be a stupid question but I can't see an obvious way to change it to a soft trigger, i.e calling a function or enabling something. Everything I can find regarding n-pulses with STM32 speaks of using this mode.
There are a lot of things happening between each acquisition so I don't want to DMA data out with a repetition counter either - I want to be able to soft trigger each acquisition pulse similar to manual control of SPI nSS, but with a single call.

Cheers
« Last Edit: October 10, 2018, 11:25:14 pm by matt09 »
 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1719
  • Country: se
Re: STM32 One Pulse
« Reply #1 on: October 11, 2018, 07:30:07 am »
It might be possible to do what you're asking for.
But: what are you exactly asking for?

The overall question is (to me, at least) not completely clear.

This is what I get:
  • You have an SPI attached ADC.
  • It would seems HAL is being used.
  • nSS is SW generated. I imagine through HAL_GPIO_WritePin().

Now to the parts I'm not so confident:
  • This ADC needs, in addition to the SPI lines (CLK, MISO, MOSI, nSS) a trigger line to start conversion, the one you are caling "pulse"
  • You would like to automatically generate this trigger, and after the conversion time read the values.
  • "The pulse is created via a hardware trigger". How? What is generating the HW trigger and is that triggering the MCU or the ADC?

How off am I from your actual question?
Can you tell us something more?
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline matt09Topic starter

  • Regular Contributor
  • *
  • Posts: 194
  • Country: gb
Re: STM32 One Pulse
« Reply #2 on: October 11, 2018, 12:39:45 pm »
Hi,

Sorry if this was ambiguous, the SPI was just an example.
This is the ADC I am using
Code: [Select]
http://www.analog.com/media/en/technical-documentation/data-sheets/231116fa.pdfI am trying to generate the CNV/acquisition pulse.
I want to call a function or set a bit, etc, which internally triggers a defined pulse for the CNV pin, something similar to HAL_TIM_OC_Start()
After this I will use HAL_SPI_Receive to clock through the data and do a few other things before wanting to initiate the next pulse.

At the moment the trigger is generated though a channel on TIM1, with PWM generation on CH2 and slave mode set as trigger mode with TI1FP as trigger source and clock source set to internal. I can create what ever pulse I like using a hard trigger on associated pin but I want to replace this with a software call. Or alternatively achieve the same outcome, i.e a pulse, some other way. I am new to STM programming and am working with others unfamiliar with coding so want to use the HAL if possible.

Thanks
« Last Edit: October 11, 2018, 03:37:24 pm by matt09 »
 

Offline mark03

  • Frequent Contributor
  • **
  • Posts: 711
  • Country: us
Re: STM32 One Pulse
« Reply #3 on: October 11, 2018, 07:37:39 pm »
The standard STM32 timers have a "one pulse mode" which should do what you want.  Sorry I can't help with accessing that through the HAL though.  I only go register-direct in my projects.
 

Online Siwastaja

  • Super Contributor
  • ***
  • Posts: 8173
  • Country: fi
Re: STM32 One Pulse
« Reply #4 on: October 11, 2018, 08:41:55 pm »
The STM32 "basic" timers are very easy and straightforward (at least when compared to many other STM32 peripherals).

For this reason, they make a good first excercise, before jumping into SPI or I2C which can be a bit PITA as they have a few traps (unless the HAL function happens to do exactly what you need and you are happy with it, of course).

I think your question is an excellent first "homework" for you to do.

Download the reference manual and read up from there, see the section for the relevant timer and its register description. It has one pulse mode, it can generate the pulse, and then it can generate the interrupt for you. I guess it should be able to even generate a DMA request for you so that the SPI/whatever transaction  happens automatically after the pulse with no CPU interaction - then you'd only configure an interrupt from the DMA completion to tell you you have data back.
« Last Edit: October 11, 2018, 08:43:46 pm by Siwastaja »
 

Offline mark03

  • Frequent Contributor
  • **
  • Posts: 711
  • Country: us
Re: STM32 One Pulse
« Reply #5 on: October 12, 2018, 12:42:07 am »
As a further example of cool stuff you can do using the STM32 timers, I had a similar application with a SPI-interfaced ADC, except I needed a SPI clock higher than the maximum you can use in master mode.  On my STM32 part, slave-mode SPI allowed a faster clock, but how to supply that clock to both the ADC and the [slave] SPI peripheral in the microcontroller?  No problem:  Use the timer one-pulse mode in conjunction with its repetition counter to generate the exact number of clock pulses required for a single SPI transfer, and send that out a timer pin, looped back around to both SCLK pins.  Then set that timer to trigger off of a second timer, which also generates the CNV pulses on one of its timer pins.  Everything runs autonomously with the ADC results DMAed into a memory buffer at 2 MSPS.  Easy... at least after I got it working ;)

Edit: I see you are using a very similar ADC.  Let me know if you're interested in this solution and I'll see if I can scrounge up some code.  It doesn't touch the HAL, though.
« Last Edit: October 12, 2018, 12:46:07 am by mark03 »
 
The following users thanked this post: Siwastaja

Offline matt09Topic starter

  • Regular Contributor
  • *
  • Posts: 194
  • Country: gb
Re: STM32 One Pulse
« Reply #6 on: October 15, 2018, 12:32:03 pm »
Hi,

Thanks for the offer Mark03, I may PM you in future if that's okay when I more onto making optimising code.

I have got software pulse triggering working using "__HAL_TIM_ENABLE(&htim1)" but have a problem with the code shown below.

HAL_TIM_OnePulse_Start(&htim1, TIM_CHANNEL_1);
volatile uint16_t array [300];
HAL_Delay(1000);       // let ADC power up
      
while(1)
       {
       for(int n=0;n<300;n++)             // acquisition loop
         {
            if (hspi1.State != HAL_SPI_STATE_BUSY_RX)
            {
               __HAL_TIM_ENABLE(&htim1);    //send pulse   
               HAL_SPI_Receive(&hspi1,(uint8_t*)array[n],1,1);   //clock data
               HAL_Delay(1);
            }
         }
      }


With the HAL_Delay(1); in the loop, the pulses look fine but for some reason the HAL_SPI_Receive function isn't initiated - there are no clocks. When I remove the delay, the clock is there but the pulse output is almost inverted being high most of the time, and even runs over the receive clock?

I don't quite understand what's going on as I have the != HAL_SPI_STATE_BUSY_RX there but I don't think it's working?

Thanks
« Last Edit: October 15, 2018, 12:36:49 pm by matt09 »
 

Offline matt09Topic starter

  • Regular Contributor
  • *
  • Posts: 194
  • Country: gb
Re: STM32 One Pulse
« Reply #7 on: October 17, 2018, 10:12:31 am »
Anyone?
 

Offline matt09Topic starter

  • Regular Contributor
  • *
  • Posts: 194
  • Country: gb
Re: STM32 One Pulse
« Reply #8 on: October 21, 2018, 01:16:55 pm »
Is there something glaringly obviously wrong here I should have already noticed, or are people unsure?
 

Offline speleos

  • Newbie
  • Posts: 1
  • Country: pt
Re: STM32 One Pulse
« Reply #9 on: March 01, 2019, 03:06:02 pm »
I'm facing a similar issue. I want also to use a hardware timer to sync some ADCs but can't get the One Pulse to work - at leat on the evaluation board for STM32L4.
 

Offline mark03

  • Frequent Contributor
  • **
  • Posts: 711
  • Country: us
Re: STM32 One Pulse
« Reply #10 on: March 01, 2019, 05:01:22 pm »
Well, no one asked for this, but it seems to be an enduring question, so in case it helps, here is the timer set-up code for the scenario I described above.  If it helps wean you off of the HAL, so much the better 8)

Code: [Select]
// Timer assignments:
//   TIM1   16 SPI clock cycles on PA8 to the ADC+MCU, on each conversion; triggered by ADC BUSY on PA9
//   TIM3   ADC conversion clock on PB4, which gets re-clocked through the U1 flip-flops

// Enable clocks to TIM1/3 and GPIOA/B
RCC->AHB2ENR  |= RCC_AHB2ENR_GPIOAEN | RCC_AHB2ENR_GPIOBEN;
RCC->APB2ENR  |= RCC_APB2ENR_TIM1EN;
RCC->APB1ENR1 |= RCC_APB1ENR1_TIM3EN;


// TIM1 CONFIGURATION

// TIM1 masters the ADC SPI bus.  The MCU SPI peripheral runs in slave mode, and the LTC237x ADC
// is nominally a slave device also, so TIM1_CH1 is used to drive both the ADC and the MCU SCLK pins
// with 16 clocks on every ADC conversion.  The ADC signals a new conversion result with a falling
// edge on BUSY (TIM1_CH2, also connected to the MCU SPI slave-select input), which is used as a
// trigger to start TIM1 with its repetition counter programmed for 16 cycles.
//
// CR1: OPM=1 (one pulse mode, up-counter, counter disabled)
TIM1->CR1 = TIM_CR1_OPM;
// CR2: defaults
// SMCR: TS=110 (TI2FP2 trig source), SMS=1000 (combined reset + trigger mode)
TIM1->SMCR = TIM_SMCR_TS_1 | TIM_SMCR_TS_2 | TIM_SMCR_SMS_3;
// CCMR1: OC1M=0110 (PWM mode 1), OC1PE=1 (CCR1 register becomes buffered),
//        CC2S=01 (channel 2 is an input, mapped to TI2)
// CCMR2: defaults
TIM1->CCMR1 = TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1PE | TIM_CCMR1_CC2S_0;
// CCER: CC1P=0 (OC1 active high), CC1E=1 (OC1 output activated),
//       CC2NP=0 and CC2P=1 (channel 2 input is falling-edge sensitive)
TIM1->CCER = TIM_CCER_CC1E | TIM_CCER_CC2P;
// BDTR: MOE=1 (bypass the break/dead-time stuff; required to make TIM1/TIM8 behave like others)
TIM1->BDTR = TIM_BDTR_MOE;
// PSC = 0 (don't prescale 80-MHz input clock)
TIM1->PSC = 0;
// ARR sets the SCLK frequency
TIM1->ARR = 80 / SPI_CLOCK_MHZ - 1;
// CCR1 = (ARR+1)/2 for 50% duty cycle
TIM1->CCR1 = 40 / SPI_CLOCK_MHZ;
// RCR: 16 repetitions (SCLK periods) on each trigger
TIM1->RCR = 15;
// Generate an update event to force these values into the active registers
TIM1->EGR = TIM_EGR_UG;


// TIM3 CONFIGURATION

// TIM3 provides the LTC2378x sample clock.  The TIM3_CH1 (PB4) output is synchronized through two
// flip-flops (U1) clocked by the 16-MHz master TCXO to reduce jitter.  The MCU PLL output (80 MHz)
// driving the timer peripherals is phase-locked to the TCXO, but the phase offset is unknown.  We
// may need to adjust the TIM3_CH1 output phase to safely meet the flip-flop setup time, so we run
// the timer in "combined PWM mode" (ref man 27.3.11).
//
// CR1: defaults (up-counter, counter disabled)
// CR2: defaults
// SMCR: defaults (no slave mode)
// CCMR1: OC1M=1101 (combined PWM mode 2), OC2M=0110 (PWM mode 1),
//        OC1PE/OC2PE=1 (CCRx registers become buffered)
// CCMR2: defaults
TIM3->CCMR1 = TIM_CCMR1_OC1M_0 | TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_3
            | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2
            | TIM_CCMR1_OC1PE | TIM_CCMR1_OC2PE;
// CCER: CC1P=0 (OC1 active high), CC1E=1 (OC1 output activated)
TIM3->CCER = TIM_CCER_CC1E;
// PSC = 0 (don't prescale 80-MHz input clock)
TIM3->PSC = 0;
// ARR = 80e6 / (target sampling rate in Hz) - 1
TIM3->ARR = (80 * ADC_SAMPLE_PERIOD_NS / 1000) - 1;
// CNV pulse rising edge set by CCR1, falling edge by CCR2
// (LTC2378x datasheet says minimum pulse width = 20 ns)
TIM3->CCR1 = 80 * ADC_CNV_OFFSET_NS / 1000;   // 12.5 ns per count at 80 MHz
TIM3->CCR2 = 80 * (ADC_CNV_OFFSET_NS + ADC_CNV_WIDTH_NS) / 1000;
// Generate an update event to force these values into the active registers
TIM3->EGR = TIM_EGR_UG;


// PIN CONFIGURATION

// Pinmux:
//   PA8  (TIM1_CH1), AF1
//   PA9  (TIM1_CH2), AF1
//   PA15 (TIM2_CH1), AF1
//   PB4  (TIM3_CH1), AF2
GPIOA->AFR[1] = (GPIOA->AFR[1] & 0x0fffff00) | (1 << (8-8)*4) | (1 << (9-8)*4) | (1 << (15-8)*4);
GPIOB->AFR[0] = (GPIOB->AFR[0] & 0xfff0ffff) | (2 << 4*4);

// Set medium-speed drive level for timer output pins, except TIM2_1 low speed (reduce noise near TCXO)
GPIOA->OSPEEDR = (GPIOA->OSPEEDR & 0x3ff0ffff) | (1 << 8*2) | (1 << 9*2);
GPIOB->OSPEEDR = (GPIOB->OSPEEDR & 0xfffffcff) | (1 << 4*2);

// Set alt-function mode: PB4 (TIM3_CH1), PA8 (TIM1_1), PA9 (TIM1_2), PA15 (TIM2_1)
GPIOA->MODER = (GPIOA->MODER & 0x3ff0ffff) | (2 << 8*2) | (2 << 9*2) | (2 << 15*2);
GPIOB->MODER = (GPIOB->MODER & 0xfffffcff) | (2 << 4*2);

// Enable counters
// (TIM1 will be started by its trigger source, the TIM1_CH2 input)
TIM3->CR1 |= TIM_CR1_CEN;


(I edited out the config for a couple of other timers I was using for other tasks, but you can see some of their pins set up in the pinmux section---please ignore.)
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf