223 lines
7.6 KiB
C
223 lines
7.6 KiB
C
/* 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();
|
|
}
|
|
}
|