//---------------------------------------------------------------------------------------------------------------------------------------- // WPT-MAIN.C Wireless Pneumatic Thermostat Main Program //---------------------------------------------------------------------------------------------------------------------------------------- // Copyright 2003-2011, Cypress Semiconductor Corporation.This software is owned by Cypress Semiconductor Corporation (Cypress) // and is protected by and subject to worldwide patent protection (United States and foreign), United States copyright laws and international // treaty provisions. Cypress hereby grants to licensee a personal, non-exclusive, non-transferable license to copy, use, modify, create // derivative works of, and compile the Cypress Source Code and derivative works for the sole purpose of creating custom software in support of // licensee product to be used only in conjunction with a Cypress integrated circuit as specified in the applicable agreement. Any reproduction, // modification, translation, compilation, or representation of this software except as specified above is prohibited without the express // written permission of Cypress. // // Disclaimer: CYPRESS MAKES NO WARRANTY OF ANY KIND,EXPRESS OR IMPLIED, WITH REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. Cypress reserves the right to make changes without further notice to the // materials described herein. Cypress does not assume any liability arising out of the application or use of any product or circuit described herein. // Cypress does not authorize its products for use as critical components in life-support systems where a malfunction or failure may reasonably be // expected to result in significant injury to the user. The inclusion of Cypress' product in a life-support systems application implies that the // manufacturer assumes all risk of such use and in doing so indemnifies Cypress against all charges. // // Use may be limited by and subject to the applicable Cypress software license agreement. //---------------------------------------------------------------------------------------------------------------------------------------- #include "includes.h" //#define LSB16(data) (uint8_t)(data&0x00FF) // Get LSB of a 16-bit data //#define MSB16(data) (uint8_t)((data>>8)&0x00FF) // Get MSB of a 16-bit data #if (ENABLE_RF_COMM == 1) void AutoDiscoveryProcess(void); void OccFailSafe(void); #endif void DisplayTemperatureUnits(void); uint8_t AddOffset(uint8_t); void DisableAutoCalBuf(void); void DispAmbTemp(void); uint8_t FlushFirstInstanceOf(uint8_t); void FlushQueue(uint8_t); int16_t GetTaskMessage(uint8_t, uint16_t *); void OneSecondProcessing(void); uint8_t ReadWDTStatus(void); void RemoveMessage(uint8_t); uint8_t RemoveOffset(uint8_t); uint8_t SendTaskMessage(uint8_t, uint8_t, uint16_t); uint8_t SetNextAlarm(void); int16_t TaskScheduler(void); void Timer8Int(void); static uint8_t ToBCD(uint8_t); void SleepTasks(void); void lvd(void); uint8_t u8EEProm_Data[E2PROM_SIZE] = {0x01, 0x10, 0x01, 0x00, DEFAULT_RF_STATE, DB_DEFAULT_CONTROL_PRESSURE_PSI_SC2,BOOL_TRUE, BOOL_FALSE}; // defaulted by u8E2PROM_DEFAULT[] (wpt-main.h file) const uint8_t u8E2PROM_DEFAULT[] = {0x01, 0x10, 0x01, 0x00, DEFAULT_RF_STATE, DB_DEFAULT_CONTROL_PRESSURE_PSI_SC2,BOOL_TRUE, BOOL_FALSE}; uint8_t cTxBuffer[I2C_TX_BUFFER_SIZE]; uint8_t sysIsrEvent = 0; uint16_t NumberTxSamples = 0xffff; // test variables uint8_t CycleCounter = 0; uint16_t ChargBeforeSleep,ChargeAfterSleep; static FILE mystdout = FDEV_SETUP_STREAM(DebugPrintChar, NULL, _FDEV_SETUP_WRITE); #if 1 const NVRAM_DATA_OVERLAY g_strctPSOC_RAM_DEFAULT = { 0, { #if(DEM0_NODE_3SEC_UPDATE_1MIN_TX_RATE == 0x01) 1, /* DVM_OPERATIONAL_MODE */ #endif (uint16_t)(DEFAULT_MOTOR_POSITION*STEPSPERDEGF), // 68*82 = 5576 DEFAULT_MOTOR_STEP_NUM_SIGNATURE, // 0xa5 OCC_STATE_OCCUPIED, // 1 DEFAULT_LOWER_SETPOINT_LIMIT, // 55 DEFAULT_UPPER_SETPOINT_LIMIT, // 85 {DEFAULTTEMPSETPOINT, DEFAULTTEMPSETPOINT}, DEFAULTTEMPSETPOINT, {DEFAULTTEMPSPAN, DEFAULTTEMPSPAN}, /* default value: 5 or 0 */ DEFAULTTEMPSPAN, DEFAULT_INTERNAL_MODE, DEFAULT_ERR_MSG, DEFAULT_EOL_TEMP_LSB, /* Default temperature reading in EOL msg */ DEFAULT_EOL_TEMP_MSB, DEFAULT_EOL_PRESSURE /* Default pressure reading in EOL msg */ } }; #endif //---------------------------------------------------------------------------------------------------------------------------------------- // Function Name : DispAmbTemp(void) // Function description : The function dispalys ambient temperature in LCD // : Read Temp from TMP275 sensor and convert the value //---------------------------------------------------------------------------------------------------------------------------------------- void DispAmbTemp(void) { ExtendMinimumExecutionTime(); // Reload the timer cAmbientTemperature_DegF = ReadTemperature(); #ifdef ENABLE_DEBUG_OUT printf("Temp F = %d\r\n",cAmbientTemperature_DegF); #endif if (cAmbientTemperature_DegF != 0) { cAmbientTemperature_DegF = AddOffset(cAmbientTemperature_DegF); // Add or remove offset values } if (cAmbientTemperature_DegF >= 99) { cAmbientTemperature_DegF = 99; } cDigitsValue = ToUserUnits(cAmbientTemperature_DegF); // Display Ambient Temperature UpdateDigits(); DisplayTemperatureUnits(); } //---------------------------------------------------------------------------------------------------------------------------------------- // Function Name : AddOffset(uint8_t cValue) // Function description : If Temperature Offset is present, add or subtract it. //---------------------------------------------------------------------------------------------------------------------------------------- uint8_t AddOffset(uint8_t cValue) { uint8_t cCalVal; if (PSOC_RAM_DATA.u8TempOffset != 0) { if (PSOC_RAM_DATA.u8TempOffset & 0x80) // -ve offset value (MSB = 1) { cCalVal = (PSOC_RAM_DATA.u8TempOffset & 0x7F); cValue = (cValue - cCalVal) ; // Subtract the offset } else // +ve offset value (MSB = 0) { cCalVal = (PSOC_RAM_DATA.u8TempOffset & 0x7F); cValue = (cValue+ cCalVal) ; // Add the offset } } return cValue; } //---------------------------------------------------------------------------------------------------------------------------------------- // Function Name : RemoveOffset(uint8_t cValue) // Function description : If Temperature Offset is present, remove it. //---------------------------------------------------------------------------------------------------------------------------------------- uint8_t RemoveOffset(uint8_t cValue) { uint8_t cCalVal; if (PSOC_RAM_DATA.u8TempOffset != 0) { if (PSOC_RAM_DATA.u8TempOffset & 0x80) // -ve offset value { cCalVal = (PSOC_RAM_DATA.u8TempOffset & 0x7F); cValue = (cValue + cCalVal) ; // offset removed value } else // +ve offset value { cCalVal = (PSOC_RAM_DATA.u8TempOffset & 0x7F); cValue = (cValue - cCalVal) ; // offset removed value } } return cValue; } //---------------------------------------------------------------------------------------------------------------------------------------- // Function: DisableAutoCalBuf() * // Description: For non deadband, Clear the AutoCal flags. DB Cal buffer will be cleared and not in use. * // Revision: Created on 00.26.03 * //---------------------------------------------------------------------------------------------------------------------------------------- void DisableAutoCalBuf(void) { #if (DB_WPT_ENABLED == 0) PSOC_RAM_DATA.u8AutoCal = 0; PSOC_RAM_DATA.u8AlarmStatus &= (uint8_t)~ALARM_AUTO_CAL; #endif } #if (DEM0_NODE_3SEC_UPDATE_1MIN_TX_RATE == 1) extern void UserSetpointChange_CS(uint8_t u8NewSetpointDegF, uint8_t u8NewSpanDegF, uint8_t bForce); void UpdateDemoState(int16_t iNodeID) { uint8_t u8OddDB = iNodeID & 0x0001; DEBUG_STRING_1_U16("%04X\r\n",iNodeID); DEBUG_STRING_2_U8("%d %d\r\n",u8OddDB,PSOC_RAM_DATA.u8DemoNodeDBEnabled); if (PSOC_RAM_DATA.u8DemoNodeDBEnabled == !u8OddDB) // If the state is changing { printf("Default\r\n"); PSOC_RAM_DATA.u8TempCenterpointBufDegF[0] = DEFAULTTEMPSETPOINT; // Default occupancy buffers and current setpoints PSOC_RAM_DATA.u8TempCenterpointBufDegF[1] = DEFAULTTEMPSETPOINT; if (u8OddDB == 1) // For Deadband { PSOC_RAM_DATA.u8TempSpanBufDegF[0] = DEFAULTTEMPSPAN; PSOC_RAM_DATA.u8TempSpanBufDegF[1] = DEFAULTTEMPSPAN; UserSetpointChange_CS(DEFAULTTEMPSETPOINT, DEFAULTTEMPSPAN, SP_CHANGE_NO_FORCE); } else { PSOC_RAM_DATA.u8TempSpanBufDegF[0] = 0; PSOC_RAM_DATA.u8TempSpanBufDegF[1] = 0; UserSetpointChange_CS(DEFAULTTEMPSETPOINT, 0, SP_CHANGE_NO_FORCE); } } PSOC_RAM_DATA.u8DemoNodeDBEnabled = u8OddDB; } #endif //--------------------------------------------------------------------------------------- // Function Name : TaskScheduler() // Function description : Gives control to all tasks on a roundrobin basis. All tasks // returns control to Task scheduler ASAP. After all tasks are // given control in one round, this checks iIdleSempahore, If it // is 0, puts the system in Sleep mode //--------------------------------------------------------------------------------------- int16_t TaskScheduler(void) { uint8_t i; iTaskNumber = 0; // start executing first task onwards ExtendMinimumExecutionTime(); while (1) { // DEBUG //DEBUG_TP5_TGL; // //printf("T %d S%d,%d,%d,%d,%d Idle %x\r\n",iTaskNumber,iState[1],iState[2],iState[3],iState[4],iState[5],iIdleSemaphore); //while(UartDebugBuf.Count); // DEBUG // Above print takes 1.4mS and makes the key debounce work //DelaymS(1); //SleepXmegaNmS(1); // #ifndef RANGE_TEST_UNIT // Sleep for 1ms all tasks except when the motor is active if(!(iIdleSemaphore & MASK_MTASK )) { // Dont sleep if debug messages pending while(UartDebugBuf.Count); set_sleep_mode(SLEEP_MODE_PWR_SAVE);//SLEEP_MODE_PWR_SAVE);// SLEEP_MODE_PWR_DOWN); sleep_enable(); sleep_cpu(); // // code sleeps here, wakes up with ISR from key press or RTC IRQ // sleep_disable(); }//else //DelaymS(1); #endif if (iTaskNumber == LTASK) // 0 { //printf("LTASK\r\n"); ClearWDT(); i = LCDTask(); // return 0 if no message available otherwise 1 } if (iTaskNumber == KTASK) // 1 { //printf("KTASK\r\n"); ClearWDT(); i = KeyboardTask(); // Always returns 1 } if (iTaskNumber == MTASK) // 2 { //printf("MTASK\r\n"); ClearWDT(); //#ifndef RANGE_TEST_UNIT i = MotorTask(); // return 0 if no message available otherwise 1 //#endif } if (iTaskNumber == UTASK) // 3 { //printf("UTASK\r\n"); ClearWDT(); i = UserInterfaceTask(); // Always return 0 } // HTASK and RTASK are not used. Radio functions are executed once we exit the task scheduler //#if (ENABLE_RF_COMM == 1) // Don't use this code if RF Comm is disabled //if (iTaskNumber == HTASK) // 4 //{ //if ((u8EEProm_Data[E2PROM_IS_RFENABLED] != RF_STATE_OFF) && (PSOC_RAM_DATA.u8WakeUpState != WAKE_STATE_SUPRESS_TX)) //{ ////printf("HTASK\r\n"); //ClearWDT(); //i = HouseKeepingTask(); // always return 1 //} //} //#endif //#if (ENABLE_RF_COMM == 1) // Don't use this code if RF Comm is disabled //if (iTaskNumber == RTASK) // 5 //{ //if ((u8EEProm_Data[E2PROM_IS_RFENABLED] != RF_STATE_OFF) && (PSOC_RAM_DATA.u8WakeUpState != WAKE_STATE_SUPRESS_TX)) //{ ////printf("RTASK\r\n"); //ClearWDT(); //i = RadioTask(); // return 0 if no message return 1 if message is "MSG_SEND_FRAME" //} //} //#endif iTaskNumber++; if(iTaskNumber == HTASK) iTaskNumber = LASTTASK; // Skip over Radio task. Want radio task to be last thing in the normal process after all other WPT tasks // are complete and iIdleSemaphore == 0 if (iTaskNumber >= LASTTASK) // 6 { iTaskNumber = LTASK; // Go back to first task //printf("LASTTASK\r\n"); if (iIdleSemaphore == 0) // If no one has requested the "task scheduler" to prevent sleep { // try going to sleep // if we can be idle and all tasks finished then exit this function, otherwise keep looping through tasks break; } } i = 0; } // while (1) return 1; } //---------------------------------------------------------------------------------------------------------------------------------------- // Function Name : RemoveMessage() // Function description : Removes the nth mesage in the queue and moves all mesages from bottom to top //---------------------------------------------------------------------------------------------------------------------------------------- void RemoveMessage(uint8_t u8Index) { int16_t i, k; i = mMessageQueue.iMessageCount; if (i <= u8Index) // If index to remove is already empty, return without compressing { return; } i--; mMessageQueue.iMessageCount = i; for (k=u8Index; k> 4; j = j & 0x0f; if (j == cTaskID) // If Ready Message Matches TaskID from user { if (pMessageParameter != NULL) // Store parameter only if user passed in a valid pointer { *pMessageParameter = mMessageQueue.mTaskMessage[0].u16MessageParameter; // Store message parameter } RemoveMessage(0); return (i & 0x0f); // return message number } // If we get there, there are messages in the queue but the ready element does not match user's TaskID // Search if their exists a message from user in queue for (i1=0; i1> 4; j = j & 0x0f; if (j == cTaskID) { // task ID matches return (0xff); // inidcates there is message ahead in the queue } } return 0; // indicates there is no message for this task, in the queue } //---------------------------------------------------------------------------------------------------------------------------------------- // Function Name : MessageQueueNumFree(); // Function description : This function Returns the number of free entries in queue. Returns 1 if 1 entry free, 2 if 2 entries free. // Returns a char not unsigned in case of bug that causes iMessageCount to be negative. //---------------------------------------------------------------------------------------------------------------------------------------- uint8_t MessageQueueNumFree(void) { return ((char)QSIZE - (char)mMessageQueue.iMessageCount); } //---------------------------------------------------------------------------------------------------------------------------------------- // Function Name : MessageQueueNumFree(); // Function description : This function Returns the number of free entries in queue. Returns 1 if 1 entry free, 2 if 2 entries free. // Returns a char not unsigned in case of bug that causes iMessageCount to be negative. //---------------------------------------------------------------------------------------------------------------------------------------- uint8_t FlushFirstInstanceOf(uint8_t cTaskID) { uint8_t i; cTaskID <<= 4; for (i=0; i SURVEY_WAKE_TIME/WPT_SLEEP_TIME) { // Need to put system to sleep until button press or module wakes up SleepRFModule(MODULE_MAX_SLEEP_TIME); // Clear display DisplayChar(0,0); DisplayBattery(BATTERY_ICON_0); }else //xro fixme: module sleep time need to be function of sleep interval, not fixed value SleepRFModule(600000);//((uint32_t)WPT_SLEEP_TIME-1)*1000); // less one second so rm wakes up just pror to RTC wakeup if (iMotorStepNumber != -1) // update on a valid motor step value { PSOC_RAM_DATA.u8MotorStepNum = (uint8_t)iMotorStepNumber; } //printf("\r\nSetNextAlarm in SLEEP\r\n"); PSOC_RAM_DATA.u8WakeUpState = SetNextAlarm(); // Set Alarm for Next wake up. DelaymS(3); // Fixed delay for I2C communication // wait for all rf module messages to complete while(UartTxBuf.Count); _delay_us(300); // wait for last char to go out // Read current charge count and reset back to full 0xffff FramImage.TotalChargeCounts += 0xffff-ReadFuelLevelReg(1); DEBUG_TP1_LOW; SleepFuelGauge(); // save accumulate charge count FramWrite((uint8_t *)&FramImage.TotalChargeCounts,FRAM_CHARGE_SIZE,FRAM_CHARGE_OFFSET); PowerDown(); // Turn off power to external hardware // Enable key interrupts on rising edge pe2, all keys are connected via diode // Need to preserve pull down PinVar = PORTE_PIN2CTRL; PinVar &= ~0x07; PinVar |= 0x01; // Rising edge int PORTE_PIN2CTRL = PinVar; PORTE_INTCTRL = 0x01; // enable low level enable for int0 PORTE_INTFLAGS = 0x01; // clear flags PORTE_INT0MASK = MASK_KEY_INT_PIN; // Enable port e int 0 for any key if(NumberTxSamples > SURVEY_WAKE_TIME/WPT_SLEEP_TIME) { // Enable Atmel to wake up if serial activity from module waking up. USARTD0_CTRLB &= ~0x10; // disable usart rx functions so we can control IO pins // Set falling edge interrupt PORTD_PIN2CTRL = 0x02; // Stop RTC RTC_CTRL = 0x00; // disable // disable any uart tx/rx interrupts on usartd0 // Casch current settings UartIntState = USARTD0_CTRLA; USARTD0_CTRLA = 0; // Enable rx interrupt on pd2 PORTD_INTFLAGS = 0x01; // clear flags PORTD_INT0MASK = LORA_MODULE_RXD_PIN; // Enable portD int 0 PORTD_INTCTRL = 0x01; }else { // Enable ISR from nirq_rtc on pa2 PORTA_INTCTRL = 0x01; // low level enable for int0 PORTA_INTFLAGS = 0x01; // clear flags PORTA_INT0MASK = RTC_NIRQ_PIN; // Enable port b int 0 for all keys } // Disable Atmel RTC as ISLRTC will wake us up. RTC_CTRL = 0; // Disable RTC RTC_INTCTRL = 0x00; // disable rtc interrupts cPowerControlStatus |= MASK_SLEEPING; sysIsrEvent = 0; // clear all events // now go to sleep // Wake up only from key press or RTC nIRQ. set_sleep_mode(SLEEP_MODE_PWR_SAVE);//SLEEP_MODE_PWR_SAVE);// SLEEP_MODE_PWR_DOWN); sleep_enable(); sleep_cpu(); // // code sleeps here, wakes up with ISR from key press or RTC IRQ // sleep_disable(); cPowerControlStatus &= ~MASK_SLEEPING; RTC_INTCTRL = 0x02; // enable rtc interrupts if(NumberTxSamples > SURVEY_WAKE_TIME/WPT_SLEEP_TIME) { USARTD0_CTRLA = UartIntState; USARTD0_CTRLB |= 0x10; // enable usart both rx // set tx as output PORTD_DIRSET = LORA_MODULE_TXD_PIN; } } //---------------------------------------------------------------------------------------------------------------------------------------- // Function Name : ISR(IRQ_NIRQ_RTC) // Function description : Service alarm interrupt from isl2020RTC //---------------------------------------------------------------------------------------------------------------------------------------- ISR(IRQ_NIRQ_RTC) { #ifdef ENABLE_DEBUG_OUT DEBUG_STRING("RTC IRQ\r\n"); #endif // disable pa2 interrupt PORTA_INT0MASK = 0; // disable int 0 for all keys sysIsrEvent |= RTC_ISR_FLAG; } //---------------------------------------------------------------------------------------------------------------------------------------- // Function Name : ISR(IRQ_KEY_SW) // Function description : Service Key switch common ISR //---------------------------------------------------------------------------------------------------------------------------------------- ISR(IRQ_KEY_SW) { DEBUG_STRING("SW IRQ\r\n"); // disable pa2 interrupt PORTE_INT0MASK = 0; // disable int 0 for all keys sysIsrEvent |= KEY_ISR_FLAG; } //---------------------------------------------------------------------------------------------------------------------------------------- // Function Name : ToBCD(void) // Function description : //---------------------------------------------------------------------------------------------------------------------------------------- static unsigned char ToBCD(unsigned char u8Data) { if (u8Data > 9) // do BCD conversion only if the value is > 9 { u8Data = ((u8Data / 10) << 4) +(u8Data % 10); // Minute conversion } return u8Data; } //---------------------------------------------------------------------------------------------------------------------------------------- // Function Name : SetNextAlarm(void) // Function description : This fucntion is used for setting up the alarm for next wake up. // LoRa-There is no aspect of time synchronization so no need to set alarm. Rather when the system is told to power down // then that function will set the Atmel RTC to wake the system in the defined time period, 5min typically. //---------------------------------------------------------------------------------------------------------------------------------------- uint8_t SetNextAlarm(void) { uint8_t u8WUState, CurTimeSec,CurTimeMin; //unsigned char cAlmSec = 0, cAlmMin = 0, cRefRate = 0, //cCurMin = GetCurrentTimeParameter(1); // get current minutes will be 0-59 ReadTime(); // load cTxBuffer with sec min hour ClearWDT(); u8WUState = WAKE_STATE_NORMAL; // default Normal - main will execute naturally. This flag will bypass all special cases. // for now just set alarm in future CurTimeSec = BCDToHex(cTxBuffer[0]); CurTimeMin =BCDToHex(cTxBuffer[1]); CurTimeSec += WPT_SLEEP_TIME%60; CurTimeMin += (uint8_t)((uint16_t)WPT_SLEEP_TIME / 60); if(CurTimeSec>=60) { CurTimeSec = CurTimeSec%60; if(CurTimeMin >= 59) CurTimeMin %= 59; else CurTimeMin++; } if(CurTimeMin>=60) CurTimeMin %= 60; // Next Wake State variables are setup here so we dont have to check the curr min at the start of main. // worried if we miss the Tx minute we were looking for, we may drop packest. Doing it here we ensure // If we were going to wake and Tx at the next sample, this flag will gauratee we Tx reguarless of current minute when we wake next time. // cSeconds = ToBCD(CurTimeSec) ; // Set MSB bit for Alarm indication cMinutes = ToBCD(CurTimeMin) ; // Set MSB Bit for Alarm indication #ifdef ENABLE_DEBUG_OUT printf("Current time %02x:%02x ",cTxBuffer[1],cTxBuffer[0]); printf("Alarm time %02x:%02x\r\n",cMinutes,cSeconds); #endif cSeconds |= 0X80; cMinutes |= 0x80; SetRTCAlarm(); // Setup the RTC chip for next alarm return u8WUState; } //---------------------------------------------------------------------------------------------------------------------------------------- // Func: ISR(IRQ_LORA_MODULE_RXD) // // Desc: Called when atmel is sleeping and there is radio module tx activity that // wakes us up // // Input: // // Output: //---------------------------------------------------------------------------------------------------------------------------------------- ISR(IRQ_LORA_MODULE_RXD) { //DEBUG_TP5_HIGH; // disable future interrupts PORTD_INT0MASK = 0; //DEBUG_TP5_LOW; } //---------------------------------------------------------------------------------------------------------------------------------------- // Func: SleepXmegaNmS() // Desc: Puts Atmel to power_save mode for a specific number of ms. Atmel RTC us used to // generate timer interrupt at the specified time. This is used during the module TX time. // Needs to support the maximum TX time of 2S for 868 DR0. // clock source will be switched to // // Input: uint16_t number of mS to sleep // // Output: //---------------------------------------------------------------------------------------------------------------------------------------- void SleepXmegaNmS(uint16_t SleepNmS) { // Modify RTC count register for the number of ms needed for delay. RTC is being // clocked at 32KHz so period needs to be SleepNmS * 32 // RTC_CTRL = 0; // Stop clock RTC.CNT =0; while(RTC.STATUS); RTC.PER = SleepNmS*32; while(RTC.STATUS); RTC.COMP = 100; RTC_INTCTRL = 0x02; // overflow med level // Prescale = 1 and enable RTC_CTRL = 0x01; // prescale of 1 and enable clock cPowerControlStatus |= MASK_LOW_POWER_WAIT_TIME; // now go to sleep set_sleep_mode(SLEEP_MODE_PWR_SAVE);//SLEEP_MODE_PWR_SAVE);// SLEEP_MODE_PWR_DOWN); sleep_enable(); sleep_cpu(); // // code sleeps here, wakes up with ISR from key press or RTC IRQ // sleep_disable(); // Put RTC back to operational state // RTC_CTRL = 0; // Stop clock RTC.CNT =0; while(RTC.STATUS); RTC.PER = 32; while(RTC.STATUS); RTC_CTRL = 0x01; // prescale of 1 and enable clock cPowerControlStatus &= ~MASK_LOW_POWER_WAIT_TIME; } //---------------------------------------------------------------------------------------------------------------------------------------- // Func: SleepXmegaWakeRmUartRx(uint16_t SleepTime) // Desc: Puts Atmel to power_save mode only to awake with Rx Interrupt from radio module when it exits sleep just prior to RX1. // This is intended to be used during radio Tx and wait for Rx1/2 windows. Configures // pd2 (radio module RX input) for ISR. As a safety measure the RTC is loaded with the delay time and will also wake // the processor in the event that the radio fails to wake up. // // Input: // SleepTime-Lenght of time that the radio module was requested to sleep. RTC should be just a bit longer. Time is based // on the time between end of TX and RX1. // // Output: //---------------------------------------------------------------------------------------------------------------------------------------- void SleepXmegaWakeRmUartRx(uint16_t SleepTime) { uint8_t UartIntState; // wait for all radio messages to complete if(UartTxBuf.Count) { while(UartTxBuf.Count); _delay_us(200); // wait for last char to go out at 57.6k } USARTD0_CTRLB &= ~0x10; // disable usart rx functions so we can control IO pins // Set falling edge interrupt PORTD_PIN2CTRL = 0x02; // Stop RTC // Keep RTC running as backup RTC_CTRL = 0x00; // disable // disable any uart tx/rx interrupts on usartd0 // Casch current settings UartIntState = USARTD0_CTRLA; USARTD0_CTRLA = 0; // Enable rx interrupt on pd2 PORTD_INTFLAGS = 0x01; // clear flags PORTD_INT0MASK = LORA_MODULE_RXD_PIN; // Enable portD int 0 PORTD_INTCTRL = 0x01; // Keep RTC running (can possibly turn off) // Module doesnt respond with 'ok' after programmed sleep interval which hangs this function // Use RTC to set interrupt longer than sleep time to recover // RTC is clocked at 1/31768 = 30.5uS. Max RTC period is 65535 = 2S. We only need 1 second between // end of TX and RX1 so should be ok RTC.CNT =0; while(RTC.STATUS); RTC.PER = 0x9999;// 1.4S equiv while(RTC.STATUS); RTC_CTRL = 0x01; // Enable cPowerControlStatus |= MASK_LOW_POWER_WAIT_TIME; cPowerControlStatus &= ~MASK_LOW_POWER_RTC_EVENT; sysIsrEvent = 0; // clear all events // now go to sleep DEBUG_TP1_HIGH; set_sleep_mode(SLEEP_MODE_PWR_SAVE);//SLEEP_MODE_PWR_SAVE);// SLEEP_MODE_PWR_DOWN); sleep_enable(); sleep_cpu(); // // code sleeps here, wakes up with ISR from key press or RTC IRQ // sleep_disable(); PORTD_INTCTRL = 0x00; // Disable interrupts on DEBUG_TP1_LOW; cPowerControlStatus &= ~MASK_LOW_POWER_WAIT_TIME; if(cPowerControlStatus & MASK_LOW_POWER_RTC_EVENT) { DEBUG_TP5_HIGH; // This means that the RX "OK" from radio module didnt wake us up but rather the RTC saved us // Try to recover radio module printf("module rx recovery\r\n"); WakeRFModule(); DEBUG_TP5_LOW; } RTC_CTRL = 0x01; // enable with prescale of 1 USARTD0_CTRLA = UartIntState; USARTD0_CTRLB |= 0x10; // enable usart both rx // set tx as output PORTD_DIRSET = LORA_MODULE_TXD_PIN; //// Put back RTC timer to 1 ms tick RTC_CTRL = 0x00; // Enable RTC.CNT =0; while(RTC.STATUS); RTC.PER = 32;// back to 1 mS while(RTC.STATUS); RTC_CTRL = 0x01; // Enable } //---------------------------------------------------------------------------------------------------------------------------------------- // Func: Atmel_Init() // Desc: Configure Atmel hardware for operation // // Input: // // Output: //---------------------------------------------------------------------------------------------------------------------------------------- void Atmel_Init(void) { // configure part to run on 2MHz osc with 8x pll up to 16MHz due to 2.7V requirement of 32MHz // Configure PLL for 2MHz source, 8x OSC.PLLCTRL = 0x08; // 8x OSC.CTRL = 0x15; // enable pll and keep 2mhz/32k enabled until pll is stable enough to use as sys clock while((OSC_STATUS & 0x10)==0); CCP = 0xd8; // unlock protection CLK.CTRL = 0x04; // Select pll as system clock // used for 32mhz version //// Configure the part to run full out when powered on //OSC.CTRL |= 0x07; // Enable 32MHz,32k, osc //while((OSC_STATUS & 0x2)== 0); // wait for 32mhz osc to stabilize // used for 32mhz version //TP1 debug out PORTA_DIRSET = DEBUG_PIN_TP1; DEBUG_TP1_LOW; //TP2 debug out REPLACED WITH KEY ISR //PORTE_DIRSET = DEBUG_PIN_TP2; //DEBUG_TP2_LOW; //TP5 debug out PORTE_DIRSET = DEBUG_PIN_TP5; DEBUG_TP5_LOW; // turn off clocks to any unused xmega peripherals PR_PRGEN |= 0x02; // evsys //PR_PRPA |= 0x03; // dac,adc,ac PR_PRPC |= 0x07; // hires,tc5,tc4 PR_PRPD |= 0x07; // Enable WDT //===========TO DO=================== // Background timer // RTC used to generate 1ms interrupts TimerInit(); // I2C interface to FRAM/Humidity/RTC/LCD/BLE sensor // SCL pe1 // SDA pe0 I2CHwInit(); // LCD SPI interface related // pc4 = str(nSS) controlled by user // pc5 = MOSI // pc6 = MISO // pc7 = clk (ifclock is 16MHZ, max clock speed is 4mhz) #ifdef ENABLE_LCD_SPI InitSpi(); #endif // Enable I2C pullups on pd7 PORTD_OUTCLR = I2C_PULLUP_POWER; // Default to not powered PORTD_DIRSET = I2C_PULLUP_POWER; //b7 as output // UART // pD3 TX To 2903 // pD2 RX from 2903 // pC7 TX Debug out // pC6 RX Debug in ///Used as clear bind input UartInit(); UartFlushRxBuffer(); ///////////////////////////// // PORTA PIN CONFIG ///////////////////////////// // porta4:7 used for motor interface, set as output PORTA_DIRSET = MASK_MOTOR_0 | MASK_MOTOR_1 | MASK_MOTOR_2 | MASK_MOTOR_3; // pa2, nirq_rtc. Need to add pullup to input from RTC. IRQ active low so enable falling edge ISR PORTA_PIN2CTRL = 0x1a; //x 0 01,1 010 // PA0 is analog in so disable digital input PORTA_PIN0CTRL |= 0x07; // Configure ISC=111, input disable ///////////////////////////// // PORTB PIN CONFIG ///////////////////////////// // PB3 2903 nReset PORTB_OUTSET = LORA_MODULE_RESET_PIN; // 1 = default state PORTB_DIRSET = LORA_MODULE_RESET_PIN; // Set pulldown for keys on pb0/1/2 PORTB_PIN0CTRL = 0x10; // Pulldown PORTB_PIN1CTRL = 0X10; PORTB_PIN2CTRL = 0x10; // Set pulldown for key isr input pin pe2 PORTE_PIN2CTRL = 0x10; // Pulldown ///////////////////////////// // PORTC PIN CONFIG ///////////////////////////// // PC0 Active high output to enable 5V switcher. Keep off initially PORTC_OUTCLR = V5_ENABLE_PIN; // 0 = default state off PORTC_DIRSET = V5_ENABLE_PIN; // Pc1 power control to switched Vbat to RN2903/humidity/fram, // controls gate of p type mosfet with external pullup to vbat // drive as OC or wired-and. out = 0 pull low for on state, out = 1 , float for off state PORTC_PIN1CTRL = 0x28; // opc=101 for wired-and VBAT_SW_POWER_OFF; PORTC_DIRSET = VBAT_SW_POWER_PIN; // pc4/5/6/7 not used so pull to ground PORTC_PIN4CTRL = 0x10; // Pulldown PORTC_PIN5CTRL = 0x10; // Pulldown PORTC_PIN6CTRL = 0x10; // Pulldown PORTC_PIN7CTRL = 0x10; // Pulldown #ifndef ENABLE_LCD_SPI // pc4/5/6/7 not used so pull to ground PORTC_PIN4CTRL = 0x10; // Pulldown //PORTC_PIN5CTRL = 0x10; // Pulldown This pin is driven by SDA so not floating PORTC_PIN6CTRL = 0x10; // Pulldown //PORTC_PIN7CTRL = 0x10; // Pulldown This poin driven by SCL so not floating #endif ///////////////////////////// // PORTD PIN CONFIG ///////////////////////////// // pd6 provides power to external RTC. PORTD_OUTSET = RTC_POWER_PIN; // Default with RTC always powered on PORTD_DIRSET = RTC_POWER_PIN; // Optical sensor power pin pa2 PORTD_OUTCLR = OPTO_POWER_PIN; // Default off PORTD_DIRSET = OPTO_POWER_PIN; PMIC_CTRL = 0x07; // enable hi/med/low interrupts sei(); } //---------------------------------------------------------------------------------------------------------------------------------------- // Func: main() // Desc: WPT Main //---------------------------------------------------------------------------------------------------------------------------------------- void main() { uint8_t u8TmpVal = 0; uint8_t ResetStatus; uint8_t TempBuf8[8]; stdout = &mystdout; Atmel_Init(); PowerUp(); InitFuelGauge(); printf("Power on Reset\r\n"); FRamRead((uint8_t *)&FramImage,sizeof(FramImage),0); //Read contents of FRAM into RAM // Look for Inc key pressed at por to clear joined state if (ReadAllKey() & MASK_INR_SW) { printf("Clear join state\r\n"); FramImage.SystemState =0; FramImage.HwEuiValid = 0; FramImage.HwEui[4] = 0; FramImage.HwEui[5] = 0; FramImage.HwEui[6] = 0; FramImage.HwEui[7] = 0; // Trigger OTAA bind LoRaModuleStatus |= LORA_STATE_OTAA_REQ; } // Look for OVR switch to reset battery count if (ReadAllKey() & MASK_OVR_SW) { printf("Clear Charge count\r\n"); FramImage.TotalChargeCounts =0; } // Radio module was powered off even if we were just reset so need to get module into known state. // check fram to see if bind parameters are valid after we reset the module ResetStatus = RfModuleInit(); // Issue hw reset and check printf("rm reset"); if(ResetStatus) printf(" Success\r\n"); else printf(" Fail\r\n"); #ifdef RF_TEST RunRfTest(); #endif // Display FW version at startup cDigitsValue = SOFTWARE_VERSION; UpdateValue(); DelaymS(1000); cDigitsValue = SOFTWARE_VERSION_DP; UpdateValue(); DelaymS(1000); if(FramImage.SystemState == 1) { // we jsut powered up and are bound so just need to get module into a bound state printf("Refresh bind state\r\n"); ProtocolInit(); } else { // Refresh HWEUI as well GetHwEui((uint8_t *)TempBuf8); // Read from module // Update FRAM for (u8TmpVal=0;u8TmpVal<8;u8TmpVal++) { FramImage.HwEui[u8TmpVal] = TempBuf8[u8TmpVal]; } FramImage.HwEuiValid = EEPROM_SIGNATURE; FramWrite((uint8_t *)&FramImage.HwEuiValid,FRAM_HWEUI_VALID_SIZE+FRAM_HWEUI_SIZE,FRAM_HWEUI_VALID_OFFSET); // Update FRAM } ConfigRHSensor(); InitialiseRTC(); printf("Factory Default\r\n"); // ************************************************************************************************ // // ************************************ Start of Factory Default ********************************** // // ************************************************************************************************ // memset(&g_strctPSoCRAMOverlay, 0, sizeof(NVRAM_DATA_OVERLAY)); // Clear PSoC RAM - this will force clearing of RTC ram when we save data at end of sample memcpy(&g_strctPSoCRAMOverlay, &g_strctPSOC_RAM_DEFAULT, sizeof(g_strctPSOC_RAM_DEFAULT)); // Load factory defaults #ifndef RANGE_TEST_UNIT SetMotorDirty(); // Done ASAP in case we brownout and need to re-detect the need to factory default printf("span %d\r\n",PSOC_RAM_DATA.u8TempActiveSpanDegF); // //// PSOC_RAM_DATA.strctPriPath = strctPath; // Recall the primary path information. This will either be all 0's (normal default) or the last know primary path cKeyboardValue = 0; // Clear these out so a button press will not register when first powering on cCurrentStatus = 0; iUserSwitchStatus = 0; // //ClearWDT(); printf("RTC\r\n"); DefaultRTCReg(); // Clear all RTC unser configurable registers to zero InitialiseRTC(); // if device is newly powered up, Reprogram it with 1/1/2008 00:00:00 // DisableAutoCalBuf(); if (PSOC_RAM_DATA.u8GMDirty == DIRTY_FLAG_MDIRTY) // If MDirty here or coming out of EOL, must mean we need to home the motor { BlankAllSegmentsWithDDRAM(); // Update display and show batt icon if about to home motor. Dont want delay with unknown LCD state DisplayBattery(PSOC_RAM_DATA.u8BattLevel); DisplayChar(CHAR_D, CHAR_Y); // Display "DY" on LCD - This was done inside DoMotorHoming(), pulled out to reuse function elsewhere; "Dy" happens later (not in DoMotorHoming function) PSOC_RAM_DATA.u16CurMotorPosSteps = (70*STEPSPERDEGF); // force motor to move one direction in DoMotorHoming DoMotorHoming(); // Home motor position PutBackDisplay(); #if (DB_WPT_ENABLED == 0) MoveMotor((DEFAULTTEMPSETPOINT*STEPSPERDEGF), NO_SCHEDULER); // If Conventional, move motor to 72F after homing whether DA/RA. If DB, the logic below will properly move #endif } #endif // ************************************************************************************************ // // ************************************ End of Factory Default ************************************ // // ************************************************************************************************ // // xro fix // Implement WTD //OSC_CR0 |= 0x18; // WatchDogTimer set to 3sec. cPowerControlStatus = 0; g_bExitSetpointChangeFromOverride = 0; ClearWDT(); cWakeUpSource = WAKE_UP_RTC_ALARM; // Source of wake up cTxBeforeSleep = 0; // Initialize EEPROM from FRAM image// E2PROM_E2Read((uint8_t*)&u8EEProm_Data,E2PROM_SIZE); iNodeID = (u8EEProm_Data[E2PROM_NODE_ID_MSB]<<8); iNodeID += u8EEProm_Data[E2PROM_NODE_ID_LSB]; iNewNodeID = iNodeID; iNetworkID = u8EEProm_Data[E2PROM_NETWORK_ID]; iNewNetworkID = iNetworkID; // ------------- Load EEPROM ------------- // for (u8TmpVal=0; u8TmpVal DVM_OVERRIDE_MODE)) { PSOC_RAM_DATA.u8DeviceMode = DVM_OPERATIONAL_MODE; } //if (cWakeUpSource == WAKE_UP_KEY_PRESS) // Make sure we do not suppress any DB or neccassary Tx during a button press. //{ //PSOC_RAM_DATA.u8WakeUpState = WAKE_STATE_NORMAL; // We want DB to be processed on button press so user can jump start the regulation. //} PSOC_RAM_DATA.u8WakeUpState = WAKE_STATE_NORMAL; cPowerControlStatus &= ~MASK_MEASUREMENT_DONE; // Measurement not yet done //printf("MAIN_BattLvl: %d\r\n", PSOC_RAM_DATA.u8BattLevel); // In-commissioning display // Clear E0 msg for next 15-min interval (NORMAL -> SUPPRESS_TX -> SUPPRESS_TX) or // after a WAKE_UP_KEY_PRESS which also ends up with WAKE_STATE_NORMAL (also in main loop) if ((PSOC_RAM_DATA.u8WakeUpState == WAKE_STATE_NORMAL) && (PSOC_RAM_DATA.u8ErrCode < 0xE2)) { PSOC_RAM_DATA.u8ErrCode = 0; // Clear E0 and E1 msg within 15 mins } i1MsCounter = 0; i10MsCounter = 0; cTimerStatus = 0; iIdleSemaphore = 0; ExtendMinimumExecutionTime(); printf("Starting Main\r\n"); // WPT 900 architecture is always powered on and simply sleeps during wake cycles while (1) { //CycleCounter++; // debug DisplayBattery(GetBatteryLevel()); #ifndef RANGE_TEST_UNIT DispAmbTemp();// Trigger reading and display the ambient temperature. Ambient calculated here used extensively for deadband, etc. PSOC_RAM_DATA.u8Humidity = TrigReadHumidity(); #endif if (PSOC_RAM_DATA.u8WakeUpState != WAKE_STATE_SUPRESS_DB) { //printf("db control\r\n"); DeadbandControl(); } while(UartDebugBuf.Count); //TaskScheduler handles all wpt + radio tasks // TaskScheduler exits when no sleep is ok and all tasks have completed // Reduct system clock rate to save power and match MIPS of psoc TaskScheduler(); // Display "--" instead DisplayChar(CHAR_DASH, CHAR_DASH); DisplayCF(0); // 'both off DelaymS(500); //============================================ // do htasks here as we removed MASK_REPORT_BOUNDARY timer flag generation #ifndef RANGE_TEST_UNIT ReadPressure(); #endif //DEBUG_TP5_LOW; if (!(iIdleSemaphore & MASK_MTASK)) { //printf("MOTOR OFF\r\n"); TurnMotorOFF(); OPTO_POWER_OFF; //printf("---- TurnMotorOff in HTask----\r\n"); } //============================================ //CCP = 0xd8; // unlock protection //CLK.PSCTRL = 0x00; // cpu clock full speed. // See if otaa bind request was issued if(LoRaModuleStatus & LORA_STATE_OTAA_REQ) { //DEBUG_TP1_HIGH; ForceJoin(iNetworkID); LoRaModuleStatus &= ~LORA_STATE_OTAA_REQ; //DEBUG_TP1_LOW; }else if(FramImage.SystemState == 1) { SetupDataSet1(); // All wpt tasks are complete, now do lora transaction, process any backchannel data and update motor position if necessary //DEBUG_TP5_HIGH; WakeRFModule(); // wait for any debug messages to complete before starting tx as cpu will be put to sleep while(UartDebugBuf.Count); SendFrame(); #ifdef RANGE_TEST_UNIT // scale and display RSSI on LCD if(Rssi == 255) cDigitsValue = 0x00;// PSOC_RAM_DATA.u8ErrCode; // Error code else if(Rssi <= RSSI_5_THRESH) cDigitsValue = 0x05;// PSOC_RAM_DATA.u8ErrCode; // Error code else if(Rssi <= RSSI_4_THRESH) cDigitsValue = 0x04;// PSOC_RAM_DATA.u8ErrCode; // Error code else if(Rssi <= RSSI_3_THRESH) cDigitsValue = 0x03;// PSOC_RAM_DATA.u8ErrCode; // Error code else if(Rssi <= RSSI_2_THRESH) cDigitsValue = 0x02;// PSOC_RAM_DATA.u8ErrCode; // Error code else cDigitsValue = 0x01;// PSOC_RAM_DATA.u8ErrCode; // Error code printf("Digvalue=%d\r\n",cDigitsValue); UpdateValue(); // Dispaly xx on LCD #endif }else { cDigitsValue = 0xE1;// PSOC_RAM_DATA.u8ErrCode; // Error code UpdateValue(); } //DEBUG_TP5_LOW; // Now go to sleep and awake on keypress or RTC PSOC_RAM_DATA.u8WakeUpState = WAKE_STATE_NORMAL; SleepTasks(); //////////////////////////////// //////////////////////////////// //////////////////////////////// // SLEEP /////////////////////////////// //////////////////////////////// //////////////////////////////// WakeupTasks(); // Disable wakeup sources RTC/Key PORTE_INT0MASK = 0; PORTA_INT0MASK = 0; // Reset all variables that were expected with restart of psoc i10MsCounter = 0; cPowerControlStatus &= ~MASK_MEASUREMENT_DONE; // measurement not yet done i1MsCounter = 0; cRadioStatus &= ~MASK_MESSAGE_SENT; // send radio message at least once if (sysIsrEvent & KEY_ISR_FLAG) // Check for any key press { NumberTxSamples = 0; // Reset counter DetectKey(); // Identify the key combinations cWakeUpSource = WAKE_UP_KEY_PRESS; cTxBeforeSleep = 1; // enable data transmission on key-press } else if (sysIsrEvent & RTC_ISR_FLAG) // Check whether wakeup due to RTC { cWakeUpSource = WAKE_UP_RTC_ALARM; } sysIsrEvent = 0; } }