/* Include Files */ #include "KTag.h" #define FIRE_CONTROL_REGISTER__IR_OFF 0b00000 #define FIRE_CONTROL_REGISTER__IR_ON_MODULATED_LOW_POWER 0b00011 #define FIRE_CONTROL_REGISTER__IR_ON_MODULATED_HIGH_POWER 0b00101 #define FIRE_CONTROL_REGISTER__IR_ON_MODULATED_MAX_POWER 0b11111 #define FIRE_CONTROL_REGISTER__IR_ON_UNMODULATED_LOW_POWER 0b00010 #define FIRE_CONTROL_REGISTER__IR_ON_UNMODULATED_HIGH_POWER 0b00100 #define FIRE_CONTROL_REGISTER__IR_ON_UNMODULATED_MAX_POWER 0b00110 #define TRIGGER_STATUS_REGISTER__NO_ACTION 0x00 #define TRIGGER_STATUS_REGISTER__TRIGGER_PULLED 0x01 #define TRIGGER_STATUS_REGISTER__TRIGGER_RELEASED 0x02 void Trigger_Interrupt_ISR(); void Bit_Stream_Timer_ISR(); TimedPulseTrain_T * Shot_Buffer; TagPacket_T Shot_Packet; TaskHandle_t Fire_Control_Task_Handle; static TimedPulseTrain_T * Active_Pulse_Train = NULL; static uint8_t Active_Bitstream_Index = 0; static TickType_t TicksAtTriggerPress; static inline void Initiate_Pulse_Train(TimedPulseTrain_T * pulsetrain) { Bit_Stream_Timer_Disable(); Active_Pulse_Train = pulsetrain; Active_Bitstream_Index = 0; if (Active_Pulse_Train->bitstream[Active_Bitstream_Index].symbol == MARK) { Fire_Control_Register_Write(FIRE_CONTROL_REGISTER__IR_ON_MODULATED_MAX_POWER); } else { Fire_Control_Register_Write(FIRE_CONTROL_REGISTER__IR_OFF); } Bit_Stream_Timer_SetPeriod(Active_Pulse_Train->bitstream[0].duration); Bit_Stream_Timer_SetCounter(0); Active_Bitstream_Index++; Bit_Stream_Timer_Enable(); Bit_Stream_Timer_TriggerStart(); } static inline void Next_Bit(void) { static BaseType_t xHigherPriorityTaskWoken = pdFALSE; Bit_Stream_Timer_Disable(); if (Active_Pulse_Train->bitstream[Active_Bitstream_Index].duration != LAST_PULSE) { if (Active_Pulse_Train->bitstream[Active_Bitstream_Index].symbol == MARK) { Fire_Control_Register_Write(FIRE_CONTROL_REGISTER__IR_ON_MODULATED_MAX_POWER); } else { Fire_Control_Register_Write(FIRE_CONTROL_REGISTER__IR_OFF); } if (Active_Bitstream_Index < ((2*MAX_PULSES) - 2)) { Bit_Stream_Timer_SetPeriod(Active_Pulse_Train->bitstream[Active_Bitstream_Index].duration); Bit_Stream_Timer_SetCounter(0); Active_Bitstream_Index++; Bit_Stream_Timer_Enable(); Bit_Stream_Timer_TriggerStart(); } else { // The bitstream is too long! // Turn the IR Emitter off, and wait a long time. Fire_Control_Register_Write(FIRE_CONTROL_REGISTER__IR_OFF); xSemaphoreGiveFromISR(NeoPixels_Semaphore, &xHigherPriorityTaskWoken); Bit_Stream_Timer_SetPeriod(UINT16_MAX); Bit_Stream_Timer_SetCounter(0); Active_Pulse_Train = NULL; Bit_Stream_Timer_Enable(); Bit_Stream_Timer_TriggerStart(); } } else { // Turn the IR Emitter off, and wait a long time. Fire_Control_Register_Write(FIRE_CONTROL_REGISTER__IR_OFF); xSemaphoreGiveFromISR(NeoPixels_Semaphore, &xHigherPriorityTaskWoken); Bit_Stream_Timer_SetPeriod(UINT16_MAX); Bit_Stream_Timer_SetCounter(0); Active_Pulse_Train = NULL; Bit_Stream_Timer_Enable(); Bit_Stream_Timer_TriggerStart(); } portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } void Init_Fire_Control(void) { // Register and enable the ISRs. Cy_SysInt_Init(&Trigger_Interrupt_cfg, Trigger_Interrupt_ISR); Cy_SysInt_Init(&Bit_Stream_Timer_Interrupt_cfg, Bit_Stream_Timer_ISR); NVIC_EnableIRQ(Trigger_Interrupt_cfg.intrSrc); NVIC_EnableIRQ(Bit_Stream_Timer_Interrupt_cfg.intrSrc); // Initialize the hardware. Bit_Stream_Timer_Clock_Enable(); Bit_Stream_Timer_Init(&Bit_Stream_Timer_config); Bit_Stream_Timer_SetPeriod(2); Bit_Stream_Timer_Start(); SW_CLK_Enable(); PWM_IR_Modulation_Start(); Fire_Control_Register_Write(FIRE_CONTROL_REGISTER__IR_OFF); } void Fire_Control_Task(void * pvParameters) { while (true) { vTaskDelay(1000 / portTICK_PERIOD_MS); } } SystemKResult_T Prepare_Tag() { Shot_Packet.player_ID = NVM_PLAYER_ID; Shot_Packet.team_ID = NVM_TEAM_ID; Weapon_t weapon = GetWeaponFromID(NVM_WEAPON_ID); Shot_Packet.color = (uint32_t)PROTOCOLS_GetColor(weapon.Protocol, Shot_Packet.team_ID, Shot_Packet.player_ID); Shot_Packet.protocol = weapon.Protocol; Shot_Packet.damage = weapon.Damage_Per_Shot; Shot_Buffer = PROTOCOLS_EncodePacket(&Shot_Packet); Fire_Control_Set_Modulation_Frequency(PROTOCOLS_GetModulationFrequency(weapon.Protocol)); return SYSTEMK_RESULT_SUCCESS; } SystemKResult_T Send_Tag() { xSemaphoreTake(NeoPixels_Semaphore, portMAX_DELAY); Initiate_Pulse_Train(Shot_Buffer); KEvent_T tag_sent_event = { .ID = KEVENT_TAG_SENT, .Data = (void *)0x00 }; Post_KEvent(&tag_sent_event); return SYSTEMK_RESULT_SUCCESS; } void Fire_Control_Set_Modulation_Frequency(ModulationFrequency_T freq) { PWM_IR_Modulation_TriggerKill(); if (freq == FREQUENCY_38kHz) { PWM_IR_Modulation_SetPeriod0(314); //PWM_IR_Modulation_SetCompare0(314/2); // 50% Duty Cycle PWM_IR_Modulation_SetCompare0((314 * 3)/10); // 30% Duty Cycle } else // (freq == FREQUENCY_56kHz) { PWM_IR_Modulation_SetPeriod0(213); //PWM_IR_Modulation_SetCompare0(213/2); // 50% Duty Cycle PWM_IR_Modulation_SetCompare0((213 * 3)/10); // 30% Duty Cycle } PWM_IR_Modulation_TriggerStart(); } //! ISR for the trigger input. void Trigger_Interrupt_ISR() { portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; // Clear the interrupt. NVIC_ClearPendingIRQ(Trigger_Interrupt_cfg.intrSrc); // Read the trigger register to know if this was a pull or a release. uint8_t trigger_status = Trigger_Status_Reg_Read(); if ((trigger_status & TRIGGER_STATUS_REGISTER__TRIGGER_PULLED) == TRIGGER_STATUS_REGISTER__TRIGGER_PULLED) { TicksAtTriggerPress = xTaskGetTickCountFromISR(); KEvent_T switch_event = {.ID = KEVENT_CENTER_SWITCH_PRESSED, .Data = NULL}; Post_KEvent_From_ISR(&switch_event, &xHigherPriorityTaskWoken); } else if ((trigger_status & TRIGGER_STATUS_REGISTER__TRIGGER_RELEASED) == TRIGGER_STATUS_REGISTER__TRIGGER_RELEASED) { uint32_t triggerPressDurationInms = pdTICKS_TO_MS(xTaskGetTickCountFromISR() - TicksAtTriggerPress); KEvent_T switch_event = {.ID = KEVENT_CENTER_SWITCH_RELEASED, .Data = (void *) triggerPressDurationInms}; Post_KEvent_From_ISR(&switch_event, &xHigherPriorityTaskWoken); } else { // What happened!!? } // If an event was enqueued above, a context switch might be required. // xHigherPriorityTaskWoken was initialized to pdFALSE on interrupt entry. If calling // xSemaphoreGiveFromISR() caused a task to unblock, and the unblocked task has a // priority equal to or higher than the currently running task (the task that was // interrupted by this ISR), then xHigherPriorityTaskWoken will have been set to pdTRUE // and portEND_SWITCHING_ISR() will request a context switch to the newly unblocked task. portEND_SWITCHING_ISR( xHigherPriorityTaskWoken ); } void Bit_Stream_Timer_ISR() { // Get all the enabled pending interrupts... uint32_t source = Bit_Stream_Timer_GetInterruptStatusMasked(); // ...and clear them. Bit_Stream_Timer_ClearInterrupt(source); if (Active_Pulse_Train != NULL) { Next_Bit(); } }