Skip to content

Commit

Permalink
Fixed third PWM channel
Browse files Browse the repository at this point in the history
  • Loading branch information
Laurence authored and Laurence committed May 23, 2013
1 parent 069af76 commit d45aee5
Show file tree
Hide file tree
Showing 7 changed files with 395 additions and 11 deletions.
127 changes: 127 additions & 0 deletions Sensors/ppg_new.c~
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include "ppg.h"
#include "../adc.h"
#include "../Util/buffer.h"
#include "../main.h"
#include "../timer.h"

volatile float Last_PPG_Values[PPG_CHANNELS];

//This should really be done with macros, but 64(decimate)*21(decimate)*14(adc clks)*6(adc clkdiv)=112896
// == 336*336 - so one change in the pwm reload value will give an orthogonal frequency to the baseband decimator

/**
* @brief Run the baseband LO on the quadrature samples, then integrate and dump the baseband into indivdual LED bins
* @param Pointer to the output data buffer
* @retval None
* This will be called at 744.048Hz
*/
void PPG_LO_Filter(volatile uint16_t* buff) {
static uint8_t bindex; //Baseband decimation index
static int32_t Frequency_Bin[PPG_CHANNELS][2];//All Frequencies in use - consisting of and I and Q component
static uint32_t Fudgemask;
static const int8_t sinusoid[72]=STEP_SIN,cosinusoid[72]=STEP_COS;//Lookup tables
int32_t I=0,Q=0,a; //I and Q integration bins, general purpose variables
//Tryfudge(&Fudgemask); //Try to correct timer phase here
for(uint16_t n=0;n<ADC_BUFF_SIZE/4;) { //buffer size/4 must be a multiple of 4
for(uint8_t m=0;m<72;m++,n++) { //Loop through the 72 sample lookup
I+=(int16_t)buff[n]*(int16_t)cosinusoid[m];
Q+=(int16_t)buff[n]*(int16_t)sinusoid[m];
}
}
//Now run the "baseband" decimating filter(s)
//Negative frequencie(s) go here, need to get to 0hz, so multiply bin by a +ive complex exponential
#if PPG_CHANNELS>=3
a=Frequency_Bin[0][0];
Frequency_Bin[0][0]=Frequency_Bin[2][0]*7-Frequency_Bin[2][1]*4;//Rotate the phasor in the bin - real here (~30degree rotation)
Frequency_Bin[0][1]=Frequency_Bin[2][1]*7+a*4;//complex here
Frequency_Bin[0][1]>>=3;Frequency_Bin[2][0]>>=3;//divide by 8
#endif
//Zero frequency - i.e. directly on quadrature
//nothing to do to this bin
//Positive frequency
#if PPG_CHANNELS>=2
a=Frequency_Bin[2][0];
Frequency_Bin[2][0]=Frequency_Bin[0][0]*7+Frequency_Bin[0][1]*4;//Rotate the phasor in the bin - real here (~-30degree rotation)
Frequency_Bin[2][1]=Frequency_Bin[0][1]*7-a*4;//complex here
Frequency_Bin[2][1]>>=3;Frequency_Bin[0][0]>>=3;//divide by 8
#endif
#if PPG_CHANNELS>3 || !PPG_CHANNELS
#error "Unsupported number of channels - decoder error"
#endif
//Add the I and Q directly into the bins
for(uint8_t n=0;n<PPG_CHANNELS;n++) {
Frequency_Bin[n][0]+=I;Frequency_Bin[n][1]+=Q;//I,Q is real,imaginary
}
//End of decimating filters
if(++bindex==PPG_NO_SUBSAMPLES) { //Decimation factor of 12 - 62.004Hz data output
for(uint8_t n=0;n<PPG_CHANNELS;n++) {
Last_PPG_Values[n]=sqrtf(((float)Frequency_Bin[n][0]*(float)Frequency_Bin[n][0])+((float)Frequency_Bin[n][1]*(float)Frequency_Bin[n][1]));
Add_To_Buffer(((uint32_t)Last_PPG_Values[n])>>8,&(Buff[n]));//There will always be at least 8 bits on noise, so shift out the mess
} //fill the array of buffers
memset(Frequency_Bin,0,sizeof(Frequency_Bin));//Zero everything
bindex=0; //Reset this
//Fudgemask|=FUDGE_ALL_TIMERS; //Sets a TIMx fudges as requested
}
}

/**
* @brief Corrects PWM values to get the ADC input in the correct range
* @param none
* @retval none
*/
void PPG_Automatic_Brightness_Control(void) {
uint8_t channel;
uint16_t vals[3]={0,0,0};
uint16_t old_vals[3]; //This function iterates until the PWM duty correction falls below a limit set in header
do {
memcpy(old_vals,vals,sizeof(old_vals));//Copy over to the old values
for(channel=0;channel<PPG_CHANNELS;channel++) { //Loop through the channels
uint16_t pwm=Get_PWM_0();
if(channel==1)
pwm=Get_PWM_1();
else if(channel==2)
pwm=Get_PWM_2();//Retreives the set pwm for this channel
vals[channel]=PPG_correct_brightness((uint32_t)Last_PPG_Values[channel], pwm);
}
//Apply the pwm duty correction here
Set_PWM_2(vals[2]);
Set_PWM_1(vals[1]);
Set_PWM_0(vals[0]);
uint32_t time=Millis; //Store the system time here
time+=(uint32_t)(4000.0/PPG_SAMPLE_RATE);//four samples
while(Millis<time); //Delay for a couple of PPG samples to set the analogue stabilise
}while((abs(vals[0]-old_vals[0])>old_vals[0]/PWM_STEP_LIM)||(abs(vals[1]-old_vals[1])>old_vals[1]/PWM_STEP_LIM)||\
(abs(vals[2]-old_vals[2])>old_vals[2]/PWM_STEP_LIM));
}




/**
* @brief Output a corrected PWM value to get the ADC input in the correct range
* @param Output sample from the decimator, present PWM duty cycle value
* @retval A new corrected duty cycle value
* This will be called from the main code between pressure applications and timed refills
* If more leds are added at different pwm frequencies, then we need to take the sum of Decimated values and scale
* To avoid clipping of the frontend
*/
uint16_t PPG_correct_brightness(uint32_t Decimated_value, uint16_t PWM_value) {
//2^adc_bits*samples_in_half_buffer/4*baseband_decimator
//(2^12)*(64/4)*21 == 1376256 == 2*target_decimated_value TODO impliment this with macros full - atm just TARGET_ADC
float corrected_pwm=PWM_Linear(PWM_value);
corrected_pwm*=(float)(TARGET_ADC)/(float)Decimated_value;//This is the linearised pwm value required to get target amplitude
corrected_pwm=(corrected_pwm>1.0)?1.0:corrected_pwm;//Enforce limit on range to 100%
return ((asinf(corrected_pwm)/M_PI)*PWM_PERIOD_CENTER);//Convert back to a PWM period value
}

/**
* @brief Output a linearised value in range 0 to 1 from a PWM duty cycle
* @param PWM duty cycle
* @retval A linearised value as a float in the range 0 to 1
*/
float PWM_Linear(uint16_t PWM_value) {
return sinf(((float)PWM_value/(float)PWM_PERIOD_CENTER)*M_PI);//returns the effecive sinusoidal amplitude in range 0-1
}
Binary file modified main.bin
Binary file not shown.
Binary file modified main.elf
Binary file not shown.
8 changes: 4 additions & 4 deletions timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ void setup_pwm(void) {
TIM_OCInitStructure.TIM_Pulse = 4;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

/* Time base configuration - timer 4 as PWM0/2*/
/*Time base configuration - timer 4 as PWM0/2*/
#if BOARD<3
TIM_TimeBaseStructure.TIM_Period = PWM_PERIOD_ZERO;
TIM_TimeBaseStructure.TIM_Period = PWM_PERIOD_ZERO; //PWM0 on early boards
#else
TIM_TimeBaseStructure.TIM_Period = PWM_PERIOD_TWO; //PWM2 on revision 3 boards
TIM_TimeBaseStructure.TIM_Period = PWM_PERIOD_TWO; //PWM2 on later boards
#endif
/*setup 4*/
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
Expand All @@ -84,7 +84,7 @@ void setup_pwm(void) {
TIM_ARRPreloadConfig(TIM4, ENABLE);


/*Now setup timer2 as PWM1/0*/
/*Now setup timer 2 as PWM1/0*/
#if BOARD<3
TIM_TimeBaseStructure.TIM_Period = PWM_PERIOD_ONE;
#else
Expand Down
219 changes: 219 additions & 0 deletions timer.c~
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
#include "timer.h"
#include "gpio.h"
#include "Sensors/ppg.h"

/**
* @brief Configure the timer channels for PWM out on CRT board
* @param None
* @retval None
* This initialiser function assumes the clocks and gpio have been configured
*/
void setup_pwm(void) {
/* -----------------------------------------------------------------------
TIM Configuration: generate 2/3 PWM signals with different duty cycles:
The TIMxCLK frequency is set to SystemCoreClock (Hz), to get TIMx counter
clock at 72 MHz the Prescaler is computed as following:
- Prescaler = (TIMxCLK / TIMx counter clock) - 1
SystemCoreClock is set to 72 MHz

The TIM3 is running at 11.905KHz: TIM3 Frequency = TIM4 counter clock/(ARR + 1)
= 72 MHz / 6047
(with 239.5clk adc sampling -> 252adc clk/sample, and 12mhz adc clk this gives quadrature
sampling)

----------------------------------------------------------------------- */
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure={};
TIM_OCInitTypeDef TIM_OCInitStructure={};
GPIO_InitTypeDef GPIO_InitStructure;
/*Enable the Tim2 clk*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/*Enable the Tim4 clk*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
/*Enable the Tim1 clk*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
#if BOARD>=3
/*Enable the Tim3 clk*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
#endif
//Setup the GPIO pins
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;//reduced slew rate to reduce interference on the board
GPIO_PinRemapConfig( GPIO_FullRemap_TIM2, ENABLE );//to B.10
#if BOARD<3
GPIO_InitStructure.GPIO_Pin = PWM0|PWM1|PWM2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init( GPIOB, &GPIO_InitStructure );
#else
GPIO_InitStructure.GPIO_Pin = PWM0|PWM2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init( GPIOB, &GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = PWM1;
GPIO_Init( GPIOA, &GPIO_InitStructure );
#endif

TIM_DeInit(TIM1);
TIM_DeInit(TIM2);
#if BOARD>=3
TIM_DeInit(TIM3);
#endif
TIM_DeInit(TIM4);
/*Setup the initstructure*/
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 4;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

/*Time base configuration - timer 4 as PWM2/0*/
#if BOARD<3
TIM_TimeBaseStructure.TIM_Period = PWM_PERIOD_TWO; //PWM2 on early boards
#else
TIM_TimeBaseStructure.TIM_Period = PWM_PERIOD_ZERO; //PWM0 on later boards
#endif
/*setup 4*/
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
#if BOARD<3
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //Mode 1 on early board revisions
/* 'PWM1' Mode configuration: Channel3 */
TIM_OC3Init(TIM4, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable);
#else
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //As Board revisions >=3 using P channel mosfet drivers, so inverted levels
#endif
/* PWM1 Mode configuration: Channel4 */
TIM_OC4Init(TIM4, &TIM_OCInitStructure);
TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable);
/* TIM4 enable preload */
TIM_ARRPreloadConfig(TIM4, ENABLE);


/*Now setup timer 2 as PWM1/2*/
#if BOARD<3
TIM_TimeBaseStructure.TIM_Period = PWM_PERIOD_ONE;
#else
TIM_TimeBaseStructure.TIM_Period = PWM_PERIOD_TWO;
#endif
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //Same as timer4
/* PWM1 Mode configuration: Channel3 */
TIM_OC3Init(TIM2, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);
/* TIM2 enable preload */
TIM_ARRPreloadConfig(TIM2, ENABLE);


#if BOARD>=3
/*Now setup timer3 as PWM1*/
TIM_TimeBaseStructure.TIM_Period = PWM_PERIOD_ONE;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //Same as timer4
/* PWM1 Mode configuration: Channel1 */
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
/* TIM3 enable preload */
TIM_ARRPreloadConfig(TIM3, ENABLE);
#endif

/*Now we setup the master/slave to orthogonalise the timers*/
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Disable;//No signal output to pins
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //Normal PWM mode for gating
#if BOARD>=3
//Timers2 and 4 are slaved off timer3
TIM_SelectMasterSlaveMode(TIM3,TIM_MasterSlaveMode_Enable);//This is purely a syncronisation option applied to the master
TIM_OCInitStructure.TIM_Pulse = GATING_PERIOD_ZERO; //Macros used to define these
TIM_SelectOutputTrigger(TIM3,TIM_TRGOSource_OC2Ref);
TIM_OC2Init(TIM3, &TIM_OCInitStructure); //Tim3,ch2 used for gating
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
TIM_SelectMasterSlaveMode(TIM2,TIM_MasterSlaveMode_Enable);//This is purely a syncronisation option applied to the master
TIM_SelectInputTrigger(TIM2,TIM_TS_ITR2); //Tim2 off tim3
TIM_SelectSlaveMode(TIM2,TIM_SlaveMode_Gated); //Tim2 is gated by the tim3 channel2 input
#endif
//Timer4 is slaved off timer2 using channel1 output compare on both board revisions, providing we have at least 2 PPG channels
#if BOARD>=3
TIM_OCInitStructure.TIM_Pulse = GATING_PERIOD_ONE; //Defined in header file
#else
TIM_OCInitStructure.TIM_Pulse = GATING_PERIOD_ZERO; //Defined in header file
#endif
TIM_SelectOutputTrigger(TIM2,TIM_TRGOSource_OC1Ref);
TIM_OC1Init(TIM2, &TIM_OCInitStructure); //Tim2,ch1 used for gating
TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);
#if PPG_CHANNELS>1
//TIM_SelectMasterSlaveMode(TIM4,TIM_MasterSlaveMode_Enable);
TIM_SelectInputTrigger(TIM4,TIM_TS_ITR1); //Tim4 off tim2
TIM_SelectSlaveMode(TIM4,TIM_SlaveMode_Gated); //Tim4 is gated by the tim2 channel1 input
#endif

/*We enable all the timers at once with interrupts disabled*/
__disable_irq();
#if BOARD<3
#if PPG_CHANNELS>1
TIM_Cmd(TIM4, ENABLE);
#endif
TIM_Cmd(TIM2, ENABLE);
#else
#if PPG_CHANNELS>2
TIM4->CNT=PWM_PERIOD_CENTER/2;//This causes the third timer to be in antiphase, giving reduce peak ADC signal
TIM_Cmd(TIM4, ENABLE);
#endif
#if PPG_CHANNELS>1
TIM_Cmd(TIM2, ENABLE);
#endif
TIM_Cmd(TIM3, ENABLE);
#endif
__enable_irq();

/*Now setup timer1 as motor control */
//note no prescaler
TIM_TimeBaseStructure.TIM_Period = 2047;//gives a slower frequency - 35KHz, meeting Rohm BD6231F spec, and giving 11 bits of res each way
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//Make sure we have mode1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//Enable output
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable;//These settings need to be applied on timers 1 and 8
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);//same as timer4
/* PWM1 Mode configuration: Channel1 */
TIM_OC1Init(TIM1, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
TIM_CtrlPWMOutputs(TIM1, ENABLE); //Needs to be applied on 1 and 8
/* TIM1 enable counter */
TIM_ARRPreloadConfig(TIM1, ENABLE);
TIM_Cmd(TIM1, ENABLE);
Set_PWM_Motor(0); //Make sure motor off
}

/**
* @brief Try to correct the timer phase by adjusting the reload register for one pwm cycle
* @param Pointer to unsigned 32 bit integer bitmask for the timers to be corrected
* @retval none
* Note: this could be improved, atm it just uses some macros and is hardcoded for the timers, but making it more flexible would need arrays of pointers
* (need to use different timers for each output)
*//*
void Tryfudge(uint32_t* Fudgemask) {
if((*Fudgemask)&(uint32_t)0x01 && TIM2->CNT<(FUDGED_PWM_PERIOD(0)-2)) {//If the second bit is set, adjust the first timer in the list if it is safe to do so
TIM2->CR1 &= ~TIM_CR1_ARPE;//Disable reload buffering so we can load directly
TIM2->ARR = FUDGED_PWM_PERIOD(0);//Load reload register directly
TIM2->CR1 |= TIM_CR1_ARPE;//Enable buffering so we load buffered register
TIM2->ARR = NORMAL_PWM_PERIOD(0);//Load the buffer, so the pwm period returns to normal after 1 period
*Fudgemask&=~(uint32_t)0x01;//Clear the bit
}
if((*Fudgemask)&(uint32_t)0x04 && TIM4->CNT<(FUDGED_PWM_PERIOD(2)-2)) {//If the first bit is set, adjust the first timer in the list if it is safe to do so
TIM4->CR1 &= ~TIM_CR1_ARPE;//Disable reload buffering so we can load directly
TIM4->ARR = FUDGED_PWM_PERIOD(2);//Load reload register directly
TIM4->CR1 |= TIM_CR1_ARPE;//Enable buffering so we load buffered register
TIM4->ARR = NORMAL_PWM_PERIOD(2);//Load the buffer, so the pwm period returns to normal after 1 period
*Fudgemask&=~(uint32_t)0x04;//Clear the bit
}
}*/

/**
* @brief Configure the timer channel for PWM out to pump motor, -ive duty turns on valve
* @param None
* @retval None
* setting duty=0 gives idle state
*/
void Set_Motor(int16_t duty) {
duty=(duty>MAX_DUTY)?MAX_DUTY:duty; //enforce limits on range
if(duty<0) {//We are dumping with the solenoid valve
SET_SOLENOID(1);
Set_PWM_Motor(0);
}
else {//We are driving the pump
SET_SOLENOID(0);
Set_PWM_Motor(duty);
}
}
Loading

0 comments on commit d45aee5

Please sign in to comment.