Combining bare metal / low-level library and Arduino

Post here first, or if you can't find a relevant section!
Post Reply
STM32_Newbbe
Posts: 4
Joined: Thu Nov 05, 2020 10:26 am

Combining bare metal / low-level library and Arduino

Post by STM32_Newbbe »

Hi all,

I had that idea to use the STM32duino to add Webserver capability quite easily to my little STM32 project that relies only on the peripherals (timers & interrupts).

Turns out it won't function as intended (well, does that ever happen?).

Here's a rough description of what I want to do:
a rising edge on a pin triggers Timer2 which generates a pulse for 2ms. On the end of that pulse, another pulse is started and held until a falling edge on the input pin occurs.
The triggering should be selectable between falling and rising edge.
As a variant the second pulse might be time controlled.

What I did is work out the peripheral config with the help of CubeIDE and wrote it down completely in either LL-functions or direct register manipulation.
I then created an Arduino sketch to have my webserver functionality. Basically a simple html page that has one or two fields in form to POST values that change the times. Switching between pulse-controlled output and time-controlled output is to be done by jumper. Selecting the active edge as well.

I the pasted the initialization code into the setup of the arduino sketch.
and it did not work :(

So, before I start digging into the depth of the STM32duino cores and other sources:

Has anyone done something similar before?
What should one take care of when trying to do such a project?

Any help/ideas welcome

here's the contents main arduino sketch file

Code: Select all

#include <Ethernet.h>
#include <EEPROM.h>
#include "SystemDefines.h"
#include "SystemVariables.h"
#include "Servers.h"
#include "stm32l4xx_ll_bus.h"
#include "stm32l4xx_ll_system.h"
#include "stm32l4xx_ll_exti.h"
#include "stm32l4xx_ll_gpio.h"

void setup()
{  
  memset(cPostParameters, 0, sizeof(cPostParameters));

  Serial.begin(115200);
  
  EEPROM.get(ADRESS_PULSLAENGE, dGesamtPulsdauer);
  EEPROM.get(ADRESS_24V_ZEIT, d24V_Pulsdauer);
  dAlteGesamtPulsdauer = dGesamtPulsdauer;
  dAlte24V_Pulsdauer = d24V_Pulsdauer;
  liCounter5V = ((dGesamtPulsdauer - d24V_Pulsdauer) * 250) - 1;
  liCounter24V = (d24V_Pulsdauer * 250) - 1;
  
  Serial.print("Pulslaenge 5V: "); Serial.println((dGesamtPulsdauer), 2);
  Serial.print("Pulslaenge 24V: "); Serial.println((d24V_Pulsdauer), 2);
  
  if(ETHERNET)
  { 
    setupWebServer();
  }
// setup interrupts
  pinMode(3, OUTPUT);
  pinMode(11, OUTPUT);
  digitalWrite(3, LOW);
  pinMode(2, INPUT);  // Trigger comes as a rising slope on pin 2. External Pulldown needed, 100nF to Ground to avoid bouncing
  pinMode(7, INPUT_PULLUP);
   
  bPulseDurationIs = digitalRead(7);
  if(bPulseDurationIs == TIME_CONTROLLED)Serial.println("bPulseDurationIs = TIME_CONTROLLED");
  if(bPulseDurationIs == PULSE_CONTROLLED)Serial.println("bPulseDurationIs = PULSE_CONTROLLED");


//*-- GPIOA  -----------------------------------------------
  LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);

//  Input to trigger pulse generation
  LL_GPIO_SetPinMode(       TRIGGER_24V_GPIO_Port, TRIGGER_24V_Pin, LL_GPIO_MODE_ALTERNATE);
  LL_GPIO_SetPinSpeed(      TRIGGER_24V_GPIO_Port, TRIGGER_24V_Pin, LL_GPIO_SPEED_FREQ_VERY_HIGH);
  LL_GPIO_SetPinOutputType( TRIGGER_24V_GPIO_Port, TRIGGER_24V_Pin, LL_GPIO_OUTPUT_PUSHPULL);
  LL_GPIO_SetAFPin_0_7(     TRIGGER_24V_GPIO_Port, TRIGGER_24V_Pin, LL_GPIO_AF_2);
  LL_GPIO_SetPinPull(       TRIGGER_24V_GPIO_Port, TRIGGER_24V_Pin, LL_GPIO_PULL_NO);

//  Output to 24V BTS
  LL_GPIO_SetPinMode(       BTS_24V_GPIO_Port, BTS_24V_Pin, LL_GPIO_MODE_ALTERNATE);
  LL_GPIO_SetPinSpeed(      BTS_24V_GPIO_Port, BTS_24V_Pin, LL_GPIO_SPEED_FREQ_VERY_HIGH);
  LL_GPIO_SetPinOutputType( BTS_24V_GPIO_Port, BTS_24V_Pin, LL_GPIO_OUTPUT_PUSHPULL);
  LL_GPIO_SetPinPull(       BTS_24V_GPIO_Port, BTS_24V_Pin, LL_GPIO_PULL_UP);
  LL_GPIO_SetAFPin_0_7(     BTS_24V_GPIO_Port, BTS_24V_Pin, LL_GPIO_AF_1);

//  Output to 5V BTS
  LL_GPIO_SetPinMode(       BTS_5V_GPIO_Port, BTS_5V_Pin, LL_GPIO_MODE_OUTPUT);
  LL_GPIO_SetPinSpeed(      BTS_5V_GPIO_Port, BTS_5V_Pin, LL_GPIO_SPEED_FREQ_VERY_HIGH);
  LL_GPIO_SetPinOutputType( BTS_5V_GPIO_Port, BTS_5V_Pin, LL_GPIO_OUTPUT_PUSHPULL);
  LL_GPIO_SetPinPull(       BTS_5V_GPIO_Port, BTS_5V_Pin, LL_GPIO_PULL_UP);

//  Simulated Trigger output, uses Timer 15 to generate a 25ms pulse every 60ms
  LL_GPIO_SetPinMode(       TRIGGER_SIMUL_GPIO_Port, TRIGGER_SIMUL_Pin, LL_GPIO_MODE_ALTERNATE);
  LL_GPIO_SetPinSpeed(      TRIGGER_SIMUL_GPIO_Port, TRIGGER_SIMUL_Pin, LL_GPIO_SPEED_FREQ_VERY_HIGH);
  LL_GPIO_SetPinOutputType( TRIGGER_SIMUL_GPIO_Port, TRIGGER_SIMUL_Pin, LL_GPIO_OUTPUT_PUSHPULL);
  LL_GPIO_SetPinPull(       TRIGGER_SIMUL_GPIO_Port, TRIGGER_SIMUL_Pin, LL_GPIO_PULL_UP);
  LL_GPIO_SetAFPin_0_7(     TRIGGER_SIMUL_GPIO_Port, TRIGGER_SIMUL_Pin, LL_GPIO_AF_14);

//---------------------------------------------------------------------------*/

//*-- Timer 2 -----------------------------------------------
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);

  NVIC_SetPriority(TIM2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
  NVIC_EnableIRQ(TIM2_IRQn);

  TIM2->CR1   |= TIM_CR1_ARPE;                  //  enable auto-reload buffer
  TIM2->CCMR1 |= TIM_CCMR1_OC2PE;               //  enable preload register (buffered)
  TIM2->CCMR1 |= TIM_CCMR1_OC2M_1;              //  set PWM mode 1 (togehter with next instruction)
  TIM2->CCMR1 |= TIM_CCMR1_OC2M_2;              //  set PWM mode 1 (togehter with previous instruction)
  TIM2->CCR2   = 1;                             //  set compare value to 1 for a minmal pulse, will be overridden by fast mode
  TIM2->CCER  |= TIM_CCER_CC2P;                 //  output of channel 2 is active low
  TIM2->CCMR1 |= TIM_CCMR1_OC2FE;               //  enable fast mode
  TIM2->CR1   |= TIM_CR1_OPM;                   //  select one-pulse mode
  TIM2->SMCR  |= LL_TIM_SLAVEMODE_TRIGGER;      //  Timer is enabled in Trigger mode
  TIM2->SMCR  |= LL_TIM_TS_ETRF;                //  Trigger input is external pin (filtered)
  TIM2->SMCR  |= LL_TIM_ETR_FILTER_FDIV1_N2;    //  Filter external Trigger for 2 cycles
//  TIM2->SMCR  |= LL_TIM_ETR_FILTER_FDIV1_N4;  //  Filter external Trigger for 4 cycles
//  TIM2->SMCR  |= LL_TIM_ETR_FILTER_FDIV1_N8;  //  Filter external Trigger for 8 cycles
  TIM2->CCER  |= TIM_CCER_CC2E;                 //  output of channel 2 is enabled
  TIM2->BDTR  |= TIM_BDTR_MOE;                  //  enable outputs for this Timer
  TIM2->CNT    = 0;                             //  Reset Count register to 0
  TIM2->PSC    = 79;                            //  set prescaler in order to get 1µs clocks
  TIM2->ARR    = 1999;                          //  we use complete period as high-time, so set it do desired duration of 2ms
  TIM2->EGR   |= TIM_EGR_UG;                    //  generate update event to initialize all preloaded registers
  while((!LL_TIM_IsActiveFlag_UPDATE(TIM2))){}; //  wait fpr interrupt to have occured
  LL_TIM_ClearFlag_UPDATE(TIM2);                //  clear flag
  TIM2->DIER  |= TIM_DIER_UIE;                  //  enable interrupt on channel 2 Update
  TIM2->DIER  |= TIM_DIER_TIE;                  //  enable interrupt on channel 2 Trigger
  TIM2->CR1   |= TIM_CR1_CEN;                   //  enable this Timer

//---------------------------------------------------------------------------*/

//*--  EXTI Line5  ------------------------------------------------------------------------------
  LL_SYSCFG_SetEXTISource(LL_SYSCFG_EXTI_PORTA, LL_SYSCFG_EXTI_LINE5);
  LL_EXTI_EnableIT_0_31(LL_EXTI_LINE_5);
  LL_EXTI_DisableEvent_0_31(LL_EXTI_LINE_5);
  LL_EXTI_DisableRisingTrig_0_31(LL_EXTI_LINE_5);
  LL_EXTI_EnableFallingTrig_0_31(LL_EXTI_LINE_5);
  NVIC_SetPriority(EXTI9_5_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
  NVIC_EnableIRQ(EXTI9_5_IRQn);
//---------------------------------------------------------------------------------*/


//*-- Timer 15  -----------------------------------------
//  enable this on in last place, since it is the one that generates the simulated trigger
  LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM15);

  TIM15->CR1  |= TIM_CR1_ARPE;                  //  enable auto-reload buffer
  TIM15->CCMR1 |= TIM_CCMR1_OC2PE;              //  enable preload register (buffered)
  TIM15->CCMR1 |= TIM_CCMR1_OC2M_1;             //  set PWM mode 1 (togehter with next instruction)
  TIM15->CCMR1 |= TIM_CCMR1_OC2M_2;             //  set PWM mode 1 (togehter with previous instruction)
  TIM15->CCER  |= TIM_CCER_CC2E;                //  output of channel 2 is enabled
  TIM15->BDTR  |= TIM_BDTR_MOE;                 //  enable outputs for this Timer
  TIM15->CNT    = 0;                            //  Reset Count register to 0
  TIM15->PSC    = 79;                           //  set prescaler in order to get 1µs clocks
  TIM15->ARR    = 59999;                        //  we want a period of 60ms
  TIM15->CCR2   = 14999;                        //  and a puls of 25ms
  TIM15->EGR    |= TIM_EGR_UG;                  //  generate update event to initialize all preloaded registers
  while((!LL_TIM_IsActiveFlag_UPDATE(TIM15))){};//  wait fpr interrupt to have occured
  LL_TIM_ClearFlag_UPDATE(TIM15);               //  clear flag
  TIM15->CR1   |= TIM_CR1_CEN;                  //  enable this Timer

//--------------------------------------------------*/

}

void loop() 
{
  delay(100);
  handleWebRequest();
}


void EXTI9_5_IRQHandler(void)
{
//  second edge has been detected, reset 5V BTS and LED
  if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_5))
  {
    LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_5);
    LL_GPIO_ResetOutputPin(BTS_5V_GPIO_Port, BTS_5V_Pin);
    LL_GPIO_ResetOutputPin(LED_GPIO_Port, LED_Pin);
  }
}


void TIM2_IRQHandler(void)
{

//  Update event occured, this means the ms have passed, set 5V BTS
  if(LL_TIM_IsActiveFlag_UPDATE(TIM2))
  {
    LL_TIM_ClearFlag_UPDATE(TIM2);
    LL_GPIO_SetOutputPin(BTS_5V_GPIO_Port, BTS_5V_Pin);
  }

//* TIM2 got triggered, set the LED
  if(LL_TIM_IsActiveFlag_TRIG(TIM2))
  {
    LL_TIM_ClearFlag_TRIG(TIM2);
    LL_GPIO_SetOutputPin(LED_GPIO_Port, LED_Pin);
  }
//-----------------------------*/
}

User avatar
fpiSTM
Posts: 851
Joined: Wed Dec 11, 2019 7:11 pm
Answers: 33
Location: Le Mans
Contact:

Re: Combining bare metal / low-level library and Arduino

Post by fpiSTM »

You have to disable the Arduino High level which use the HAL Timer module.
See https://github.com/stm32duino/wiki/wiki ... odule-only

STM32_Newbbe
Posts: 4
Joined: Thu Nov 05, 2020 10:26 am

Re: Combining bare metal / low-level library and Arduino

Post by STM32_Newbbe »

Hi fpi,

that brought me one step further! Thanks a lot :)

at least TIM15 is now working.

I also can initialize TIM2, but the UPDATE-interrupt does not get serviced
And, if I activate the TRIGGER-interrupt, the system halts at first rising edge of TIM15 output, i.e. the trigger event for TIM2

I guess that somewhere in the depths of the Arduino core these interrupts are used?

User avatar
fpiSTM
Posts: 851
Joined: Wed Dec 11, 2019 7:11 pm
Answers: 33
Location: Le Mans
Contact:

Re: Combining bare metal / low-level library and Arduino

Post by fpiSTM »

Sketch ino file is converted to cpp. So handler have to be prefixed by: extern "C"

STM32_Newbbe
Posts: 4
Joined: Thu Nov 05, 2020 10:26 am

Re: Combining bare metal / low-level library and Arduino

Post by STM32_Newbbe »

thanks again!

so much to learn...

STM32_Newbbe
Posts: 4
Joined: Thu Nov 05, 2020 10:26 am

Re: Combining bare metal / low-level library and Arduino

Post by STM32_Newbbe »

now the compiler complaints about TIM2_IRQHandler and EXTI9_5_IRQHandler already being defined :(

I added

Code: Select all

  #undef HAL_TIM_MODULE_ENABLED
  #define HAL_TIM_MODULE_DISABLED
  #undef HAL_EXTI_MODULE_ENABLED
  #define HAL_EXTI_MODULE_DISABLED
no change

User avatar
fpiSTM
Posts: 851
Joined: Wed Dec 11, 2019 7:11 pm
Answers: 33
Location: Le Mans
Contact:

Re: Combining bare metal / low-level library and Arduino

Post by fpiSTM »

You should not disabled the HAL but use the ****_MODULE_ONLY definition

Post Reply

Return to “General discussion”