forked from Laurenceb/STM32-Logger
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Laurence
authored and
Laurence
committed
May 23, 2013
1 parent
069af76
commit d45aee5
Showing
7 changed files
with
395 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
Oops, something went wrong.