How to genaraite ppm in stm32f1

Post here first, or if you can't find a relevant section!
aml@stm32
Posts: 3
Joined: Mon Mar 23, 2020 8:25 pm

How to genaraite ppm in stm32f1

Post by aml@stm32 »

can any one help me to write a simple code to genaraite ppm signal on stm32 i had only found codes for atmega and esp null for stm
i am geting trouble at porting it (dont have good idia about timer and counter)
ag123
Posts: 1655
Joined: Thu Dec 19, 2019 5:30 am
Answers: 24

Re: How to genaraite ppm in stm32f1

Post by ag123 »

aml@stm32
Posts: 3
Joined: Mon Mar 23, 2020 8:25 pm

Re: How to genaraite ppm in stm32f1

Post by aml@stm32 »

thanks , but that project convert ppm to serial i need the invarce

esp code for ppm /// it works

Code: Select all

#define CPU_MHZ 80
#define CHANNEL_NUMBER 8  //set the number of chanels
#define CHANNEL_DEFAULT_VALUE 1100  //set the default servo value
#define FRAME_LENGTH 22500  //set the PPM frame length in microseconds (1ms = 1000µs)
#define PULSE_LENGTH 300  //set the pulse length
#define onState 0  //set polarity of the pulses: 1 is positive, 0 is negative
#define sigPin 5 //set PPM signal output pin on the arduino
#define DEBUGPIN 4
volatile unsigned long next;
volatile unsigned int ppm_running=1;
int ppm[CHANNEL_NUMBER];


   void inline ppmISR(void){
  static boolean state = true;
  if (state) {  //start pulse
    digitalWrite(sigPin, onState);
    next = next + (PULSE_LENGTH * CPU_MHZ);
    state = false;
    alivecount++;
  }
  else{  //end pulse and calculate when to start the next pulse
    static byte cur_chan_numb;
    static unsigned int calc_rest;
    digitalWrite(sigPin, !onState);
    state = true;
  if(cur_chan_numb >= CHANNEL_NUMBER){
      cur_chan_numb = 0;
      calc_rest = calc_rest + PULSE_LENGTH;//
      next = next + ((FRAME_LENGTH - calc_rest) * CPU_MHZ);
      calc_rest = 0;
      digitalWrite(DEBUGPIN, !digitalRead(DEBUGPIN));
    }
    else{
      next = next + ((ppm[cur_chan_numb] - PULSE_LENGTH) * CPU_MHZ);
      calc_rest = calc_rest + ppm[cur_chan_numb];
      cur_chan_numb++;
    }
  }
  timer0_write(next);
}

void setup() {
   pinMode(sigPin,OUTPUT);
   digitalWrite(sigPin, !onState);
   noInterrupts();
   ppm_running=0;
   timer0_isr_init();
   timer0_attachInterrupt(ppmISR);
   next=ESP.getCycleCount()+1000;
   timer0_write(next);
   interrupts();
}void loop() {}
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//ported code for stm32 ppm signal genaraits but incorrect :?: dont know why
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Code: Select all

#include <Arduino.h>


#define CPU_MHZ 72
#define CHANNEL_NUMBER 8  //set the number of chanels
#define CHANNEL_DEFAULT_VALUE 1100  //set the default servo value
#define FRAME_LENGTH 22500  //set the PPM frame length in microseconds (1ms = 1000µs)
#define PULSE_LENGTH 300  //set the pulse length
#define onState 0  //set polarity of the pulses: 1 is positive, 0 is negative
#define sigPin PB12 //set PPM signal output pin on the arduino

volatile unsigned long next;
volatile unsigned int ppm_running=1;
int ppm[CHANNEL_NUMBER];
int alivecount ;

HardwareTimer timer(2);



   void inline ppmISR(void){
  static boolean state = true;
  if (state) {  //start pulse
    digitalWrite(sigPin, onState);
    next = next + (PULSE_LENGTH * CPU_MHZ);
    state = false;

  }
  else{  //end pulse and calculate when to start the next pulse
    static byte cur_chan_numb;
    static unsigned int calc_rest;
    digitalWrite(sigPin, !onState);
    state = true;
  if(cur_chan_numb >= CHANNEL_NUMBER){
      cur_chan_numb = 0;
      calc_rest = calc_rest + PULSE_LENGTH;//
      next = next + ((FRAME_LENGTH - calc_rest) * CPU_MHZ);
      calc_rest = 0;

    }
    else{
      next = next + ((ppm[cur_chan_numb] - PULSE_LENGTH) * CPU_MHZ);
      calc_rest = calc_rest + ppm[cur_chan_numb];
      cur_chan_numb++;
    }
  }
  timer.setCount(next);
}



void setup() {
 pinMode(PC13,OUTPUT);
  pinMode(PB12,OUTPUT);
  timer.pause();
  timer.setChannel1Mode(TIMER_OUTPUT_COMPARE);
  timer.setCompare(TIMER_CH1, 1);
  timer.attachCompare1Interrupt(ppmISR);
  timer.refresh();
  next=timer.getCount()+1000;
  timer.setCount(next);
  ppm[0]=1000;
        ppm[1]=1444;
        ppm[2]=1444;
        ppm[3]=1444;
        ppm[4]=1000;
        ppm[5]=1000;
        ppm[6]=1000;
        ppm[7]=1000;

  timer.resume();

}
void loop() {
 digitalWrite(PC13,HIGH);
 delay(500);
 digitalWrite(PC13,LOW);
 delay(500);

}
aml@stm32
Posts: 3
Joined: Mon Mar 23, 2020 8:25 pm

Re: How to genaraite ppm in stm32f1

Post by aml@stm32 »

working ported code for stm32f1 (the blue pill) :mrgreen:

Code: Select all

  #include <Arduino.h>

#define CPU_MHZ 72
#define CHANNEL_NUMBER 8  //set the number of chanels
#define CHANNEL_DEFAULT_VALUE 1100  //set the default servo value
#define FRAME_LENGTH 22500  //set the PPM frame length in microseconds (1ms = 1000µs)
#define PULSE_LENGTH 300  //set the pulse length
#define onState 0  //set polarity of the pulses: 1 is positive, 0 is negative
#define sigPin PB12 //set PPM signal output pin on the arduino

volatile unsigned long next;
volatile unsigned int ppm_running=1;
int ppm[CHANNEL_NUMBER];
int alivecount ;

HardwareTimer timer(2);

   void inline ppmISR(void){
   static boolean state = true;
   if (state) {  //start pulse
    digitalWrite(sigPin, onState);
    next = next + PULSE_LENGTH ;
    state = false;

  }
    else{  //end pulse and calculate when to start the next pulse
    static byte cur_chan_numb;
    static unsigned int calc_rest;
    digitalWrite(sigPin, !onState);
    state = true;
    if(cur_chan_numb >= CHANNEL_NUMBER){
      cur_chan_numb = 0;
      calc_rest = calc_rest + PULSE_LENGTH;
      next = next + (FRAME_LENGTH - calc_rest) ;
      calc_rest = 0;

    }
    else{
      next = next + (ppm[cur_chan_numb] - PULSE_LENGTH) ;
      calc_rest = calc_rest + ppm[cur_chan_numb];
      cur_chan_numb++;
    }
  }
timer.setCompare(TIMER_CH1, next);
}


void setup() {
  pinMode(PC13,OUTPUT);
  pinMode(PB13,OUTPUT);
  pinMode(PB12,OUTPUT);
  timer.pause()  ;
  timer.setPrescaleFactor(72);
  timer.setChannel1Mode(TIMER_OUTPUT_COMPARE);
  next=timer.getCount()+10;
  timer.setCompare(TIMER_CH1,next);
  timer.attachCompare1Interrupt(ppmISR);
  timer.refresh();
        ppm[0]=1000;
        ppm[1]=1444;
        ppm[2]=1444;
        ppm[3]=1444;
        ppm[4]=1444;
        ppm[5]=1000;
        ppm[6]=1000;
        ppm[7]=1000;

  timer.resume();
}

  void loop() {
 digitalWrite(PC13,LOW);
 delay(500);
 digitalWrite(PC13,HIGH);
 delay(600);     
}
Artur
Posts: 2
Joined: Sat Jun 19, 2021 9:10 am

Re: How to genaraite ppm in stm32f1

Post by Artur »

Can somebody port it to PlatformIO? I would be very thankful.
dannyf
Posts: 447
Joined: Sat Jul 04, 2020 7:46 pm

Re: How to genaraite ppm in stm32f1

Post by dannyf »

you probably want to understand your requirements and the various approaches first.

a few ways I can think of:
1. use a hardware pwm - likely you will need a 32-bit timer;
2. use a timer isr;
3. use a compare-math isr;
4. simple counting in the main loop.

each has their own pluses and minuses. the esp approach is #2 above -> a little bit on the complex side.

here is a short one - untested.

Code: Select all

//user isr, called once every second
//generate 1ppm, pulse width = 1sec
#define PPM_PR	60	//1ppm period, in sec
#define PPM_DR	1	//ppm high duration, in sec
void ppm_isr(void) {
  static char cnt=PPM_PR;
  
  if (cnt--==0) {digitalWrite(PPM_PIN, HIGH); cnt=PPM_PR;}  //cnt decremented to 0, generate a pulse
  if (cnt   ==PPM_PR-PPM_DR) digitalWrite(PPM_PIN, LOW); //reset ppm pin
}
install that isr and you are set.
ag123
Posts: 1655
Joined: Thu Dec 19, 2019 5:30 am
Answers: 24

Re: How to genaraite ppm in stm32f1

Post by ag123 »

Artur wrote: Sat Jun 19, 2021 9:18 am Can somebody port it to PlatformIO? I would be very thankful.
i'd guess it is about this
viewtopic.php?p=7582#p7582

there are some faqs which may be relevant
viewtopic.php?f=2&t=3
viewtopic.php?f=2&t=301


i read a little into RC stuff here
https://www.dialog-semiconductor.com/si ... lation.pdf
https://ardupilot.org/copter/docs/commo ... coder.html

for RC stuff, if one have flexibility with both the transmitter and receiver, using things like nrf24l01 or cc2500 radios are probably easier.
those can transmit and receive data as bytes rather than having to convert to pwm, ppm etc.

however if the 'protocol' is that specified in the dialog semi app note, it is basically a high pulse of a fixed duration, followed by a delay until the next high pulse. that's converting from 'pwm' of 'high' pulses.

if you understand how h/w timers work, an 'easy' way to generate those 'ppm',
is to set the duration of the pulse (both the high + low time) as well as the 'duty cycle' of the pulse, the width of the high pulse
at the start of a timer (update) event interrupt,
this will vary the duration of the 'ppm' pulse for each pulse. and you'd need to keep state (e.g. count the pulses, set durations for each pulse, and stop at the end of 8th pulse). that would work straight out as a 'ppm encoder', without needing a separate h/w to convert from 'pwm' pulses to 'ppm' pulses.
that said, i'd think nrf24l01 and cc2500 are probably 'easier' and more reliable. that '8 channels' can be simply represented as a sequence of 8 bytes sent over a serial protocol (e.g. spi)
dannyf
Posts: 447
Joined: Sat Jul 04, 2020 7:46 pm

Re: How to genaraite ppm in stm32f1

Post by dannyf »

that's an interesting protocol - i had mistakenly thought ppm is pulse per minute, :)

it can be done via software - I wrote a driver for multiple servos and it can be repurposed here.

in the stm32f1 world, it can be done via hardware:
1. a slave timer to generate a single pulse of 200us wide. it is to be trigger by a master;
2. the master generates a single pulse of variable length. this piece is very much like software uart transmission.
ag123
Posts: 1655
Joined: Thu Dec 19, 2019 5:30 am
Answers: 24

Re: How to genaraite ppm in stm32f1

Post by ag123 »

i think this is a little similar to driving those neopixels ws2812b led strips. ws2812b has a 'protocol' of varying 'pwm' periods. hence, it couldn't be done by simply fix the period and change the duty cycle. the period itself needs to be changed for every subsequent led command. just that ws2812b has much smaller periods. i'm not too sure if timer interrupt isr is adequate to catch up with ws2812b.
dannyf
Posts: 447
Joined: Sat Jul 04, 2020 7:46 pm

Re: How to genaraite ppm in stm32f1

Post by dannyf »

i throw this together as a proof of concept - it is done on a pic24 but the same structure applies:

Code: Select all

	//output compare
	oc5Init(1000); oc5AttachISR(ppm_gen);	//generate 8 pulses
	oc2Init(1000); oc2AttachISR(ppm_200us);	//each pulse 200us long
two output compare modules are used, one to trigger pulse generation, and another to generate 200us (200ticks) pulse.

OC5 is essentially the #2 I mentioned below, and OC2 #1.

Code: Select all

//ppm generator, 8-ch
//called via output compare
#define PPM_CH		8					//number of channels
#define PPM_RESET	10000				//duration for trailing reset
#define PPM_PR		200					//ppm pulse period
uint16_t ppm_dr[PPM_CH+1]={
	800, 1600, 800, 400, 800, 1200, 600, 800,	//ch duration
	PPM_RESET							//trailing blank
};
//for oc5
void ppm_gen(void) {
	static char ppm_idx=0;				//ppm index
	
	OC5R += ppm_dr[ppm_idx++];			//advance to the next match point (for the next pulse)
	if (ppm_idx == PPM_CH+1) {			//send the blank
		ppm_idx=0;						//reset the index is all pulses have been generated
	} else {
		OC2R = OC_TIMEBASE()+ PPM_PR;	//200 ticks on duration
		digitalWrite(LED, HIGH); 		//trigger the slave
	}		
}

//for oc2 - 200 tick pulse generation
void ppm_200us(void) {
	digitalWrite(LED, LOW);				//turn off the pulse
}
OC5 ISR is hooked up to ppm_gen() and it manages through the 8 pulses. OC2 ISR is hooked up to ppm_200us() and it generates individual pulses, each of 200-ticks duration. all of this is done in the backgroud and the user needs to fill in the right values in ppm_dr[]

the OCxR registers are the equivalent of TIMx_CCRn registers on a stm32f chip.
Post Reply

Return to “General discussion”