/**************************************************************************//** * @brief Setup the ACMP *****************************************************************************/ void setupACMP(void) { /* ACMP configuration constant table. */ static const ACMP_Init_TypeDef initACMP = { .fullBias = false, /* fullBias */ .halfBias = true, /* halfBias */ .biasProg = 0x0, /* biasProg */ .interruptOnFallingEdge = false, /* interrupt on rising edge */ .interruptOnRisingEdge = false, /* interrupt on falling edge */ //.interruptOnFallingEdge = true, /* interrupt on rising edge */ //.interruptOnRisingEdge = true, /* interrupt on falling edge */ .warmTime = acmpWarmTime4, /* 512 cycle warmup to be safe */ .hysteresisLevel = acmpHysteresisLevel0, /* No Hysteresis */ .inactiveValue = false, /* inactive value */ .lowPowerReferenceEnabled = true, /* low power reference */ .vddLevel = 32, /* VDD level : VDD/2 */ .enable = false /* Don't request enabling. */ }; /* Configure ACMP. */ ACMP_Init(ACMP1, &initACMP); /* Disable ACMP0 out to a pin. */ ACMP_GPIOSetup(ACMP1, 0, false, false); /* Set up ACMP negSel to VDD, posSel is controlled by LESENSE. */ ACMP_ChannelSet(ACMP1, acmpChannelVDD, acmpChannel0); /* LESENSE controls ACMP thus ACMP_Enable(ACMP0) should NOT be called in order * to ensure lower current consumption. */ #ifdef DEBUG_ACMP ACMP_ChannelSet(ACMP1, acmpChannelVDD, acmpChannel2); ACMP1->IEN = 1; ACMP_Enable(ACMP1); NVIC_ClearPendingIRQ(ACMP0_IRQn); NVIC_EnableIRQ(ACMP0_IRQn); #endif } #ifdef DEBUG_ACMP // ACMP IRQ Handler is shared between ACMP0 and ACMP1 void ACMP0_IRQHandler(void) { PRINTF("%s\r\n",__func__); ACMP1->IFC = 1; NVIC_ClearPendingIRQ(ACMP0_IRQn); }
/**************************************************************************//** * @brief This function iterates through all the capsensors and reads and * initiates a reading. Uses EM1 while waiting for the result from * each sensor. *****************************************************************************/ void CAPSENSE_Sense(void) { /* Use the default STK capacative sensing setup and enable it */ ACMP_Enable(ACMP_CAPSENSE); uint8_t ch; /* Iterate trough all channels */ for (currentChannel = 0; currentChannel < ACMP_CHANNELS; currentChannel++) { /* If this channel is not in use, skip to the next one */ if (!channelsInUse[currentChannel]) { continue; } /* Set up this channel in the ACMP. */ ch = currentChannel; ACMP_CapsenseChannelSet(ACMP_CAPSENSE, (ACMP_Channel_TypeDef) ch); /* Reset timers */ TIMER0->CNT = 0; TIMER1->CNT = 0; measurementComplete = false; /* Start timers */ TIMER0->CMD = TIMER_CMD_START; TIMER1->CMD = TIMER_CMD_START; /* Wait for measurement to complete */ while ( measurementComplete == false ) { EMU_EnterEM1(); } } /* Disable ACMP while not sensing to reduce power consumption */ ACMP_Disable(ACMP_CAPSENSE); }
/**************************************************************************//** * @brief ACMP_setup * Configures and starts the ACMP *****************************************************************************/ void ACMP_setup(void) { /* Enable necessary clocks */ CMU_ClockEnable(cmuClock_ACMP0, true); CMU_ClockEnable(cmuClock_GPIO, true); /* Configure PC4 as input with pull down for ACMP channel 4 input */ GPIO_PinModeSet(gpioPortC, 4, gpioModeInputPullFilter, 0); /* Analog comparator parameters */ const ACMP_Init_TypeDef acmpInit = { .fullBias = false, /* no full bias current*/ .halfBias = false, /* no half bias current */ .biasProg = 7, /* Biasprog current 1.4 uA */ .interruptOnFallingEdge = false, /* disable interrupt for falling edge */ .interruptOnRisingEdge = false, /* disable interrupt for rising edge */ .warmTime = acmpWarmTime256, /* Warm-up time in clock cycles, should be >140 cycles for >10us warm-up @ 14MHz */ .hysteresisLevel = acmpHysteresisLevel0, /* Hysteresis level 0 - no hysteresis */ .inactiveValue = 0, /* Inactive comparator output value */ .lowPowerReferenceEnabled = false, /* Low power reference mode disabled */ .vddLevel = 32, /* Vdd reference scaling of 32 */ }; /* Init ACMP and set ACMP channel 4 as positive input and scaled Vdd as negative input */ ACMP_Init(ACMP0, &acmpInit); ACMP_ChannelSet(ACMP0, acmpChannelVDD, acmpChannel4); ACMP_Enable(ACMP0); } /**************************************************************************//** * @brief PRS_ScanChannel * Waits for activity on a selected PRS channel and writes on the LCD when activity occurs * * @param[in] timer * Pointer to TIMER peripheral register block. * * @param[in] prsCh * PRS Channel to be monitored * * @param[in] edgeType * Signal edge to be monitored/captured *****************************************************************************/ void PRS_ScanChannel(TIMER_TypeDef *timer, TIMER_PRSSEL_TypeDef prsCh, TIMER_Edge_TypeDef edgeType) { /* enable the clock for the correct timer */ #define TIMER_Clock(T) (T==TIMER0 ? cmuClock_TIMER0 : \ T==TIMER1 ? cmuClock_TIMER1 : 0) /* Enable necessary clocks */ CMU_ClockEnable((CMU_Clock_TypeDef)TIMER_Clock(timer), true); /* Initialize LCD */ SegmentLCD_Init(false); /* Select CC channel parameters */ const TIMER_InitCC_TypeDef timerCCInit = { .eventCtrl = timerEventFalling, /* input capture event control */ .edge = edgeType, /* input capture on falling edge */ .prsSel = prsCh, /* prs channel select channel 5*/ .cufoa = timerOutputActionNone, /* no action on counter underflow */ .cofoa = timerOutputActionNone, /* no action on counter overflow */ .cmoa = timerOutputActionNone, /* no action on counter match */ .mode = timerCCModeCapture, /* CC channel mode capture */ .filter = false, /* no filter */ .prsInput = true, /* CC channel PRS input */ .coist = false, /* comparator output initial state */ .outInvert = false, /* no output invert */ }; /* Initialize TIMER0 CC0 */ TIMER_InitCC(timer, 0, &timerCCInit); /* Select timer parameters */ const TIMER_Init_TypeDef timerInit = { .enable = true, /* start counting when init complete */ .debugRun = false, /* counter not running on debug halt */ .prescale = timerPrescale1024, /* prescaler of 64 */ .clkSel = timerClkSelHFPerClk, /* TIMER0 clocked by the HFPERCLK */ .fallAction = timerInputActionNone, /* stop counter on falling edge */ .riseAction = timerInputActionNone, /* reload and start on rising edge */ .mode = timerModeUp, /* counting up */ .dmaClrAct = false, /* no DMA */ .quadModeX4 = false, /* no quad decoding */ .oneShot = false, /* counting up constinuously */ .sync = false, /* no start/stop/reload by other timers */ }; /* Initialize TIMER0 */ TIMER_Init(timer, &timerInit); /* Poll the Input Capture Valid flag in the Status register The program will hang at this point waiting for activity on this channel */ while(!((TIMER0->STATUS & _TIMER_STATUS_ICV0_MASK )>>16)); } /**************************************************************************//** * @brief Main function * Main is called from __iar_program_start, see assembly startup file *****************************************************************************/ int main(void) { /* Align different chip revisions*/ CHIP_Init(); /* Initialise the ACMP */ ACMP_setup(); /* PRS setup */ /* Enable clock for PRS and select ACMP as source and ACMP0OUT (ACMP0 OUTPUT) as signal for channel 5 */ CMU_ClockEnable(cmuClock_PRS, true); PRS_SourceSignalSet(5, PRS_CH_CTRL_SOURCESEL_ACMP0, PRS_CH_CTRL_SIGSEL_ACMP0OUT, prsEdgeOff); /* Start PRS scan This function will halt the program while there is no PRS activity This function assumes that the PRS has been setup previously */ PRS_ScanChannel(TIMER0, timerPRSSELCh5, timerEdgeFalling); /* Write PRS and channel number on the LCD to acknowledge PRS activity */ SegmentLCD_Write("PRS"); SegmentLCD_Number((int)timerPRSSELCh5); while(1) { /* Enter EM1 while waiting for capture. */ EMU_EnterEM1(); } }
/**************************************************************************//** * @brief TIMER0_setup * Configures the TIMER *****************************************************************************/ void TIMER_setup(void) { /* Enable necessary clocks */ CMU_ClockEnable(cmuClock_TIMER0, true); CMU_ClockEnable(cmuClock_PRS, true); /* Select CC channel parameters */ TIMER_InitCC_TypeDef timerCCInit = { .eventCtrl = timerEventEveryEdge, /* Input capture event control */ .edge = timerEdgeBoth, /* Input capture on falling edge */ .prsSel = timerPRSSELCh5, /* Prs channel select channel 5*/ .cufoa = timerOutputActionNone, /* No action on counter underflow */ .cofoa = timerOutputActionNone, /* No action on counter overflow */ .cmoa = timerOutputActionNone, /* No action on counter match */ .mode = timerCCModeCapture, /* CC channel mode capture */ .filter = false, /* No filter */ .prsInput = true, /* CC channel PRS input */ .coist = false, /* Comparator output initial state */ .outInvert = false, /* No output invert */ }; /* Initialize TIMER0 CC0 channel */ TIMER_InitCC(HIJACK_RX_TIMER, 0, &timerCCInit); /* Select timer parameters */ const TIMER_Init_TypeDef timerInit = { .enable = false, /* Do not start counting when init complete */ .debugRun = false, /* Counter not running on debug halt */ .prescale = HIJACK_TIMER_RESOLUTION, /* Prescaler of 1 */ .clkSel = timerClkSelHFPerClk, /* TIMER0 clocked by the HFPERCLK */ .fallAction = timerInputActionReloadStart, /* Stop counter on falling edge */ .riseAction = timerInputActionReloadStart, /* Reload and start on rising edge */ .mode = timerModeUp, /* Counting up */ .dmaClrAct = false, /* No DMA */ .quadModeX4 = false, /* No quad decoding */ .oneShot = false, /* Counting up constinuously */ .sync = false, /* No start/stop/reload by other timers */ }; /* Initialize TIMER0 */ TIMER_Init(HIJACK_RX_TIMER, &timerInit); /* PRS setup */ /* Select ACMP as source and ACMP0OUT (ACMP0 OUTPUT) as signal */ PRS_SourceSignalSet(5, PRS_CH_CTRL_SOURCESEL_ACMP0, PRS_CH_CTRL_SIGSEL_ACMP0OUT, prsEdgeOff); /* Enable CC0 interrupt */ TIMER_IntEnable(HIJACK_RX_TIMER, TIMER_IF_CC0); /* Enable TIMER0 interrupt vector in NVIC */ NVIC_EnableIRQ(TIMER0_IRQn); } /**************************************************************************//** * @brief ACMP_setup * Configures and starts the ACMP *****************************************************************************/ static void ACMP_setup(void) { /* Enable necessary clocks */ CMU_ClockEnable(HIJACK_RX_ACMPCLK, true); CMU_ClockEnable(cmuClock_GPIO, true); /* Configure ACMP input pin. */ GPIO_PinModeSet(HIJACK_RX_GPIO_PORT, HIJACK_RX_GPIO_PIN, gpioModeInput, 0); /* Analog comparator parameters */ const ACMP_Init_TypeDef acmpInit = { .fullBias = false, /* No full bias current*/ .halfBias = true, /* No half bias current */ .biasProg = 2, /* Biasprog current 1.4 uA */ .interruptOnFallingEdge = false, /* Disable interrupt for falling edge */ .interruptOnRisingEdge = false, /* Disable interrupt for rising edge */ .warmTime = acmpWarmTime256, /* Warm-up time in clock cycles, should be >140 cycles for >10us warm-up @ 14MHz */ .hysteresisLevel = acmpHysteresisLevel7, /* Hysteresis level 0 - no hysteresis */ .inactiveValue = 1, /* Inactive comparator output value */ .lowPowerReferenceEnabled = false, /* Low power reference mode disabled */ .vddLevel = HIJACK_RX_ACMP_LEVEL, /* Vdd reference scaling of 32 */ }; /* Use ACMP0 output, PD6 . */ //GPIO_PinModeSet(gpioPortD, 6, gpioModePushPull, 0); //ACMP_GPIOSetup(ACMP0, 2, true, false); /* Init ACMP and set ACMP channel 4 as positive input and scaled Vdd as negative input */ ACMP_Init(HIJACK_RX_ACMP, &acmpInit); ACMP_ChannelSet(HIJACK_RX_ACMP, HIJACK_RX_ACMP_NEG, HIJACK_RX_ACMP_CH); ACMP_Enable(HIJACK_RX_ACMP); } /** * @brief calculate whether cnt is in 500us region * ticker = 64Mhz/128 = 2us * 475us < cnt < 510us * */ static chk_result_t IsTime2Detect(uint32_t inv) { chk_result_t ret; if( inv < HIJACK_DEC_NUM_TICKS_MIN){ offset = inv; ret = pass; } else if ( ( inv <= HIJACK_DEC_NUM_TICKS_MAX ) && ( inv >= HIJACK_DEC_NUM_TICKS_MIN ) ) { offset = 0; inv = 0; ret = suit; } else{ offset = 0; inv = 0; ret = error; } return ret; } /* * Find phase remain or phase reversal. */ static void dec_parser(uint8_t bit_msk, state_t state) { if ( ( suit == IsTime2Detect(inv) ) ){ //it's time to determine if( falling == cur_edge ){ dec.data &= ~(1 << bit_msk); #if DEC_DEBUG == 1 uartPutChar( '+' ) ; uartPutChar( '_' ) ; #endif//DEC_DEBUG == 1 } else{ dec.data |= (1 << bit_msk); #if DEC_DEBUG == 1 uartPutChar( '_' ) ; uartPutChar( '+' ) ; #endif//DEC_DEBUG == 1 dec.odd++; } dec.state = state; //state switch } else if ( error == IsTime2Detect(inv) ){ //wait for edge detection time dec.state = Waiting; //state switch } } /**************************************************************************//** * @brief decode state machine * Invoke in TIMER_ISR for decoding. *****************************************************************************/ void decode_machine(void) { inv = offset + cur_stamp; //update offset #if 0 if( dec.state > Waiting ){ USART_printHexBy16u(inv); if(cur_edge == rising){ uartPutChar( '\\' ) ; } else{ uartPutChar( '/' ) ; } } #endif switch (dec.state){ case Waiting: /* go to start bit if rising edge exist. */ if (rising == cur_edge) { dec.state = Sta0; offset = 0; inv = 0; } break; // case Sta0: if( ( suit == IsTime2Detect(inv) ) && ( falling == cur_edge ) ){ dec.data = 0; //clear data field for store new potential data dec.odd = 0; //clear odd field parity counter dec.state = Bit0; #if DEC_DEBUG == 1 uartPutChar( 'S' ) ; uartPutChar( '+' ) ; uartPutChar( '_' ) ; #endif } else{ dec.state = Waiting; } break; // case Bit0: #if DEC_DEBUG == 1 uartPutChar( '0' ) ; #endif dec_parser(BIT0, Bit1); break; // case Bit1: #if DEC_DEBUG == 1 uartPutChar( '1' ) ; #endif dec_parser(BIT1, Bit2); break; // case Bit2: #if DEC_DEBUG == 1 uartPutChar( '2' ) ; #endif dec_parser(BIT2, Bit3); break; // case Bit3: #if DEC_DEBUG == 1 uartPutChar( '3' ) ; #endif dec_parser(BIT3, Bit4); break; // case Bit4: #if DEC_DEBUG == 1 uartPutChar( '4' ) ; #endif dec_parser(BIT4, Bit5); break; // case Bit5: #if DEC_DEBUG == 1 uartPutChar( '5' ) ; #endif dec_parser(BIT5, Bit6); break; // case Bit6: #if DEC_DEBUG == 1 uartPutChar( '6' ) ; #endif dec_parser(BIT6, Bit7); break; // case Bit7: #if DEC_DEBUG == 1 uartPutChar( '7' ) ; #endif dec_parser(BIT7, Parity); break; // case Parity: if ( ( suit == IsTime2Detect(inv) ) ){ //it's time to determine if( rising == cur_edge ){ dec.odd++; #if DEC_DEBUG == 1 uartPutChar( '_' ) ; uartPutChar( '+' ) ; #endif } else{ #if DEC_DEBUG == 1 uartPutChar( '+' ) ; uartPutChar( '_' ) ; #endif } #if DEC_DEBUG == 1 uartPutChar( dec.odd + 0x30) ; #endif if( 1 == (dec.odd%2)){ //parity pass dec.state = Sto0; } else{ //parity failed dec.state = Waiting; } } else if ( error == IsTime2Detect(inv) ){ //wait for edge detection time dec.state = Waiting; } break; // case Sto0: if ( ( suit == IsTime2Detect(inv) ) ){ //it's time to determine if( rising == cur_edge ){ //stop bit is rising edge USART_txByte(dec.data); #if DEC_DEBUG == 1 uartPutChar( '_' ) ; uartPutChar( '+' ) ; #endif HIJACKPutData(&dec.data, &decBuf, sizeof(uint8_t)); } else{ #if DEC_DEBUG == 1 uartPutChar( '+' ) ; uartPutChar( '_' ) ; #endif } dec.state = Waiting; #if DEC_DEBUG == 1 uartPutChar( '\r' ) ; uartPutChar( '\n' ) ; #endif } else if ( error == IsTime2Detect(inv) ){ //wait for edge detection time dec.state = Waiting; } break; // default: break; // } }