Created
August 9, 2019 01:25
-
-
Save zpv/8d32a7c1491209f3b920a5b919aa05ee to your computer and use it in GitHub Desktop.
DEFCON27 badge.c
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
/* | |
DEFCON 27 Official Badge (2019) | |
Author: Joe Grand, Grand Idea Studio [@joegrand] aka Kingpin | |
Program Description: | |
This program contains the firmware for the DEFCON 27 official badge. | |
Complete design documentation can be found at: | |
http://www.grandideastudio.com/portfolio/defcon-27-badge | |
*/ | |
// Portions of this code are subject to the following license: | |
/* | |
* The Clear BSD License | |
* Copyright 2016-2018 NXP Semiconductor, Inc. | |
* All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without modification, | |
* are permitted provided that the following conditions are met: | |
* | |
* o Redistributions of source code must retain the above copyright notice, this list | |
* of conditions and the following disclaimer. | |
* | |
* o Redistributions in binary form must reproduce the above copyright notice, this | |
* list of conditions and the following disclaimer in the documentation and/or | |
* other materials provided with the distribution. | |
* | |
* o Neither the name of NXP Semiconductor, Inc. nor the names of its | |
* contributors may be used to endorse or promote products derived from this | |
* software without specific prior written permission. | |
* | |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR | |
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | |
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
*/ | |
/** | |
* @file dc27_badge.c | |
* @brief Application entry point. | |
*/ | |
#include <stdio.h> | |
#include "board.h" | |
#include "peripherals.h" | |
#include "pin_mux.h" | |
#include "clock_config.h" | |
#include "MKL27Z644.h" | |
#include "fsl_debug_console.h" | |
#include "fsl_smc.h" | |
#include "fsl_flash.h" | |
#include "LPBroadcast_NXH_DC27.eep.h" // Pre-compiled firmware blob for NXH2261 (loaded during power-up) | |
/*************************************************************************** | |
**************************** Definitions ********************************** | |
***************************************************************************/ | |
#define __BADGE_TYPE HUMAN | |
//#define __BADGE_TYPE GOON | |
//#define __BADGE_TYPE SPEAKER | |
//#define __BADGE_TYPE VENDOR | |
//#define __BADGE_TYPE PRESS | |
//#define __BADGE_TYPE VILLAGE | |
//#define __BADGE_TYPE CONTEST | |
//#define __BADGE_TYPE ARTIST | |
//#define __BADGE_TYPE CFP | |
//#define __BADGE_TYPE UBER | |
#undef __BADGE_MAGIC | |
//#define __BADGE_MAGIC | |
#define LOW 0U | |
#define HIGH 1U | |
// LED animation | |
#define LED_HEARTBEAT_FADE_DELAY 6 // Time (ms) per brightness setting ramp up/down | |
#define LED_HEARTBEAT_WAIT_DELAY 1000 // Time (ms) to sleep at LED maximum brightness | |
#define LED_SPARKLE_FADE_DELAY 2 // Time (ms) per brightness setting ramp up/down | |
#define LED_SPARKLE_ON_DELAY 350 // Time (ms) to remain on at LED maximum brightness | |
#define LED_SPARKLE_WAIT_DELAY 1500 // Time (ms) to sleep between LED updates | |
// Bit masks for badge quest flags | |
#define FLAG_0_MASK 0x01 // Any Valid Communication | |
#define FLAG_1_MASK 0x02 // Talk/Speaker | |
#define FLAG_2_MASK 0x04 // Village | |
#define FLAG_3_MASK 0x08 // Contest & Events | |
#define FLAG_4_MASK 0x10 // Arts & Entertainment | |
#define FLAG_5_MASK 0x20 // Parties | |
#define FLAG_6_MASK 0x40 // Group Chat | |
#define FLAG_ALL_MASK 0x7F | |
#define GROUP_ALL_MASK 0x3F | |
#define CONSOLE_RCVBUF_SIZE 20 // Number of bytes in debug console (interactive mode) receive buffer | |
#define ART_DEFAULT "DC27" // Default string for ASCII art generator | |
#define NVM_DATA_SIZE 4U // Number of bytes to store in KL27 Flash | |
#define SECTOR_INDEX_FROM_END 1U // Location of KL27 Flash sector to use for game data storage | |
// CLKOUT | |
#define SIM_CLKOUT_SEL_OSCERCLK_CLK 6U // CLKOUT pin clock select: OSCERCLK (from clock_config.c) | |
// LPUART0 (to/from NXH2261) | |
#define LPUART0_RING_BUFFER_SIZE 2048U // Number of bytes in ring buffer for receiving data from NXH | |
// I2C | |
#define I2C_NXH2261_ADDR 0x10 | |
#define I2C_LP5569_ADDR 0x32 | |
// NXH2261 | |
#define NXH2261_DATA_PACKET_SIZE 18U // header + 16 user bytes + footer | |
#define NXH2261_MAX_CHUNK_SIZE 128U | |
#define NXH2261_CMD_GET_VERSION 0x0F80 // Get device version information | |
#define NXH2261_CMD_PREVENT_BOOT 0x0F16 // Aborts automatic boot procedure, puts device into bootloader | |
#define NXH2261_CMD_EEPROM_ENABLE 0x0F18 // Enables the EEPROM (required before any EEPROM manipulation) | |
#define NXH2261_CMD_EEPROM_DISABLE 0x0F19 // Disables the EEPROM | |
#define NXH2261_CMD_EEPROM_UNLOCK 0x0F0C // Unlocks EEPROM memory location for a single write | |
#define NXH2261_CMD_EEPROM_READ 0x0F0B // Read data from EEPROM | |
#define NXH2261_CMD_EEPROM_WRITE 0x0F0A // Write data to EEPROM | |
#define NXH2261_CMD_START_APP 0x0F00 // Executes the application currently in RAM | |
#define NXH2261_CMD_EEPROM_BOOT 0x0F0F // Copies application from EEPROM into RAM and executes | |
// LP5569 | |
#define LP5569_LED_NUM 6U // Number of LEDs per badge | |
#define LED_CONTROL_RED 0x08 // No master fading, exponential adjustment, LED powered by charge pump | |
#define LED_CURRENT_RED 0x0A // 1.0mA | |
#define LED_PWM_RED 0xC0 // 75% duty cycle | |
#define LED_CONTROL_ORANGE 0x08 // No master fading, exponential adjustment, LED powered by charge pump | |
#define LED_CURRENT_ORANGE 0x07 // 0.7mA | |
#define LED_PWM_ORANGE 0xC0 // 75% duty cycle | |
#define LED_CONTROL_GREEN 0x08 // No master fading, exponential adjustment, LED powered by charge pump | |
#define LED_CURRENT_GREEN 0x02 // 0.2mA | |
#define LED_PWM_GREEN 0xC0 // 75% duty cycle | |
#define LED_CONTROL_BLUE 0x08 // No master fading, exponential adjustment, LED powered by charge pump | |
#define LED_CURRENT_BLUE 0x07 // 0.7mA | |
#define LED_PWM_BLUE 0xC0 // 75% duty cycle | |
#define LED_CONTROL_PURPLE 0x08 // No master fading, exponential adjustment, LED powered by charge pump | |
#define LED_CURRENT_PURPLE 0x07 // 0.7mA | |
#define LED_PWM_PURPLE 0xC0 // 75% duty cycle | |
#define LED_CONTROL_WHITE 0x08 // No master fading, exponential adjustment, LED powered by charge pump | |
#define LED_CURRENT_WHITE 0x06 // 0.6mA | |
#define LED_PWM_WHITE 0xC0 // 75% duty cycle | |
// From Section 8.6: Register Maps | |
#define LP5569_REG_CONFIG 0x00 // Configuration Register | |
#define LP5569_REG_LED_ENGINE_CONTROL1 0x01 // Engine Execution Control Register | |
#define LP5569_REG_LED_ENGINE_CONTROL2 0x02 // Engine Operation Mode Register | |
#define LP5569_REG_LED0_CONTROL 0x07 // LED0 Control Register | |
#define LP5569_REG_LED1_CONTROL 0x08 // LED1 Control Register | |
#define LP5569_REG_LED2_CONTROL 0x09 // LED2 Control Register | |
#define LP5569_REG_LED3_CONTROL 0x0A // LED3 Control Register | |
#define LP5569_REG_LED4_CONTROL 0x0B // LED4 Control Register | |
#define LP5569_REG_LED5_CONTROL 0x0C // LED5 Control Register | |
#define LP5569_REG_LED6_CONTROL 0x0D // LED6 Control Register | |
#define LP5569_REG_LED7_CONTROL 0x0E // LED7 Control Register | |
#define LP5569_REG_LED8_CONTROL 0x0F // LED8 Control Register | |
#define LP5569_REG_LED0_PWM 0x16 // LED0 PWM Duty Cycle | |
#define LP5569_REG_LED1_PWM 0x17 // LED1 PWM Duty Cycle | |
#define LP5569_REG_LED2_PWM 0x18 // LED2 PWM Duty Cycle | |
#define LP5569_REG_LED3_PWM 0x19 // LED3 PWM Duty Cycle | |
#define LP5569_REG_LED4_PWM 0x1A // LED4 PWM Duty Cycle | |
#define LP5569_REG_LED5_PWM 0x1B // LED5 PWM Duty Cycle | |
#define LP5569_REG_LED6_PWM 0x1C // LED6 PWM Duty Cycle | |
#define LP5569_REG_LED7_PWM 0x1D // LED7 PWM Duty Cycle | |
#define LP5569_REG_LED8_PWM 0x1E // LED8 PWM Duty Cycle | |
#define LP5569_REG_LED0_CURRENT 0x22 // LED0 Current Control | |
#define LP5569_REG_LED1_CURRENT 0x23 // LED1 Current Control | |
#define LP5569_REG_LED2_CURRENT 0x24 // LED2 Current Control | |
#define LP5569_REG_LED3_CURRENT 0x25 // LED3 Current Control | |
#define LP5569_REG_LED4_CURRENT 0x26 // LED4 Current Control | |
#define LP5569_REG_LED5_CURRENT 0x27 // LED5 Current Control | |
#define LP5569_REG_LED6_CURRENT 0x28 // LED6 Current Control | |
#define LP5569_REG_LED7_CURRENT 0x29 // LED7 Current Control | |
#define LP5569_REG_LED8_CURRENT 0x2A // LED8 Current Control | |
#define LP5569_REG_MISC 0x2F // I2C Charge Pump and Clock Configuration | |
#define LP5569_REG_ENGINE1_PC 0x30 // Engine 1 Program Counter | |
#define LP5569_REG_ENGINE2_PC 0x31 // Engine 2 Program Counter | |
#define LP5569_REG_ENGINE3_PC 0x32 // Engine 3 Program Counter | |
#define LP5569_REG_MISC2 0x33 // Charge Pump and LED Configuration | |
#define LP5569_REG_ENGINE_STATUS 0x3C // Engine 1/2/3 Status | |
#define LP5569_REG_IO_CONTROL 0x3D // TRIG/INT/CLK Configuration | |
#define LP5569_REG_VARIABLE_D 0x3E // Global Variable D | |
#define LP5569_REG_RESET 0x3F // Software Reset | |
#define LP5569_REG_ENGINE1_VARIABLE_A 0x42 // Engine 1 Local Variable A | |
#define LP5569_REG_ENGINE2_VARIABLE_A 0x43 // Engine 2 Local Variable A | |
#define LP5569_REG_ENGINE3_VARIABLE_A 0x44 // Engine 3 Local Variable A | |
#define LP5569_REG_MASTER_FADER1 0x46 // Engine 1 Master Fader | |
#define LP5569_REG_MASTER_FADER2 0x47 // Engine 2 Master Fader | |
#define LP5569_REG_MASTER_FADER3 0x48 // Engine 3 Master Fader | |
#define LP5569_REG_MASTER_FADER_PWM 0x4A // PWM Input Duty Cycle | |
#define LP5569_REG_ENGINE1_PROG_START 0x4B // Engine 1 Program Starting Address | |
#define LP5569_REG_ENGINE2_PROG_START 0x4C // Engine 2 Program Starting Address | |
#define LP5569_REG_ENGINE3_PROG_START 0x4D // Engine 3 Program Starting Address | |
#define LP5569_REG_PROG_MEM_PAGE_SELECT 0x4F // Program Memory Page Select | |
#define LP5569_REG_PROGRAM_MEM_00 0x50 // MSB 0 | |
#define LP5569_REG_PROGRAM_MEM_01 0x51 // LSB 0 | |
#define LP5569_REG_PROGRAM_MEM_02 0x52 // MSB 1 | |
#define LP5569_REG_PROGRAM_MEM_03 0x53 // LSB 1 | |
#define LP5569_REG_PROGRAM_MEM_04 0x54 // MSB 2 | |
#define LP5569_REG_PROGRAM_MEM_05 0x55 // LSB 2 | |
#define LP5569_REG_PROGRAM_MEM_06 0x56 // MSB 3 | |
#define LP5569_REG_PROGRAM_MEM_07 0x57 // LSB 3 | |
#define LP5569_REG_PROGRAM_MEM_08 0x58 // MSB 4 | |
#define LP5569_REG_PROGRAM_MEM_09 0x59 // LSB 4 | |
#define LP5569_REG_PROGRAM_MEM_10 0x5A // MSB 5 | |
#define LP5569_REG_PROGRAM_MEM_11 0x5B // LSB 5 | |
#define LP5569_REG_PROGRAM_MEM_12 0x5C // MSB 6 | |
#define LP5569_REG_PROGRAM_MEM_13 0x5D // LSB 6 | |
#define LP5569_REG_PROGRAM_MEM_14 0x5E // MSB 7 | |
#define LP5569_REG_PROGRAM_MEM_15 0x5F // LSB 7 | |
#define LP5569_REG_PROGRAM_MEM_16 0x60 // MSB 8 | |
#define LP5569_REG_PROGRAM_MEM_17 0x61 // LSB 8 | |
#define LP5569_REG_PROGRAM_MEM_18 0x62 // MSB 9 | |
#define LP5569_REG_PROGRAM_MEM_19 0x63 // LSB 9 | |
#define LP5569_REG_PROGRAM_MEM_20 0x64 // MSB 10 | |
#define LP5569_REG_PROGRAM_MEM_21 0x65 // LSB 10 | |
#define LP5569_REG_PROGRAM_MEM_22 0x66 // MSB 11 | |
#define LP5569_REG_PROGRAM_MEM_23 0x67 // LSB 11 | |
#define LP5569_REG_PROGRAM_MEM_24 0x68 // MSB 12 | |
#define LP5569_REG_PROGRAM_MEM_25 0x69 // LSB 12 | |
#define LP5569_REG_PROGRAM_MEM_26 0x6A // MSB 13 | |
#define LP5569_REG_PROGRAM_MEM_27 0x6B // LSB 13 | |
#define LP5569_REG_PROGRAM_MEM_28 0x6C // MSB 14 | |
#define LP5569_REG_PROGRAM_MEM_29 0x6D // LSB 14 | |
#define LP5569_REG_PROGRAM_MEM_30 0x6E // MSB 15 | |
#define LP5569_REG_PROGRAM_MEM_31 0x6F // LSB 15 | |
#define LP5569_REG_ENGINE1_MAPPING1 0x70 // Engine 1 LED [8] and Master Fader Mapping | |
#define LP5569_REG_ENGINE1_MAPPING2 0x71 // Engine 1 LED [7:0] Mapping | |
#define LP5569_REG_ENGINE2_MAPPING1 0x72 // Engine 2 LED [8] and Master Fader Mapping | |
#define LP5569_REG_ENGINE2_MAPPING2 0x73 // Engine 2 LED [7:0] Mapping | |
#define LP5569_REG_ENGINE3_MAPPING1 0x74 // Engine 3 LED [8] and Master Fader Mapping | |
#define LP5569_REG_ENGINE3_MAPPING2 0x75 // Engine 3 LED [7:0] Mapping | |
#define LP5569_REG_PWM_CONFIG 0x80 // PWM Input Configuration | |
#define LP5569_REG_LED_FAULT1 0x81 // LED [8] Fault Status | |
#define LP5569_REG_LED_FAULT2 0x82 // LED [7:0] Fault Status | |
#define LP5569_REG_GENERAL_FAULT 0x83 // CP Cap UVLO and TSD Fault Status | |
// Piezo | |
// Table of notes/pitch (in Hz) | |
#define NOTE_REST 0 | |
#define NOTE_B0 31 | |
#define NOTE_C1 33 | |
#define NOTE_CS1 35 | |
#define NOTE_D1 37 | |
#define NOTE_DS1 39 | |
#define NOTE_E1 41 | |
#define NOTE_F1 44 | |
#define NOTE_FS1 46 | |
#define NOTE_G1 49 | |
#define NOTE_GS1 52 | |
#define NOTE_A1 55 | |
#define NOTE_AS1 58 | |
#define NOTE_B1 62 | |
#define NOTE_C2 65 | |
#define NOTE_CS2 69 | |
#define NOTE_D2 73 | |
#define NOTE_DS2 78 | |
#define NOTE_E2 82 | |
#define NOTE_F2 87 | |
#define NOTE_FS2 93 | |
#define NOTE_G2 98 | |
#define NOTE_GS2 104 | |
#define NOTE_A2 110 | |
#define NOTE_AS2 117 | |
#define NOTE_B2 123 | |
#define NOTE_C3 131 | |
#define NOTE_CS3 139 | |
#define NOTE_D3 147 | |
#define NOTE_DS3 156 | |
#define NOTE_E3 165 | |
#define NOTE_F3 175 | |
#define NOTE_FS3 185 | |
#define NOTE_G3 196 | |
#define NOTE_GS3 208 | |
#define NOTE_A3 220 | |
#define NOTE_AS3 233 | |
#define NOTE_B3 247 | |
#define NOTE_C4 262 | |
#define NOTE_CS4 277 | |
#define NOTE_D4 294 | |
#define NOTE_DS4 311 | |
#define NOTE_E4 330 | |
#define NOTE_F4 349 | |
#define NOTE_FS4 370 | |
#define NOTE_G4 392 | |
#define NOTE_GS4 415 | |
#define NOTE_A4 440 | |
#define NOTE_AS4 466 | |
#define NOTE_B4 494 | |
#define NOTE_C5 523 | |
#define NOTE_CS5 554 | |
#define NOTE_D5 587 | |
#define NOTE_DS5 622 | |
#define NOTE_E5 659 | |
#define NOTE_F5 698 | |
#define NOTE_FS5 740 | |
#define NOTE_G5 784 | |
#define NOTE_GS5 831 | |
#define NOTE_A5 880 | |
#define NOTE_AS5 932 | |
#define NOTE_B5 988 | |
#define NOTE_C6 1047 | |
#define NOTE_CS6 1109 | |
#define NOTE_D6 1175 | |
#define NOTE_DS6 1245 | |
#define NOTE_E6 1319 | |
#define NOTE_F6 1397 | |
#define NOTE_FS6 1480 | |
#define NOTE_G6 1568 | |
#define NOTE_GS6 1661 | |
#define NOTE_A6 1760 | |
#define NOTE_AS6 1865 | |
#define NOTE_B6 1976 | |
#define NOTE_C7 2093 | |
#define NOTE_CS7 2217 | |
#define NOTE_D7 2349 | |
#define NOTE_DS7 2489 | |
#define NOTE_E7 2637 | |
#define NOTE_F7 2794 | |
#define NOTE_FS7 2960 | |
#define NOTE_G7 3136 | |
#define NOTE_GS7 3322 | |
#define NOTE_A7 3520 | |
#define NOTE_AS7 3729 | |
#define NOTE_B7 3951 | |
#define NOTE_C8 4186 | |
#define NOTE_CS8 4435 | |
#define NOTE_D8 4699 | |
#define NOTE_DS8 4978 | |
/************************************************************************** | |
************************** Macros ***************************************** | |
***************************************************************************/ | |
#define HIGH_BYTE(x) ((x) >> 8) | |
#define LOW_BYTE(x) ((x) & 0xFF) | |
#define dc27_invalid_cmd() PRINTF("?") | |
/************************************************************************** | |
************************** Structs **************************************** | |
***************************************************************************/ | |
typedef enum // badge types | |
{ | |
HUMAN, | |
GOON, | |
SPEAKER, | |
VENDOR, | |
PRESS, | |
VILLAGE, | |
CONTEST, | |
ARTIST, | |
CFP, | |
UBER | |
} badge_type_t; | |
typedef enum // badge states | |
{ | |
ATTRACT, | |
D, | |
E, | |
F, | |
C, | |
O, | |
N, | |
COMPLETE | |
} badge_state_t; | |
struct packet_of_infamy // data packet for NFMI transfer | |
{ | |
uint32_t uid; // unique ID | |
uint8_t type; // badge type | |
uint8_t magic; // magic token (1 = enabled) | |
uint8_t flags; // game flags (packed, MSB unused) | |
uint8_t unused; // unused | |
}; | |
struct note // sound generation | |
{ | |
long freq; // frequency (Hz) | |
long duration; // duration (ms) | |
char duty; // duty cycle (%) | |
}; | |
/**************************************************************************** | |
************************** Global variables ******************************** | |
***************************************************************************/ | |
// Badge | |
volatile static badge_state_t badge_state, attract_state; | |
volatile static badge_type_t badge_type = __BADGE_TYPE; | |
volatile static uint8_t game_flags, group_flags; // tasks to complete, 1 = done, 0 = not done | |
volatile unsigned char g_random; // PRNG | |
volatile bool g_oldRx, g_newRx; // used to detect rising edge transition of KL RX (for interactive mode) | |
// Flash driver/EEPROM | |
static flash_config_t s_flashDriver; | |
static ftfx_cache_config_t s_cacheDriver; | |
static uint32_t pflashBlockBase = 0; | |
static uint32_t pflashTotalSize = 0; | |
static uint32_t pflashSectorSize = 0; | |
// Timer | |
volatile uint32_t g_systickCounter; | |
volatile bool g_lptmrFlag; | |
// UART2 (to/from host) | |
extern const uart_config_t UART2_config; // peripherals.c | |
// LPUART0 (to/from NXH2261) | |
/* | |
Ring buffer for data input and output | |
Input data is stored to the ring buffer in IRQ handler | |
Ring buffer full: (((rxIndex + 1) % RING_BUFFER_SIZE) == txIndex) | |
Ring buffer empty: (rxIndex == txIndex) | |
*/ | |
volatile static uint8_t nxhRingBuffer[LPUART0_RING_BUFFER_SIZE]; | |
volatile static uint16_t nxhTxIndex; // Index of the data to send out | |
volatile static uint16_t nxhRxIndex; // Index of the memory to save new received data | |
// LP5569 LED driver default settings | |
unsigned char LP5569_Control; // Control Register | |
unsigned char LP5569_Current; // Current Control | |
unsigned char LP5569_PWM; // PWM Duty Cycle | |
// NHX2261 | |
volatile bool g_nxhDetect = false; | |
static struct packet_of_infamy nxhTxPacket; // Data packet to transmit | |
static struct packet_of_infamy nxhRxPacket; // Received data packet | |
// Piezo/PWM | |
extern const tpm_chnl_pwm_signal_param_t TPM0_pwmSignalParams[]; // peripherals.c | |
/**************************************************************************** | |
*************************** Constants ************************************** | |
***************************************************************************/ | |
const char command_prompt[] = "\n\r> "; | |
const char menu_banner[] = "\n\r\ | |
T: Display transmit packet\n\r\ | |
R: Receive packet(s)\n\r\ | |
C: Clear game flags\n\r\ | |
H: Display available commands\n\r\ | |
^: System reset\n\r\ | |
Ctrl-X: Exit interactive mode\n\r\ | |
"; | |
const char menu_banner_complete[] = "\n\r\ | |
A <string>: ASCII art generator\n\r\ | |
S <freq> <ms>: Tone generator\n\r\ | |
U <hex bytes>: Update transmit packet\n\r\ | |
"; | |
const char msg_welcome[] = "\n\r\n\rWelcome to the DEFCON 27 Official Badge\n\r\n\r"; | |
const char msg_init_start[] = "[*] Starting Initialization\n\r"; | |
const char msg_init_complete[] = "[*] Initialization Complete\n\r"; | |
const char msg_interactive_mode[] = "[*] Entering Interactive Mode [Press 'H' for Commands]\n\r"; | |
const char msg_interactive_exit[] = "\n\r[*] Exiting Interactive Mode\n\r"; | |
const char msg_nfmi_packet_err[] = "[*] NFMI Packet Update Error!\n\r"; | |
const char msg_version[] = "\ | |
Designed by Joe Grand [@joegrand] aka Kingpin\n\r\ | |
grandideastudio.com/portfolio/defcon-27-badge/\n\r\n\r\ | |
"; | |
// NES Super Mario Bros. 1-Up | |
// Sheet music from http://www.mariopiano.com/mario-sheet-music-1-up-mushroom-sound.html | |
const struct note tune_1up[] = { | |
{NOTE_E6, 125, 50}, {NOTE_G6, 125, 50}, {NOTE_E7, 125, 50}, {NOTE_C7, 125, 50}, | |
{NOTE_D7, 125, 50}, {NOTE_G7, 125, 50} | |
}; | |
// He Who Shall Not Be Named | |
// Ported from https://create.arduino.cc/projecthub/slagestee/rickroll-box-3c2245 | |
/*const struct note tune_rickroll_intro[] = { | |
{NOTE_CS5, 600, 50}, {NOTE_DS5, 1000, 50}, {NOTE_DS5, 600, 50}, {NOTE_F5, 600, 50}, | |
{NOTE_GS5, 100, 50}, {NOTE_FS5, 100, 50}, {NOTE_F5, 100, 50}, {NOTE_DS5, 100, 50}, | |
{NOTE_CS5, 600, 50}, {NOTE_DS5, 1000, 50}, {NOTE_REST, 400, 50}, {NOTE_GS4, 200, 50}, | |
{NOTE_GS4, 1000, 50} | |
};*/ | |
/*const struct note tune_rickroll_verse[] = { | |
{NOTE_REST, 400, 50}, {NOTE_CS4, 200, 50}, {NOTE_CS4, 200, 50}, {NOTE_CS4, 200, 50}, | |
{NOTE_CS4, 200, 50}, {NOTE_DS4, 400, 50}, {NOTE_REST, 200, 50}, {NOTE_C4, 200, 50}, | |
{NOTE_AS3, 200, 50}, {NOTE_GS3, 1000, 50}, {NOTE_REST, 200, 50}, {NOTE_AS3, 200, 50}, | |
{NOTE_AS3, 200, 50}, {NOTE_C4, 200, 50}, {NOTE_CS4, 600, 50}, {NOTE_GS3, 200, 50}, | |
{NOTE_GS4, 400, 50}, {NOTE_GS4, 200, 50}, {NOTE_DS4, 1000, 50}, {NOTE_REST, 200, 50}, | |
{NOTE_AS3, 200, 50}, {NOTE_AS3, 200, 50}, {NOTE_C4, 200, 50}, {NOTE_CS4, 200, 50}, | |
{NOTE_AS3, 200, 50}, {NOTE_CS4, 200, 50}, {NOTE_DS4, 400, 50}, {NOTE_REST, 200, 50}, | |
{NOTE_C4, 200, 50}, {NOTE_AS3, 200, 50}, {NOTE_AS3, 200, 50}, {NOTE_GS3, 600, 50}, | |
{NOTE_REST, 200, 50}, {NOTE_AS3, 200, 50}, {NOTE_AS3, 200, 50}, {NOTE_C4, 200, 50}, | |
{NOTE_CS4, 400, 50}, {NOTE_GS3, 200, 50}, {NOTE_GS3, 200, 50}, {NOTE_DS4, 200, 50}, | |
{NOTE_DS4, 200, 50}, {NOTE_DS4, 200, 50}, {NOTE_F4, 200, 50}, {NOTE_DS4, 800, 50}, | |
{NOTE_CS4, 1000, 50}, {NOTE_DS4, 200, 50}, {NOTE_F4, 200, 50}, {NOTE_CS4, 200, 50}, | |
{NOTE_DS4, 200, 50}, {NOTE_DS4, 200, 50}, {NOTE_DS4, 200, 50}, {NOTE_F4, 200, 50}, | |
{NOTE_DS4, 400, 50}, {NOTE_GS3, 400, 50}, {NOTE_REST, 400, 50}, {NOTE_AS3, 200, 50}, | |
{NOTE_C4, 200, 50}, {NOTE_CS4, 200, 50}, {NOTE_GS3, 600, 50}, {NOTE_REST, 200, 50}, | |
{NOTE_DS4, 200, 50}, {NOTE_F4, 200, 50}, {NOTE_DS4, 600, 50} | |
}; | |
const char* tune_rickroll_verse_lyrics[] = { | |
"\n\r", "We're ", "no ", "stran", "gers ", "", "to ", "", "love ", "", "\n\r", | |
"You ", "know ", "the ", "rules ", "and ", "so ", "do ", "I", "", "\n\r", | |
"A ", "full ", "com", "mit", "ment's ", "what ", "I'm ", "think", "ing ", "of", "", "\n\r", | |
"You ", "would", "n't ", "get ", "this ", "from ", "any ", "oth", "er ", "guy", "\n\r", | |
"I ", "just ", "wan", "na ", "tell ", "you ", "how ", "I'm ", "feel", "ing", "\n\r", | |
"Got", "ta ", "make ", "you ", "", "un", "der", "stand\n\r\n\r" | |
};*/ | |
const struct note tune_rickroll_chorus[] = { | |
{NOTE_AS4, 100, 50}, {NOTE_AS4, 100, 50}, {NOTE_GS4, 100, 50}, {NOTE_GS4, 100, 50}, | |
{NOTE_F5, 300, 50}, {NOTE_F5, 300, 50}, {NOTE_DS5, 600, 50}, {NOTE_AS4, 100, 50}, | |
{NOTE_AS4, 100, 50}, {NOTE_GS4, 100, 50}, {NOTE_GS4, 100, 50}, {NOTE_DS5, 300, 50}, | |
{NOTE_DS5, 300, 50}, {NOTE_CS5, 300, 50}, {NOTE_C5, 100, 50}, {NOTE_AS4, 200, 50}, | |
{NOTE_CS5, 100, 50}, {NOTE_CS5, 100, 50}, {NOTE_CS5, 100, 50}, {NOTE_CS5, 100, 50}, | |
{NOTE_CS5, 300, 50}, {NOTE_DS5, 300, 50}, {NOTE_C5, 300, 50}, {NOTE_AS4, 100, 50}, | |
{NOTE_GS4, 200, 50}, {NOTE_GS4, 200, 50}, {NOTE_GS4, 200, 50}, {NOTE_DS5, 400, 50}, | |
{NOTE_CS5, 800, 50}, {NOTE_AS4, 100, 50}, {NOTE_AS4, 100, 50}, {NOTE_GS4, 100, 50}, | |
{NOTE_GS4, 100, 50}, {NOTE_F5, 300, 50}, {NOTE_F5, 300, 50}, {NOTE_DS5, 600, 50}, | |
{NOTE_AS4, 100, 50}, {NOTE_AS4, 100, 50}, {NOTE_GS4, 100, 50}, {NOTE_GS4, 100, 50}, | |
{NOTE_GS5, 300, 50}, {NOTE_C5, 300, 50}, {NOTE_CS5, 300, 50}, {NOTE_C5, 100, 50}, | |
{NOTE_AS4, 200, 50}, {NOTE_CS5, 100, 50}, {NOTE_CS5, 100, 50}, {NOTE_CS5, 100, 50}, | |
{NOTE_CS5, 100, 50}, {NOTE_CS5, 300, 50}, {NOTE_DS5, 300, 50}, {NOTE_C5, 300, 50}, | |
{NOTE_AS4, 100, 50}, {NOTE_GS4, 200, 50}, {NOTE_REST, 200, 50}, {NOTE_GS4, 200, 50}, | |
{NOTE_DS5, 400, 50}, {NOTE_CS5, 800, 50}, {NOTE_REST, 400, 50} | |
}; | |
const char* tune_rickroll_chorus_lyrics[] = { | |
"Ne", "ver ", "gon", "na ", "give ", "you ", "up", "\n\r", | |
"Ne", "ver ", "gon", "na ", "let ", "you ", "down", "", "\n\r", | |
"Ne", "ver ", "gon", "na ", "run ", "a", "round ", "and ", "de", "sert ", "you", "\n\r", | |
"Ne", "ver ", "gon", "na ", "make ", "you ", "cry", "", "\n\r", | |
"Ne", "ver ", "gon", "na ", "say ", "good", "bye ", "", "\n\r", | |
"Ne", "ver ", "gon", "na ", "tell ", "a ", "lie ", "", "and ", "hurt ", "you", "\n\r\n\r" | |
}; | |
// ASCII art for generator | |
#define ART_WIDTH 67 | |
#define ART_LINES 35 | |
const char ART_DATA[] = { | |
25, 17, 25, 19, 29, 19, 16, 35, 16, 13, 41, 13, 10, 19, \ | |
10, 18, 10, 8, 17, 18, 16, 8, 7, 16, 22, 15, 7, 5, 16, \ | |
26, 15, 5, 4, 16, 7, 5, 6, 5, 5, 15, 4, 3, 6, 3, 7, 7, \ | |
7, 4, 7, 5, 15, 3, 2, 6, 6, 5, 8, 5, 6, 5, 7, 7, 3, 5, \ | |
2, 1, 6, 7, 5, 31, 5, 7, 4, 1, 1, 7, 5, 6, 3, 4, 17, 4, \ | |
3, 5, 7, 4, 1, 0, 5, 11, 3, 4, 2, 19, 2, 4, 6, 7, 4, 0, \ | |
4, 20, 2, 17, 2, 4, 3, 13, 2, 0, 5, 5, 3, 12, 2, 15, 2, \ | |
21, 2, 0, 16, 11, 3, 9, 3, 13, 4, 5, 3, 0, 20, 10, 9, 12, \ | |
16, 0, 24, 23, 20, 0, 27, 11, 2, 3, 24, 0, 28, 1, 2, 10, \ | |
26, 1, 23, 8, 2, 11, 21, 1, 1, 19, 11, 7, 10, 18, 1, 2, \ | |
6, 5, 3, 11, 14, 11, 2, 5, 6, 2, 3, 5, 15, 22, 15, 4, 3, \ | |
4, 4, 11, 29, 11, 4, 4, 5, 7, 4, 35, 5, 6, 5, 7, 4, 6, 34, \ | |
5, 4, 7, 8, 3, 6, 34, 5, 3, 8, 10, 3, 2, 42, 10, 13, 41, \ | |
13, 16, 35, 16, 19, 29, 19, 25, 17, 25 \ | |
}; | |
// Alternate ASCII art | |
/*#define ART_WIDTH 60 | |
#define ART_LINES 36 | |
const char ART_DATA[] = { \ | |
60, 1, 12, 26, 9, 12, 3, 8, 24, 17, 8, 4, 6, 23, 21, 6, \ | |
4, 6, 22, 12, 5, 6, 5, 4, 6, 21, 11, 8, 6, 4, 4, 6, 21, \ | |
10, 10, 5, 4, 4, 6, 21, 9, 11, 5, 4, 4, 6, 21, 8, 11, 6, \ | |
4, 4, 6, 21, 7, 11, 7, 4, 4, 6, 21, 6, 11, 8, 4, 4, 6, \ | |
19, 1, 1, 5, 11, 9, 4, 4, 6, 19, 1, 1, 5, 10, 10, 4, 4, \ | |
6, 18, 2, 1, 6, 8, 11, 4, 4, 6, 17, 3, 1, 7, 5, 13, 4, 4, \ | |
6, 15, 5, 2, 23, 5, 1, 29, 5, 17, 8, 1, 29, 9, 9, 12, 1, \ | |
13, 5, 40, 1, 1, 13, 5, 40, 1, 4, 6, 13, 3, 10, 6, 12, 5, \ | |
1, 5, 6, 11, 3, 11, 6, 14, 3, 1, 5, 6, 11, 3, 11, 6, 15, \ | |
2, 1, 6, 6, 9, 3, 12, 6, 16, 1, 1, 6, 6, 9, 3, 12, 6, 7, \ | |
1, 10, 7, 6, 7, 3, 13, 6, 6, 2, 10, 7, 6, 7, 3, 13, 14, \ | |
10, 8, 6, 5, 3, 14, 6, 6, 2, 10, 8, 6, 5, 3, 14, 6, 7, 1, \ | |
10, 9, 6, 3, 3, 15, 6, 16, 1, 1, 9, 6, 3, 3, 15, 6, 15, \ | |
2, 1, 10, 6, 1, 3, 16, 6, 14, 3, 1, 10, 10, 16, 6, 12, 5, \ | |
1, 11, 8, 13, 27, 1, 11, 8, 13, 27, 1, 60 \ | |
};*/ | |
/**************************************************************************** | |
********************* Function Prototypes ********************************** | |
***************************************************************************/ | |
// Badge | |
void DC27_GameInit(void); | |
void DC27_UpdateState(void); | |
void DC27_UpdateDisplay(void); | |
void DC27_UpdateFlags(bool); | |
int DC27_IncrementFlag(void); | |
void DC27_PrintBadgeType(badge_type_t); | |
void DC27_PrintState(void); | |
void DC27_PrintPacket(struct packet_of_infamy); | |
void DC27_ProcessPacket(void); | |
void DC27_InteractiveMode(void); | |
void DC27_ASCIIArt(uint8_t *); | |
// I2C | |
bool I2C_ReadRegister(I2C_Type *, uint8_t, uint8_t, uint8_t *, uint32_t); | |
bool I2C_WriteRegister(I2C_Type *, uint8_t , uint8_t , uint8_t); | |
bool I2C_ReadBulk(I2C_Type *, uint8_t, uint8_t *, uint32_t); | |
bool I2C_WriteBulk(I2C_Type *, uint8_t , uint8_t *, uint32_t); | |
void I2C_ReleaseBus(void); | |
// LED Driver | |
int KL_Setup_LP5569(void); | |
void LP5569_SetLED(unsigned char, unsigned char); | |
void LP5569_SetLED_AllOn(void); | |
void LP5569_SetLED_AllOff(void); | |
void LP5569_SetLED_D(unsigned char); | |
void LP5569_SetLED_E(unsigned char); | |
void LP5569_SetLED_F(unsigned char); | |
void LP5569_SetLED_C(unsigned char); | |
void LP5569_SetLED_O(unsigned char); | |
void LP5569_SetLED_N(unsigned char); | |
void LP5569_RampLED(badge_state_t); | |
// NFMI Radio | |
int KL_Setup_NXH2261(void); | |
int KL_Program_NXH2261(const uint32_t, const unsigned char *); | |
int KL_LoadImage_NXH2261(uint32_t, const uint8_t *, uint32_t); | |
int KL_GetPacket_NXH2261(struct packet_of_infamy *); | |
int KL_UpdatePacket_NXH2261(struct packet_of_infamy); | |
void KL_Reset_NXH2261(void); | |
// Piezo/PWM | |
void KL_Piezo(uint32_t, uint32_t, uint8_t); | |
void KL_Piezo_1Up(void); | |
void KL_Piezo_RickRoll(void); | |
// Utilities | |
unsigned char Get_Random_Byte(void); | |
void Print_Bits(uint8_t); | |
void Reorder_Array(uint8_t *, uint8_t *, uint8_t); | |
void SysTick_DelayTicks(uint32_t); | |
int KL_Flash_Init(void); | |
int KL_Flash_Write(uint32_t); | |
void KL_Flash_Read(uint32_t *); | |
bool KL_Check_RX(void); | |
void KL_Sleep(void); | |
void KL_Error(bool, bool); | |
/**************************************************************************** | |
************************** Functions *************************************** | |
***************************************************************************/ | |
/* | |
* @brief Application entry point. | |
*/ | |
int main(void) | |
{ | |
static int b = 1; | |
// Initialize board hardware | |
BOARD_InitBootPins(); | |
BOARD_InitBootClocks(); | |
BOARD_InitBootPeripherals(); | |
// Initialize host console | |
DbgConsole_Init(UART2_BASE, UART2_config.baudRate_Bps, DEBUG_CONSOLE_DEVICE_TYPE_UART, UART2_CLOCK_SOURCE); | |
// Disable LPUART interrupts during power-up to avoid receiving data before we're ready | |
DisableIRQ(LPUART0_SERIAL_RX_TX_IRQN); | |
// Other initialization | |
SMC_SetPowerModeProtection(SMC, kSMC_AllowPowerModeAll); // Configure power mode protection settings | |
CLOCK_SetClkOutClock(0); // Disable CLKOUT on power-up (used for NXH2261 calibration only) | |
SysTick_Config(SystemCoreClock / 1000U); // Set systick reload value to generate 1ms interrupt (for delay) | |
SysTick_DelayTicks(1000); // Start-up delay | |
I2C_ReleaseBus(); | |
// Send messages to console | |
PRINTF(msg_welcome); | |
PRINTF(msg_version); | |
PRINTF(msg_init_start); | |
// Display KL27 unique ID | |
// https://community.nxp.com/thread/309453 | |
PRINTF("[*] MKL27Z64 Unique Identifier: "); | |
PRINTF("0x%08X%08X%08X [32-bit: ", SIM->UIDMH, SIM->UIDML, SIM->UIDL); | |
uint32_t idCodeShort = (SIM->UIDMH ^ SIM->UIDML ^ SIM->UIDL ^ SIM->SDID); // condense ID into 32-bit value (collisions may occur between units) | |
PRINTF("0x%08X]\n\r", idCodeShort); | |
PRINTF("[*] Core Clock = %dHz\n\r", CLOCK_GetFreq(kCLOCK_CoreSysClk)); | |
PRINTF("[*] Initializing Flash Memory"); | |
if (KL_Flash_Init()) | |
PRINTF("...Error!\n\r"); | |
// Display badge type and configure default badge-specific parameters | |
// Different LED colors have different Vf, which affects brightness | |
// We want all LEDs to visually appear at the same brightness regardless of color | |
PRINTF("[*] Badge Type = "); | |
DC27_PrintBadgeType(badge_type); | |
switch(badge_type) | |
{ | |
case HUMAN: | |
LP5569_Control = LED_CONTROL_WHITE; // Control Register | |
LP5569_Current = LED_CURRENT_WHITE; // Current Control | |
LP5569_PWM = LED_PWM_WHITE; // PWM Duty Cycle | |
break; | |
case GOON: | |
LP5569_Control = LED_CONTROL_RED; | |
LP5569_Current = LED_CURRENT_RED; | |
LP5569_PWM = LED_PWM_RED; | |
break; | |
case SPEAKER: | |
LP5569_Control = LED_CONTROL_BLUE; | |
LP5569_Current = LED_CURRENT_BLUE; | |
LP5569_PWM = LED_PWM_BLUE; | |
break; | |
case VENDOR: | |
LP5569_Control = LED_CONTROL_PURPLE; | |
LP5569_Current = LED_CURRENT_PURPLE; | |
LP5569_PWM = LED_PWM_PURPLE; | |
break; | |
case PRESS: | |
LP5569_Control = LED_CONTROL_GREEN; | |
LP5569_Current = LED_CURRENT_GREEN; | |
LP5569_PWM = LED_PWM_GREEN; | |
break; | |
case VILLAGE: | |
LP5569_Control = LED_CONTROL_ORANGE; | |
LP5569_Current = LED_CURRENT_ORANGE; | |
LP5569_PWM = LED_PWM_ORANGE; | |
break; | |
case CONTEST: | |
LP5569_Control = LED_CONTROL_WHITE; | |
LP5569_Current = LED_CURRENT_WHITE; | |
LP5569_PWM = LED_PWM_WHITE; | |
break; | |
case ARTIST: | |
LP5569_Control = LED_CONTROL_WHITE; | |
LP5569_Current = LED_CURRENT_WHITE; | |
LP5569_PWM = LED_PWM_WHITE; | |
break; | |
case CFP: | |
LP5569_Control = LED_CONTROL_WHITE; | |
LP5569_Current = LED_CURRENT_WHITE; | |
LP5569_PWM = LED_PWM_WHITE; | |
break; | |
case UBER: | |
LP5569_Control = LED_CONTROL_WHITE; | |
LP5569_Current = LED_CURRENT_WHITE; | |
LP5569_PWM = LED_PWM_WHITE; | |
break; | |
default: | |
LP5569_Control = LED_CONTROL_WHITE; | |
LP5569_Current = LED_CURRENT_WHITE; | |
LP5569_PWM = LED_PWM_WHITE; | |
} | |
DC27_GameInit(); | |
PRINTF("[*] Magic Token = "); | |
#ifdef __BADGE_MAGIC | |
PRINTF("True\n\r"); // magic token (1 = enabled) | |
#else | |
PRINTF("False\n\r"); // magic token (0 = disabled) | |
#endif | |
PRINTF("[*] Testing Piezo..."); | |
KL_Piezo_1Up(); | |
PRINTF("Done!\n\r"); | |
SysTick_DelayTicks(250); | |
PRINTF("[*] Configuring LED Driver..."); | |
if (!KL_Setup_LP5569()) | |
{ | |
LP5569_SetLED_AllOn(); | |
PRINTF("Done!\n\r"); | |
} | |
else | |
{ | |
SysTick_DelayTicks(100); | |
if (KL_Setup_LP5569()) // Try again... | |
{ | |
KL_Error(true, false); // Beep the piezo to indicate a failure | |
PRINTF("Error!\n\r"); | |
} | |
} | |
// Now that the system is up and running, enable UART interrupts from the NXH | |
EnableIRQ(LPUART0_SERIAL_RX_TX_IRQN); | |
PRINTF("[*] Configuring NFMI Radio\n\r"); | |
if (KL_Setup_NXH2261()) | |
{ | |
SysTick_DelayTicks(100); | |
if (KL_Setup_NXH2261()) // Try again... | |
{ | |
KL_Error(false, true); // Blink the LEDs to indicate a failure | |
} | |
} | |
// Craft data packet for radio to transmit | |
nxhTxPacket.uid = idCodeShort; // unique ID | |
nxhTxPacket.type = (uint8_t)badge_type; // badge type | |
#ifdef __BADGE_MAGIC | |
nxhTxPacket.magic = true; // magic token (1 = enabled) | |
#else | |
nxhTxPacket.magic = false; // magic token (0 = disabled) | |
#endif | |
nxhTxPacket.flags = game_flags; // game flags (packed, MSB unused) | |
nxhTxPacket.unused = 0; // unused | |
if (KL_UpdatePacket_NXH2261(nxhTxPacket)) // load transmit packet into the NXH2261 | |
{ | |
if (KL_UpdatePacket_NXH2261(nxhTxPacket)) | |
PRINTF(msg_nfmi_packet_err); | |
} | |
g_oldRx = KL_Check_RX(); // set current state of KL_RX (if USB-to-serial adapter is connected) | |
LP5569_SetLED(0, 0); // Update start-up progress via LEDs | |
LP5569_SetLED(5, 0); | |
PRINTF(msg_init_complete); | |
if (badge_state == COMPLETE) // if the quest is done, play a friendly tune | |
{ | |
SysTick_DelayTicks(100); | |
PRINTF("\n\r"); | |
KL_Piezo_RickRoll(); | |
SysTick_DelayTicks(500); | |
} | |
while(1) | |
{ | |
g_newRx = KL_Check_RX(); | |
if (g_newRx && (!g_oldRx || b)) // if USB-to-Serial adapter has been plugged in, KL_RX pin will go HIGH | |
{ | |
PRINTF("[*] USB-to-Serial Adapter Detected\n\r"); | |
if (!b) GETCHAR(); // if adapter is plugged in while the system is already active, drop first character | |
DC27_InteractiveMode(); | |
b = 0; // if we're here for the first time after power-up | |
} | |
g_oldRx = g_newRx; | |
if (badge_state != COMPLETE) // do this if all badge tasks have not been completed | |
{ | |
// enter VLPS (Very Low Power Sleep) | |
// MCU will wake up on NXH_DETECT external interrupt (when NXH successfully receives a data packet) | |
// or if USB-to-serial adapter is connected | |
PRINTF("[*] Sleeping..."); | |
DbgConsole_Flush(); // wait for TX buffer to empty | |
PORT_SetPinInterruptConfig(BOARD_INITPINS_KL_RX_PORT, BOARD_INITPINS_KL_RX_PIN, kPORT_InterruptRisingEdge); | |
LPTMR_SetTimerPeriod(LPTMR0_PERIPHERAL, LPTMR0_TICKS); // Set time to sleep between LED update/heartbeat | |
KL_Sleep(); | |
PRINTF("Awake!\n\r"); | |
} | |
if (g_lptmrFlag && badge_state != COMPLETE) // we must have woken up from the timer | |
{ | |
if (badge_state == ATTRACT) | |
DC27_UpdateDisplay(); | |
g_lptmrFlag = false; | |
} | |
// Check badge state and update if needed | |
DisableIRQ(LPUART0_SERIAL_RX_TX_IRQN); | |
DC27_UpdateState(); | |
EnableIRQ(LPUART0_SERIAL_RX_TX_IRQN); | |
} | |
return 0; // We should never reach here | |
} | |
/**************************************************************/ | |
void DC27_GameInit(void) // Initialize DC27 badge game-related items | |
{ | |
uint32_t data; | |
#ifdef __BADGE_MAGIC // for magic token, skip attract mode to save battery | |
badge_state = D; | |
#else | |
badge_state = ATTRACT; | |
attract_state = D; // used for cycling through LED states during attract mode | |
#endif | |
// Configure game flags (for development/debugging purposes) | |
//game_flags = 0; // Clear all | |
//game_flags = 0b00111111; // State N | |
//game_flags = FLAG_ALL_MASK; // Set all | |
//DC27_UpdateFlags(false); | |
#ifdef __BADGE_MAGIC // reset game flags, since they're not being used | |
game_flags = 0; | |
DC27_UpdateFlags(false); | |
#endif | |
// read game flags from non-volatile Flash (persists between power cycles) | |
KL_Flash_Read(&data); | |
if (data == 0xFFFFFFFF) // on first use/power-up of the badge, the flash area will be uninitialized | |
{ | |
game_flags = 0; // clear flags | |
DC27_UpdateFlags(false); // write back to flash | |
} | |
else | |
{ | |
game_flags = (uint8_t)data; | |
if ((game_flags & FLAG_ALL_MASK) == FLAG_ALL_MASK) // if quest is complete... | |
{ | |
badge_state = COMPLETE; // go straight to sparkle mode | |
} | |
else | |
{ | |
game_flags &= ~FLAG_0_MASK; // clear flag (require communication with another person on power-up) | |
} | |
} | |
DC27_PrintState(); | |
} | |
/**************************************************************/ | |
void DC27_UpdateState(void) | |
{ | |
static int i, j; | |
uint8_t ch; | |
switch (badge_state) | |
{ | |
default: | |
case ATTRACT: // Attract mode: Cycle through D, E, F, C, O, N LED states | |
if (g_nxhDetect && !KL_GetPacket_NXH2261(&nxhRxPacket)) // if we have packet(s) in the receive buffer | |
{ | |
DC27_ProcessPacket(); // process the first one | |
} | |
while (!KL_GetPacket_NXH2261(&nxhRxPacket)){}; // clear the rest | |
g_nxhDetect = false; | |
if ((game_flags & FLAG_0_MASK) == 1) // Communication with anyone | |
{ | |
// set badge state accordingly based on game flags settings | |
// flags 1-5 can happen in any order, so badge state is determined | |
// by how many bits have been set so far | |
ch = 0; | |
j = game_flags >> 1; | |
for (i = 0; i < 5; ++i) | |
{ | |
ch += (j & 0x01); | |
j >>= 1; | |
} | |
switch (ch) | |
{ | |
case 0: | |
badge_state = D; | |
break; | |
case 1: | |
badge_state = E; | |
break; | |
case 2: | |
badge_state = F; | |
break; | |
case 3: | |
badge_state = C; | |
break; | |
case 4: | |
badge_state = O; | |
break; | |
case 5: | |
badge_state = N; | |
break; | |
} | |
DC27_UpdateDisplay(); | |
KL_Piezo_1Up(); | |
DC27_PrintState(); | |
SysTick_DelayTicks(500); | |
LP5569_SetLED_AllOff(); | |
DC27_UpdateFlags(true); | |
} | |
break; | |
case D: | |
if (g_nxhDetect && !KL_GetPacket_NXH2261(&nxhRxPacket)) // if we have packet(s) in the receive buffer | |
{ | |
DC27_ProcessPacket(); // process the first one | |
#ifdef __BADGE_MAGIC | |
LP5569_SetLED_AllOn(); | |
#else | |
DC27_UpdateDisplay(); | |
#endif | |
SysTick_DelayTicks(500); | |
LP5569_SetLED_AllOff(); | |
} | |
while (!KL_GetPacket_NXH2261(&nxhRxPacket)){}; // clear the rest | |
g_nxhDetect = false; | |
#ifndef __BADGE_MAGIC // magic token stays in this state | |
if (nxhRxPacket.magic == true) // if we've received data from a magic token | |
{ | |
if (DC27_IncrementFlag()) // if we've received a flag we don't already have, move to the next state | |
{ | |
SysTick_DelayTicks(500); | |
badge_state = E; | |
DC27_UpdateDisplay(); | |
KL_Piezo_1Up(); | |
DC27_PrintState(); | |
SysTick_DelayTicks(500); | |
LP5569_SetLED_AllOff(); | |
DC27_UpdateFlags(true); | |
} | |
} | |
#endif | |
break; | |
case E: | |
if (g_nxhDetect && !KL_GetPacket_NXH2261(&nxhRxPacket)) // if we have packet(s) in the receive buffer | |
{ | |
DC27_ProcessPacket(); // process the first one | |
DC27_UpdateDisplay(); | |
SysTick_DelayTicks(500); | |
LP5569_SetLED_AllOff(); | |
} | |
while (!KL_GetPacket_NXH2261(&nxhRxPacket)){}; // clear the rest | |
g_nxhDetect = false; | |
if (nxhRxPacket.magic == true) // if we've received data from a magic token | |
{ | |
if (DC27_IncrementFlag()) // if we've received a flag we don't already have, move to the next state | |
{ | |
SysTick_DelayTicks(500); | |
badge_state = F; | |
DC27_UpdateDisplay(); | |
KL_Piezo_1Up(); | |
DC27_PrintState(); | |
SysTick_DelayTicks(500); | |
LP5569_SetLED_AllOff(); | |
DC27_UpdateFlags(true); | |
} | |
} | |
break; | |
case F: | |
if (g_nxhDetect && !KL_GetPacket_NXH2261(&nxhRxPacket)) // if we have packet(s) in the receive buffer | |
{ | |
DC27_ProcessPacket(); // process the first one | |
DC27_UpdateDisplay(); | |
SysTick_DelayTicks(500); | |
LP5569_SetLED_AllOff(); | |
} | |
while (!KL_GetPacket_NXH2261(&nxhRxPacket)){}; // clear the rest | |
g_nxhDetect = false; | |
if (nxhRxPacket.magic == true) // if we've received data from a magic token | |
{ | |
if (DC27_IncrementFlag()) // if we've received a flag we don't already have, move to the next state | |
{ | |
SysTick_DelayTicks(500); | |
badge_state = C; | |
DC27_UpdateDisplay(); | |
KL_Piezo_1Up(); | |
DC27_PrintState(); | |
SysTick_DelayTicks(500); | |
LP5569_SetLED_AllOff(); | |
DC27_UpdateFlags(true); | |
} | |
} | |
break; | |
case C: | |
if (g_nxhDetect && !KL_GetPacket_NXH2261(&nxhRxPacket)) // if we have packet(s) in the receive buffer | |
{ | |
DC27_ProcessPacket(); // process the first one | |
DC27_UpdateDisplay(); | |
SysTick_DelayTicks(500); | |
LP5569_SetLED_AllOff(); | |
} | |
while (!KL_GetPacket_NXH2261(&nxhRxPacket)){}; // clear the rest | |
g_nxhDetect = false; | |
if (nxhRxPacket.magic == true) // if we've received data from a magic token | |
{ | |
if (DC27_IncrementFlag()) // if we've received a flag we don't already have, move to the next state | |
{ | |
SysTick_DelayTicks(500); | |
badge_state = O; | |
DC27_UpdateDisplay(); | |
KL_Piezo_1Up(); | |
DC27_PrintState(); | |
SysTick_DelayTicks(500); | |
LP5569_SetLED_AllOff(); | |
DC27_UpdateFlags(true); | |
} | |
} | |
break; | |
case O: | |
if (g_nxhDetect && !KL_GetPacket_NXH2261(&nxhRxPacket)) // if we have packet(s) in the receive buffer | |
{ | |
DC27_ProcessPacket(); // process the first one | |
DC27_UpdateDisplay(); | |
SysTick_DelayTicks(500); | |
LP5569_SetLED_AllOff(); | |
} | |
while (!KL_GetPacket_NXH2261(&nxhRxPacket)){}; // clear the rest | |
g_nxhDetect = false; | |
if (nxhRxPacket.magic == true) // if we've received data from a magic token | |
{ | |
if (DC27_IncrementFlag()) // if we've received a flag we don't already have, move to the next state | |
{ | |
SysTick_DelayTicks(500); | |
badge_state = N; | |
DC27_UpdateDisplay(); | |
KL_Piezo_1Up(); | |
DC27_PrintState(); | |
SysTick_DelayTicks(500); | |
LP5569_SetLED_AllOff(); | |
DC27_UpdateFlags(true); | |
group_flags = 0; | |
} | |
} | |
break; | |
case N: // Group chat (all 6 gemstone colors: Human/Contest/Artist/CFP/Uber + Goon + Speaker + Vendor + Press + Village) | |
if (g_nxhDetect && !KL_GetPacket_NXH2261(&nxhRxPacket)) // if we have packet(s) in the receive buffer | |
{ | |
DC27_ProcessPacket(); // process the first one | |
DC27_UpdateDisplay(); | |
SysTick_DelayTicks(500); | |
LP5569_SetLED_AllOff(); | |
} | |
while (!KL_GetPacket_NXH2261(&nxhRxPacket)){}; // clear the rest | |
g_nxhDetect = false; | |
switch (nxhRxPacket.type) | |
{ | |
case HUMAN: | |
case CONTEST: | |
case ARTIST: | |
case CFP: | |
case UBER: | |
group_flags |= FLAG_0_MASK; | |
break; | |
case GOON: | |
group_flags |= FLAG_1_MASK; | |
break; | |
case SPEAKER: | |
group_flags |= FLAG_2_MASK; | |
break; | |
case VENDOR: | |
group_flags |= FLAG_3_MASK; | |
break; | |
case PRESS: | |
group_flags |= FLAG_4_MASK; | |
break; | |
case VILLAGE: | |
group_flags |= FLAG_5_MASK; | |
break; | |
} | |
// win! | |
if ((group_flags & GROUP_ALL_MASK) == GROUP_ALL_MASK) | |
{ | |
game_flags |= FLAG_6_MASK; | |
badge_state = COMPLETE; | |
LP5569_SetLED_AllOff(); | |
SysTick_DelayTicks(500); | |
KL_Piezo_RickRoll(); | |
DC27_PrintState(); | |
DC27_UpdateFlags(true); | |
SysTick_DelayTicks(1500); | |
} | |
break; | |
case COMPLETE: // Sparkle mode | |
DC27_UpdateDisplay(); | |
LPTMR_SetTimerPeriod(LPTMR0_PERIPHERAL, LED_SPARKLE_WAIT_DELAY); // Set time to sleep between LED updates | |
while (!KL_GetPacket_NXH2261(&nxhRxPacket)){}; // clear any packets from buffer so we don't overflow | |
g_nxhDetect = false; | |
KL_Sleep(); // Go to sleep until our delay period is complete | |
break; | |
} | |
} | |
/**************************************************************/ | |
void DC27_UpdateDisplay(void) // update LEDs based on current state | |
{ | |
static volatile int j; | |
switch (badge_state) | |
{ | |
default: | |
case ATTRACT: // Attract mode: Cycle through D, E, F, C, O, N LED states | |
switch (attract_state) | |
{ | |
case D: | |
LP5569_RampLED(D); | |
attract_state = E; | |
break; | |
case E: | |
LP5569_RampLED(E); | |
attract_state = F; | |
break; | |
case F: | |
LP5569_RampLED(F); | |
attract_state = C; | |
break; | |
case C: | |
LP5569_RampLED(C); | |
attract_state = O; | |
break; | |
case O: | |
LP5569_RampLED(O); | |
attract_state = N; | |
break; | |
case N: | |
LP5569_RampLED(N); | |
attract_state = D; | |
break; | |
case ATTRACT: // unused states | |
case COMPLETE: | |
default: | |
attract_state = D; | |
break; | |
} | |
break; | |
case D: | |
LP5569_SetLED_D(LP5569_PWM); | |
break; | |
case E: | |
LP5569_SetLED_E(LP5569_PWM); | |
break; | |
case F: | |
LP5569_SetLED_F(LP5569_PWM); | |
break; | |
case C: | |
LP5569_SetLED_C(LP5569_PWM); | |
break; | |
case O: | |
LP5569_SetLED_O(LP5569_PWM); | |
break; | |
case N: | |
LP5569_SetLED_N(LP5569_PWM); | |
break; | |
case COMPLETE: | |
for (j = 0; j <= LP5569_PWM; j++) // ramp up to maximum defined brightness | |
{ | |
LP5569_SetLED(1, j); | |
LP5569_SetLED(4, j); | |
SysTick_DelayTicks(LED_SPARKLE_FADE_DELAY); | |
} | |
SysTick_DelayTicks(LED_SPARKLE_ON_DELAY); | |
LP5569_SetLED_AllOff(); // clear display | |
LP5569_SetLED(0, LP5569_PWM); | |
LP5569_SetLED(3, LP5569_PWM); | |
SysTick_DelayTicks(LED_SPARKLE_ON_DELAY); | |
LP5569_SetLED_AllOff(); // clear display | |
LP5569_SetLED(1, LP5569_PWM); | |
LP5569_SetLED(4, LP5569_PWM); | |
SysTick_DelayTicks(LED_SPARKLE_ON_DELAY); | |
LP5569_SetLED_AllOff(); // clear display | |
LP5569_SetLED(2, LP5569_PWM); | |
LP5569_SetLED(5, LP5569_PWM); | |
SysTick_DelayTicks(LED_SPARKLE_ON_DELAY); | |
LP5569_SetLED_AllOff(); // clear display | |
LP5569_SetLED(1, LP5569_PWM); | |
LP5569_SetLED(4, LP5569_PWM); | |
SysTick_DelayTicks(LED_SPARKLE_ON_DELAY); | |
for (j = LP5569_PWM; j >= 0; j--) // ramp down from maximum defined brightness | |
{ | |
LP5569_SetLED(1, j); | |
LP5569_SetLED(4, j); | |
SysTick_DelayTicks(LED_SPARKLE_FADE_DELAY); | |
} | |
break; | |
} | |
} | |
/**************************************************************/ | |
void DC27_UpdateFlags(bool updateNXH) | |
{ | |
// write game flags to non-volatile Flash (persists between power cycles) | |
if (KL_Flash_Write((uint32_t)game_flags)) | |
{ | |
if (KL_Flash_Write((uint32_t)game_flags)) | |
{ | |
KL_Error(true, false); // Beep the piezo to indicate a failure | |
PRINTF("[*] Flash Write Error!\n\r"); | |
} | |
} | |
nxhTxPacket.flags = game_flags; // game flags (packed, MSB unused) | |
if (updateNXH) | |
{ | |
EnableIRQ(LPUART0_SERIAL_RX_TX_IRQN); | |
if (KL_UpdatePacket_NXH2261(nxhTxPacket)) // load updated transmit packet into the NXH2261 | |
{ | |
if (KL_UpdatePacket_NXH2261(nxhTxPacket)) | |
PRINTF(msg_nfmi_packet_err); | |
} | |
DisableIRQ(LPUART0_SERIAL_RX_TX_IRQN); | |
} | |
} | |
/**************************************************************/ | |
int DC27_IncrementFlag(void) | |
{ | |
switch (nxhRxPacket.type) | |
{ | |
case SPEAKER: | |
if ((game_flags & FLAG_1_MASK) == 0) | |
{ | |
game_flags |= FLAG_1_MASK; | |
return 1; | |
} | |
break; | |
case VILLAGE: | |
if ((game_flags & FLAG_2_MASK) == 0) | |
{ | |
game_flags |= FLAG_2_MASK; | |
return 1; | |
} | |
break; | |
case CONTEST: | |
if ((game_flags & FLAG_3_MASK) == 0) | |
{ | |
game_flags |= FLAG_3_MASK; | |
return 1; | |
} | |
break; | |
case ARTIST: | |
if ((game_flags & FLAG_4_MASK) == 0) | |
{ | |
game_flags |= FLAG_4_MASK; | |
return 1; | |
} | |
break; | |
case GOON: | |
if ((game_flags & FLAG_5_MASK) == 0) | |
{ | |
game_flags |= FLAG_5_MASK; | |
return 1; | |
} | |
break; | |
} | |
return 0; | |
} | |
/**************************************************************/ | |
void DC27_PrintBadgeType(badge_type_t badge) // print badge type to console | |
{ | |
switch(badge) | |
{ | |
case HUMAN: | |
PRINTF("Human"); | |
break; | |
case GOON: | |
PRINTF("Goon"); | |
break; | |
case SPEAKER: | |
PRINTF("Speaker"); | |
break; | |
case VENDOR: | |
PRINTF("Vendor"); | |
break; | |
case PRESS: | |
PRINTF("Press"); | |
break; | |
case VILLAGE: | |
PRINTF("Village"); | |
break; | |
case CONTEST: | |
PRINTF("Contest"); | |
break; | |
case ARTIST: | |
PRINTF("Artist"); | |
break; | |
case CFP: | |
PRINTF("CFP"); | |
break; | |
case UBER: | |
PRINTF("Uber"); | |
break; | |
default: | |
PRINTF("Unknown"); | |
} | |
PRINTF("\n\r"); | |
} | |
/**************************************************************/ | |
void DC27_PrintState(void) // print current state and game flags to console | |
{ | |
PRINTF("[*] Badge State = "); | |
switch (badge_state) | |
{ | |
default: | |
case ATTRACT: | |
PRINTF("Attract"); | |
break; | |
case D: | |
PRINTF("D"); | |
break; | |
case E: | |
PRINTF("E"); | |
break; | |
case F: | |
PRINTF("F"); | |
break; | |
case C: | |
PRINTF("C"); | |
break; | |
case O: | |
PRINTF("O"); | |
break; | |
case N: | |
PRINTF("N"); | |
break; | |
case COMPLETE: | |
PRINTF("Hax0r"); | |
break; | |
} | |
PRINTF("\n\r"); | |
PRINTF("[*] Game Flags = "); | |
Print_Bits(game_flags); | |
PRINTF("\n\r"); | |
} | |
/**************************************************************/ | |
void DC27_PrintPacket(struct packet_of_infamy packet) | |
{ | |
// display hex data from data packet struct | |
const unsigned char *buffer = (unsigned char*)&packet; | |
PRINTF("0x%02X%02X%02X%02X%02X%02X%02X%02X\n\r", buffer[3], buffer[2], buffer[1], buffer[0], buffer[4], buffer[5], buffer[6], buffer[7]); | |
PRINTF("-> Unique ID: 0x%08X\n\r", packet.uid); | |
PRINTF("-> Badge Type: "); | |
DC27_PrintBadgeType(packet.type); | |
PRINTF("-> Magic Token: "); | |
if (packet.magic != 0) | |
PRINTF("Yes"); | |
else | |
PRINTF("No"); | |
PRINTF("\n\r-> Game Flags: "); | |
Print_Bits(packet.flags); // MSB unused | |
PRINTF("\n\r"); | |
} | |
/**************************************************************/ | |
void DC27_ProcessPacket(void) | |
{ | |
DC27_PrintPacket(nxhRxPacket); // print packet structure to debug console | |
PRINTF("\n\r"); | |
if ((game_flags & FLAG_0_MASK) == 0) // on first read, set game flag | |
{ | |
game_flags |= FLAG_0_MASK; | |
} | |
} | |
/**************************************************************/ | |
void DC27_InteractiveMode(void) | |
{ | |
size_t i; | |
uint8_t ch, inputString[CONSOLE_RCVBUF_SIZE]; | |
uint32_t len; | |
PRINTF(msg_interactive_mode); | |
while(1) | |
{ | |
PRINTF(command_prompt); | |
len = 0; | |
while(1) | |
{ | |
ch = GETCHAR(); // get character from the user (blocking) | |
inputString[len] = ch; // add to input buffer | |
len += 1; | |
if (ch == 24) // CAN (Ctrl-X) | |
{ | |
PRINTF(msg_interactive_exit); | |
return; | |
} | |
else if (ch == '\n' || ch == '\r' || (len > CONSOLE_RCVBUF_SIZE - 1)) // take input until CR, LF, or maximum length received | |
{ | |
inputString[len - 1] = '\0'; | |
break; | |
} | |
} | |
len = strlen((char *)inputString); | |
switch(inputString[0]) | |
{ | |
case 'T': // Display transmit packet | |
case 't': | |
if (len != 1) // if input string is longer than allowable for this command, ignore it | |
dc27_invalid_cmd(); | |
else | |
{ | |
DC27_PrintPacket(nxhTxPacket); // print packet structure to debug console | |
} | |
break; | |
case 'R': // Receive data packet(s) | |
case 'r': | |
if (len != 1) // if input string is longer than allowable for this command, ignore it | |
dc27_invalid_cmd(); | |
else | |
{ | |
i = 0; | |
while (KL_GetPacket_NXH2261(&nxhRxPacket)) // if there is no available packet... | |
{ | |
if (i == 0) // only print message one time | |
{ | |
PRINTF("Waiting for Packet(s)...\n\r"); | |
i = 1; | |
} | |
g_nxhDetect = false; | |
while (!g_nxhDetect) // wait here until a new packet has been received | |
{ | |
if (!KL_Check_RX()) | |
{ | |
PRINTF(msg_interactive_exit); | |
return; // exit interactive mode if USB-to-serial adapter is removed | |
} | |
} | |
g_nxhDetect = false; | |
} | |
DC27_ProcessPacket(); | |
if (badge_state == ATTRACT || badge_state == COMPLETE) | |
{ | |
LP5569_SetLED_AllOn(); | |
} | |
else | |
{ | |
DC27_UpdateDisplay(); // update LEDs based on current state | |
} | |
SysTick_DelayTicks(500); | |
LP5569_SetLED_AllOff(); | |
// get any other packet(s) from the ring buffer | |
while (!KL_GetPacket_NXH2261(&nxhRxPacket)) | |
{ | |
DC27_ProcessPacket(); | |
if (badge_state == ATTRACT || badge_state == COMPLETE) | |
{ | |
LP5569_SetLED_AllOn(); | |
} | |
else | |
{ | |
DC27_UpdateDisplay(); // update LEDs based on current state | |
} | |
SysTick_DelayTicks(500); | |
LP5569_SetLED_AllOff(); | |
} | |
} | |
break; | |
case 'C': // Clear game flags | |
case 'c': | |
if (len != 1) // if input string is longer than allowable for this command, ignore it | |
dc27_invalid_cmd(); | |
else | |
{ | |
PRINTF("Clear Game Flags? Are You Sure? [y/N] "); | |
len = 0; | |
while(1) | |
{ | |
ch = GETCHAR(); // get character from the user (blocking) | |
inputString[len] = ch; // add to input buffer | |
len += 1; | |
if (ch == '\n' || ch == '\r' || (len > CONSOLE_RCVBUF_SIZE - 1)) // take input until CR, LF, or maximum length received | |
{ | |
inputString[len - 1] = '\0'; | |
break; | |
} | |
} | |
len = strlen((char *)inputString); | |
if (len == 1 && (inputString[0] == 'Y' || inputString[0] == 'y')) | |
{ | |
game_flags = 0; // Clear game flags | |
DC27_UpdateFlags(true); | |
PRINTF("-> Game Flags: "); | |
Print_Bits(game_flags); // MSB unused | |
PRINTF("\n\r"); | |
} | |
} | |
break; | |
case '^': | |
NVIC_SystemReset(); // System reset (does not return) | |
break; | |
case 'A': // ASCII art generator | |
case 'a': | |
if (badge_state != COMPLETE) | |
dc27_invalid_cmd(); | |
else if (len <= 2) | |
{ | |
strcpy((char *)inputString, ART_DEFAULT); | |
DC27_ASCIIArt(inputString); // use default string if none provided on the command line | |
} | |
else | |
DC27_ASCIIArt(inputString + 2); // send user-defined string to the ASCII art generator | |
break; | |
case 'S': // Tone generator | |
case 's': | |
if (len < 5 || badge_state != COMPLETE) | |
dc27_invalid_cmd(); | |
else | |
{ | |
uint32_t freq = 0, duration = 0; | |
sscanf((char *)(inputString + 2), "%d %d", &freq, &duration); | |
KL_Piezo(freq, duration, 50); | |
} | |
break; | |
case 'U': // Update outgoing data packet | |
case 'u': | |
if (len < 18 || badge_state != COMPLETE) | |
dc27_invalid_cmd(); | |
else | |
{ | |
// convert all alphabetic characters to upper case | |
for (i = 0; i < len; ++i) | |
{ | |
if (inputString[i] >= 'a' && inputString[i] <= 'z') | |
inputString[i] -= 0x20; | |
} | |
unsigned long data_low = strtoul((const char*)(inputString + 10), NULL, 16); | |
inputString[10] = '\0'; | |
unsigned long data_high = strtoul((const char*)(inputString + 2), NULL, 16); | |
PRINTF("0x%08X%08X\n\r", data_high, data_low); | |
PRINTF("Update Transmit Packet? Are You Sure? [y/N] "); | |
len = 0; | |
while(1) | |
{ | |
ch = GETCHAR(); // get character from the user (blocking) | |
inputString[len] = ch; // add to input buffer | |
len += 1; | |
if (ch == '\n' || ch == '\r' || (len > CONSOLE_RCVBUF_SIZE - 1)) // take input until CR, LF, or maximum length received | |
{ | |
inputString[len - 1] = '\0'; | |
break; | |
} | |
} | |
len = strlen((char *)inputString); | |
if (len == 1 && (inputString[0] == 'Y' || inputString[0] == 'y')) | |
{ | |
// Craft new data packet for radio to transmit | |
nxhTxPacket.uid = data_high; // unique ID | |
nxhTxPacket.type = (data_low >> 24) & 0xFF; // badge type | |
nxhTxPacket.magic = (data_low >> 16) & 0xFF; // magic token (1 = enabled) | |
nxhTxPacket.flags = (data_low >> 8) & 0xFF; // game flags (packed, MSB unused) | |
nxhTxPacket.unused = data_low & 0xFF; // unused | |
if (KL_UpdatePacket_NXH2261(nxhTxPacket)) // load packet to the NXH2261 | |
{ | |
if (KL_UpdatePacket_NXH2261(nxhTxPacket)) | |
PRINTF(msg_nfmi_packet_err); | |
} | |
else | |
PRINTF("-> Done!\n\r"); | |
} | |
} | |
break; | |
case 'H': // Display menu | |
case 'h': | |
case '?': | |
if (len != 1) // if input string is longer than allowable for this command, ignore it | |
dc27_invalid_cmd(); | |
else | |
{ | |
PRINTF(menu_banner); | |
if (badge_state == COMPLETE) // if badge tasks are complete, display additional menu items | |
PRINTF(menu_banner_complete); | |
} | |
break; | |
default: // Command not recognized | |
dc27_invalid_cmd(); | |
break; | |
} | |
} | |
} | |
/**************************************************************/ | |
// ASCII art generator | |
// Inspired by: | |
// https://twitter.com/ef1j95/status/1101865167741173760 | |
// http://www.vintage-basic.net/bcg/love.bas | |
// https://www.associationforpublicart.org/artwork/love/ | |
void DC27_ASCIIArt(uint8_t *A) | |
{ | |
volatile uint32_t I, J, L, C, P, A1, ptr; | |
uint8_t T[120]; // crafted line of text to print | |
L = strlen((char *)A); // input text from user | |
// craft a line based on the input text from user | |
for (J = 0; J < (ART_WIDTH / L) + 1; J++) | |
{ | |
for (I = 0; I < L; I++) // repeat the input text for the entire line | |
{ | |
T[(J * L) + I] = A[I]; | |
} | |
} | |
C = 0; | |
A1 = 0; | |
ptr = 0; | |
while(1) | |
{ | |
if (A1 == 0 || A1 > ART_WIDTH) // print one line at a time | |
{ | |
A1 = 1; // current column count into the line | |
P = 0; // flag to print message characters (true) or spaces (false) | |
C += 1; | |
if (C == ART_LINES) // if we've reached the end of our artwork | |
{ | |
PRINTF("\n\r"); | |
return; // done! | |
} | |
PRINTF("\n\r "); | |
} | |
A[0] = ART_DATA[ptr++]; // read from the data array (number of the next set of characters or spaces) | |
A1 += A[0]; | |
if (P) | |
{ | |
for (I = (A1 - A[0] - 1); I < A1 - 1; I++) | |
{ | |
PRINTF("%c", T[I]); // print character from the crafted message array | |
} | |
P = 0; | |
} | |
else | |
{ | |
for (I = 0; I < A[0]; I++) | |
{ | |
PRINTF(" "); // print space | |
} | |
P = 1; | |
} | |
} | |
} | |
/**************************************************************/ | |
int KL_Flash_Init(void) | |
{ | |
ftfx_security_state_t securityStatus = kFTFx_SecurityStateNotSecure; // Memory protection status | |
status_t result; // Return code from each flash driver function | |
// Clear structs | |
memset(&s_flashDriver, 0, sizeof(flash_config_t)); | |
memset(&s_cacheDriver, 0, sizeof(ftfx_cache_config_t)); | |
// Setup flash driver | |
result = FLASH_Init(&s_flashDriver); | |
if (result != kStatus_FTFx_Success) | |
{ | |
return 1; | |
} | |
// Setup flash cache driver | |
result = FTFx_CACHE_Init(&s_cacheDriver); | |
if (result != kStatus_FTFx_Success) | |
{ | |
return 1; | |
} | |
// Check security status | |
result = FLASH_GetSecurityState(&s_flashDriver, &securityStatus); | |
if (result != kStatus_FTFx_Success) | |
{ | |
return 1; | |
} | |
// Only proceed with unprotected memory (we won't be able to read/write otherwise) | |
if (securityStatus != kFTFx_SecurityStateNotSecure) | |
{ | |
return 1; | |
} | |
// Get flash properties | |
FLASH_GetProperty(&s_flashDriver, kFLASH_PropertyPflash0BlockBaseAddr, &pflashBlockBase); | |
FLASH_GetProperty(&s_flashDriver, kFLASH_PropertyPflash0TotalSize, &pflashTotalSize); | |
FLASH_GetProperty(&s_flashDriver, kFLASH_PropertyPflash0SectorSize, &pflashSectorSize); | |
// Print flash information to debug console | |
PRINTF("\n\r-> Memory Size = %dKB [0x%X]\n\r", (pflashTotalSize / 1024), pflashTotalSize); | |
PRINTF("-> Sector Size = %dKB [0x%X]\n\r", (pflashSectorSize / 1024), pflashSectorSize); | |
return 0; | |
} | |
/**************************************************************/ | |
// Erase and write a single Flash sector | |
int KL_Flash_Write(uint32_t data) | |
{ | |
status_t result; // Return code from each flash driver function | |
uint32_t destAddress; // Base address of the target memory location | |
uint32_t s_buffer[NVM_DATA_SIZE]; | |
uint32_t i, failAddr, failDat; | |
__disable_irq(); // Disable all interrupts during Flash write operations | |
// Prepare flash cache/prefetch/speculation | |
FTFx_CACHE_ClearCachePrefetchSpeculation(&s_cacheDriver, true); | |
// In case of the protected sectors at the end of the pFlash just select | |
// the block from the end of pFlash to be used for operations | |
// SECTOR_INDEX_FROM_END = 1 means the last sector | |
// SECTOR_INDEX_FROM_END = 2 means (the last sector - 1) | |
// Erase a sector starting from destAddress | |
destAddress = pflashBlockBase + (pflashTotalSize - (SECTOR_INDEX_FROM_END * pflashSectorSize)); | |
result = FLASH_Erase(&s_flashDriver, destAddress, pflashSectorSize, kFTFx_ApiEraseKey); | |
if (kStatus_FTFx_Success != result) | |
{ | |
__enable_irq(); | |
return 1; | |
} | |
// Prepare user buffer with data | |
for (i = 0; i < NVM_DATA_SIZE; i++) | |
{ | |
s_buffer[i] = (data >> (24-(i*8))) & 0xFF; | |
} | |
// Program user buffer into flash | |
result = FLASH_Program(&s_flashDriver, destAddress, (uint8_t *)s_buffer, sizeof(s_buffer)); | |
if (kStatus_FTFx_Success != result) | |
{ | |
__enable_irq(); | |
return 1; | |
} | |
// Verify programming | |
result = FLASH_VerifyProgram(&s_flashDriver, destAddress, sizeof(s_buffer), (const uint8_t *)s_buffer, kFTFx_MarginValueUser, | |
&failAddr, &failDat); | |
if (kStatus_FTFx_Success != result) | |
{ | |
__enable_irq(); | |
return 1; | |
} | |
// Clean-up | |
FTFx_CACHE_ClearCachePrefetchSpeculation(&s_cacheDriver, false); | |
__enable_irq(); | |
return 0; | |
} | |
/**************************************************************/ | |
void KL_Flash_Read(uint32_t *data) | |
{ | |
uint32_t i; | |
uint32_t s_buffer_rbc[NVM_DATA_SIZE]; | |
uint32_t destAddress; // Base address of the target memory location | |
// In case of the protected sectors at the end of the pFlash just select | |
// the block from the end of pFlash to be used for operations | |
// SECTOR_INDEX_FROM_END = 1 means the last sector | |
// SECTOR_INDEX_FROM_END = 2 means (the last sector - 1) | |
// calculate address | |
destAddress = pflashBlockBase + (pflashTotalSize - (SECTOR_INDEX_FROM_END * pflashSectorSize)); | |
for (i = 0; i < NVM_DATA_SIZE; i++) | |
{ | |
s_buffer_rbc[i] = *(volatile uint32_t *)(destAddress + i * 4); | |
} | |
for (i = 0; i < NVM_DATA_SIZE; i++) | |
{ | |
*data <<= 8; | |
*data |= (s_buffer_rbc[i] & 0xFF); | |
} | |
} | |
/**************************************************************/ | |
bool KL_Check_RX(void) // check if USB-to-Serial adapter is connected | |
{ | |
bool res; | |
// re-map pin from KL_RX to GPIO E23 | |
gpio_pin_config_t KL_RX_config = { | |
.pinDirection = kGPIO_DigitalInput, | |
.outputLogic = 0U | |
}; | |
GPIO_PinInit(GPIOE, BOARD_INITPINS_KL_RX_PIN, &KL_RX_config); | |
PORT_SetPinMux(BOARD_INITPINS_KL_RX_PORT, BOARD_INITPINS_KL_RX_PIN, kPORT_MuxAsGpio); | |
PORTE->PCR[BOARD_INITPINS_KL_RX_PIN] = ((PORTE->PCR[BOARD_INITPINS_KL_RX_PIN] & | |
/* Mask bits to zero which are setting */ | |
(~(PORT_PCR_PS_MASK | PORT_PCR_PE_MASK | PORT_PCR_ISF_MASK))) | |
/* Pull Select: Internal pulldown resistor is enabled on the corresponding pin, if the | |
* corresponding PE field is set. */ | |
| (uint32_t)(kPORT_PullDown)); | |
SysTick_DelayTicks(10); | |
res = (bool)GPIO_PinRead(GPIOE, BOARD_INITPINS_KL_RX_PIN); | |
// set KL_RX back to UART2_RX | |
PORT_SetPinInterruptConfig(BOARD_INITPINS_KL_RX_PORT, BOARD_INITPINS_KL_RX_PIN, kPORT_InterruptOrDMADisabled); | |
PORT_SetPinMux(BOARD_INITPINS_KL_RX_PORT, BOARD_INITPINS_KL_RX_PIN, kPORT_MuxAlt4); | |
SysTick_DelayTicks(10); | |
return res; | |
} | |
/**************************************************************/ | |
void KL_Error(bool error_piezo, bool error_led) | |
{ | |
int i; | |
if (error_piezo || error_led) | |
{ | |
for (i = 0; i < 5; ++i) | |
{ | |
if (error_led) | |
LP5569_SetLED_AllOn(); | |
if (error_piezo) | |
KL_Piezo(2600, 250, 50); | |
else | |
SysTick_DelayTicks(250); | |
if (error_led) | |
LP5569_SetLED_AllOff(); | |
SysTick_DelayTicks(250); | |
} | |
} | |
} | |
/**************************************************************/ | |
void KL_Sleep(void) | |
{ | |
if (badge_state == ATTRACT || badge_state == COMPLETE) // set timer for attract mode, otherwise only wake via NXH | |
{ | |
LPTMR_StartTimer(LPTMR0_PERIPHERAL); // Start LPTMR for periodic interrupt (LED heartbeat) | |
} | |
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk; // Disable SysTick timer interrupt while we sleep | |
SMC_PreEnterStopModes(); | |
SMC_SetPowerModeVlps(SMC); // Zzzz... | |
SMC_PostExitStopModes(); | |
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; // Re-enable SysTick timer interrupt | |
LPTMR_StopTimer(LPTMR0_PERIPHERAL); // Stop timer once we wake up | |
} | |
/**************************************************************/ | |
// Output square wave to the piezo element using the provided parameters | |
// frequency (Hz), duration (ms), duty cycle (%) | |
void KL_Piezo(uint32_t freq_Hz, uint32_t duration_ms, uint8_t pwm_duty) | |
{ | |
// Update parameters | |
if (freq_Hz > 0) | |
{ | |
TPM_SetupPwm(TPM0_PERIPHERAL, TPM0_pwmSignalParams, 1, kTPM_EdgeAlignedPwm, freq_Hz, TPM0_CLOCK_SOURCE); // Frequency | |
TPM_UpdatePwmDutycycle(TPM0_PERIPHERAL,TPM0_pwmSignalParams->chnlNumber, kTPM_EdgeAlignedPwm, pwm_duty); // Duty cycle | |
TPM_StartTimer(TPM0_PERIPHERAL, kTPM_SystemClock); // Start the TPM counter | |
} | |
if (duration_ms > 0) | |
SysTick_DelayTicks(duration_ms); // Duration of note (delay for specified length in ms) | |
TPM_StopTimer(TPM0_PERIPHERAL); // Stop the TPM counter | |
} | |
/**************************************************************/ | |
void KL_Piezo_1Up(void) | |
{ | |
int i; | |
for (i = 0; i < sizeof(tune_1up) / sizeof(struct note); ++i) | |
{ | |
KL_Piezo(tune_1up[i].freq, tune_1up[i].duration, tune_1up[i].duty); | |
SysTick_DelayTicks(10); | |
} | |
} | |
/**************************************************************/ | |
void KL_Piezo_RickRoll(void) | |
{ | |
int i, led_num; | |
SysTick_DelayTicks(250); | |
g_random = (unsigned char)SysTick->VAL; // Seed PRNG with current value of SysTick timer | |
/*for (i = 0; i < sizeof(tune_rickroll_intro) / sizeof(struct note); ++i) | |
{ | |
led_num = Get_Random_Byte() % LP5569_LED_NUM; | |
if (tune_rickroll_intro[i].freq != NOTE_REST) LP5569_SetLED(led_num, LP5569_PWM[led_num]); // Enable LED to flash along with the music | |
KL_Piezo(tune_rickroll_intro[i].freq, tune_rickroll_intro[i].duration, tune_rickroll_intro[i].duty); | |
LP5569_SetLED(led_num, 0x00); | |
SysTick_DelayTicks(45); | |
} | |
for (i = 0; i < sizeof(tune_rickroll_verse) / sizeof(struct note); ++i) | |
{ | |
led_num = Get_Random_Byte() % LP5569_LED_NUM; | |
if (tune_rickroll_verse[i].freq != NOTE_REST) LP5569_SetLED(led_num, LP5569_PWM[led_num]); // Enable LED to flash along with the music | |
PRINTF("%s", tune_rickroll_verse_lyrics[i]); | |
KL_Piezo(tune_rickroll_verse[i].freq, tune_rickroll_verse[i].duration, tune_rickroll_verse[i].duty); | |
LP5569_SetLED(led_num, 0x00); | |
SysTick_DelayTicks(30); | |
}*/ | |
for (i = 0; i < sizeof(tune_rickroll_chorus) / sizeof(struct note); ++i) | |
{ | |
led_num = Get_Random_Byte() % LP5569_LED_NUM; | |
if (tune_rickroll_chorus[i].freq != NOTE_REST) LP5569_SetLED(led_num, LP5569_PWM); // Enable LED to flash along with the music | |
PRINTF("%s", tune_rickroll_chorus_lyrics[i]); | |
KL_Piezo(tune_rickroll_chorus[i].freq, tune_rickroll_chorus[i].duration, tune_rickroll_chorus[i].duty); | |
LP5569_SetLED(led_num, 0x00); | |
SysTick_DelayTicks(30); | |
} | |
} | |
/**************************************************************/ | |
int KL_Setup_NXH2261(void) // Configure NXH2261 NFMI Radio | |
{ | |
// Program the NXH binary via I2C | |
if (KL_Program_NXH2261(NxH2281Eep_size, NxH2281Eep)) | |
{ | |
return 1; // Fail! | |
} | |
LP5569_SetLED(2, 0); // Update start-up progress via LEDs | |
LP5569_SetLED(3, 0); | |
PRINTF("-> Executing Program..."); | |
KL_Reset_NXH2261(); | |
SysTick_DelayTicks(200); // maximum delay of NXH2261 low-power state | |
PRINTF("Done!\n\r"); | |
// Calibration process | |
PRINTF("-> Calibrating..."); | |
CLOCK_SetClkOutClock(SIM_CLKOUT_SEL_OSCERCLK_CLK); // Set OSCERCLK to CLKOUT | |
SysTick_DelayTicks(100); | |
// Toggle NXH_UPDATE to switch NXH2261 into UART Open State (we need to be here in order to calibrate) | |
GPIO_PinWrite(BOARD_INITPINS_NXH_UPDATE_GPIO, BOARD_INITPINS_NXH_UPDATE_GPIO_PIN, HIGH); | |
SysTick_DelayTicks(500); | |
GPIO_PinWrite(BOARD_INITPINS_NXH_UPDATE_GPIO, BOARD_INITPINS_NXH_UPDATE_GPIO_PIN, LOW); | |
SysTick_DelayTicks(100); | |
// Toggle NXH_CAL to tell NXH2261 that we're ready to start calibration | |
GPIO_PinWrite(BOARD_INITPINS_NXH_CAL_GPIO, BOARD_INITPINS_NXH_CAL_GPIO_PIN, HIGH); | |
SysTick_DelayTicks(500); | |
GPIO_PinWrite(BOARD_INITPINS_NXH_CAL_GPIO, BOARD_INITPINS_NXH_CAL_GPIO_PIN, LOW); | |
SysTick_DelayTicks(500); // wait for calibration to complete (typical 50-150ms) | |
CLOCK_SetClkOutClock(0); // Turn off CLKOUT | |
PRINTF("Done!\n\r"); | |
LP5569_SetLED(1, 0); // Update start-up progress via LEDs | |
LP5569_SetLED(4, 0); | |
return 0; // Success! | |
} | |
/**************************************************************/ | |
int KL_Program_NXH2261(const uint32_t NxH2281Eep_size, const unsigned char *NxH2281Eep) | |
{ | |
uint8_t i = 0; | |
uint32_t res; | |
uint8_t txbuf[12], rxbuf[12]; | |
PRINTF("-> Entering Bootloader..."); | |
KL_Reset_NXH2261(); | |
// After reset, the NXH2261 waits 30ms before loading code from its internal EEPROM. | |
// Within this time, the host controller can send a Prevent Boot command, which forces | |
// the device into bootloader mode and allows us to send new firmware to its EEPROM. | |
do | |
{ | |
res = 1; | |
txbuf[0] = LOW_BYTE(NXH2261_CMD_PREVENT_BOOT); | |
txbuf[1] = HIGH_BYTE(NXH2261_CMD_PREVENT_BOOT); | |
txbuf[2] = 0; // tag | |
I2C_WriteBulk(I2C0_PERIPHERAL, I2C_NXH2261_ADDR, txbuf, 3); | |
if (!I2C_ReadBulk(I2C0_PERIPHERAL, I2C_NXH2261_ADDR, rxbuf, 4)) | |
{ | |
// if I2C was successful, response should be all 0x00 | |
res = rxbuf[0] | rxbuf[1] | rxbuf[2] | rxbuf[3]; | |
} | |
SysTick_DelayTicks(5); | |
++i; | |
} while (res != 0 && i < 100); | |
if (i >= 100) // Timeout | |
{ | |
PRINTF("Error!\n\r"); | |
return 1; | |
} | |
PRINTF("Done!\n\r"); | |
// Get version information | |
txbuf[0] = LOW_BYTE(NXH2261_CMD_GET_VERSION); | |
txbuf[1] = HIGH_BYTE(NXH2261_CMD_GET_VERSION); | |
txbuf[2] = 0; // tag | |
I2C_WriteBulk(I2C0_PERIPHERAL, I2C_NXH2261_ADDR, txbuf, 3); | |
I2C_ReadBulk(I2C0_PERIPHERAL, I2C_NXH2261_ADDR, rxbuf, 9); | |
PRINTF("-> Firmware Version: 0x%02X 0x%02X\n\r", rxbuf[0], rxbuf[1]); | |
PRINTF("-> Hardware Version: 0x%02X%02X 0x%02X%02X\n\r", rxbuf[3], rxbuf[2], rxbuf[6], rxbuf[5]); | |
PRINTF("-> ROM Version: 0x%02X 0x%02X\n\r", rxbuf[7], rxbuf[8]); | |
PRINTF("-> Programming..."); | |
// Enable the EEPROM in preparation to program | |
txbuf[0] = LOW_BYTE(NXH2261_CMD_EEPROM_ENABLE); | |
txbuf[1] = HIGH_BYTE(NXH2261_CMD_EEPROM_ENABLE); | |
txbuf[2] = 0; // tag | |
I2C_WriteBulk(I2C0_PERIPHERAL, I2C_NXH2261_ADDR, txbuf, 3); | |
if (!I2C_ReadBulk(I2C0_PERIPHERAL, I2C_NXH2261_ADDR, rxbuf, 4)) | |
{ | |
// if I2C was successful, response should be all 0x00 | |
res = rxbuf[0] | rxbuf[1] | rxbuf[2] | rxbuf[3]; | |
} | |
if (res != 0) | |
{ | |
PRINTF("Error!\n\r"); | |
return 1; | |
} | |
// Program the Cortex image at the primary boot location | |
// EEPROM has a write endurance of 100k cycles | |
if (KL_LoadImage_NXH2261(NxH2281Eep_size, NxH2281Eep, 0x0000UL)) | |
{ | |
PRINTF("Error!\n\r"); | |
return 1; | |
} | |
// Disable the EEPROM when programming is complete | |
txbuf[0] = LOW_BYTE(NXH2261_CMD_EEPROM_DISABLE); | |
txbuf[1] = HIGH_BYTE(NXH2261_CMD_EEPROM_DISABLE); | |
txbuf[2] = 0; // tag | |
I2C_WriteBulk(I2C0_PERIPHERAL, I2C_NXH2261_ADDR, txbuf, 3); | |
if (!I2C_ReadBulk(I2C0_PERIPHERAL, I2C_NXH2261_ADDR, rxbuf, 4)) | |
{ | |
// if I2C was successful, response should be all 0x00 | |
res = rxbuf[0] | rxbuf[1] | rxbuf[2] | rxbuf[3]; | |
} | |
if (res != 0) | |
{ | |
PRINTF("Error!\n\r"); | |
return 1; | |
} | |
PRINTF("Done!\n\r"); | |
return 0; // Success! | |
} | |
/**************************************************************/ | |
int KL_LoadImage_NXH2261(uint32_t size, const uint8_t *data, uint32_t address) | |
{ | |
uint32_t i; | |
uint16_t txaddr, chunkSize, cnt; | |
uint32_t res = 0; | |
uint8_t txbuf[NXH2261_MAX_CHUNK_SIZE + 8], rxbuf[NXH2261_MAX_CHUNK_SIZE + 8]; | |
i = 0; | |
while (i < size) | |
{ | |
// Setup parameters | |
chunkSize = ((size - i) > NXH2261_MAX_CHUNK_SIZE ? NXH2261_MAX_CHUNK_SIZE : (size - i)); // number of bytes to write | |
txaddr = address + (i / 4UL); // offset (word address) into the EEPROM | |
// Unlock EEPROM memory region for a single write | |
txbuf[0] = LOW_BYTE(NXH2261_CMD_EEPROM_UNLOCK); | |
txbuf[1] = HIGH_BYTE(NXH2261_CMD_EEPROM_UNLOCK); | |
txbuf[2] = 0; // tag | |
txbuf[3] = LOW_BYTE(txaddr); | |
txbuf[4] = HIGH_BYTE(txaddr); | |
txbuf[5] = LOW_BYTE(chunkSize); | |
txbuf[6] = HIGH_BYTE(chunkSize); | |
I2C_WriteBulk(I2C0_PERIPHERAL, I2C_NXH2261_ADDR, txbuf, 7); | |
if (!I2C_ReadBulk(I2C0_PERIPHERAL, I2C_NXH2261_ADDR, rxbuf, 4)) | |
{ | |
// if I2C was successful, response should be all 0x00 | |
res = rxbuf[0] | rxbuf[1] | rxbuf[2] | rxbuf[3]; | |
} | |
if (res != 0) | |
{ | |
return 1; | |
} | |
// Write data to EEPROM | |
txbuf[0] = LOW_BYTE(NXH2261_CMD_EEPROM_WRITE); | |
txbuf[1] = HIGH_BYTE(NXH2261_CMD_EEPROM_WRITE); | |
txbuf[2] = 0; // tag | |
txbuf[3] = LOW_BYTE(txaddr); | |
txbuf[4] = HIGH_BYTE(txaddr); | |
txbuf[5] = 0; | |
txbuf[6] = 0; | |
txbuf[7] = LOW_BYTE(chunkSize); | |
txbuf[8] = HIGH_BYTE(chunkSize); | |
txbuf[9] = 0; | |
txbuf[10] = 0; | |
cnt = chunkSize; // move length into its own variable so memcpy doesn't erase it | |
memcpy(&txbuf[11], data + i, cnt); | |
I2C_WriteBulk(I2C0_PERIPHERAL, I2C_NXH2261_ADDR, txbuf, 11 + chunkSize); | |
if (!I2C_ReadBulk(I2C0_PERIPHERAL, I2C_NXH2261_ADDR, rxbuf, 4)) | |
{ | |
// if I2C was successful, response should be all 0x00 | |
res = rxbuf[0] | rxbuf[1] | rxbuf[2] | rxbuf[3]; | |
} | |
if (res != 0) | |
{ | |
return 1; | |
} | |
i += chunkSize; | |
} | |
return 0; // Success! | |
} | |
/**************************************************************/ | |
// retrieve the most recently received data packet from the ring buffer, if it exists | |
int KL_GetPacket_NXH2261(struct packet_of_infamy *rxPacket) | |
{ | |
size_t i; | |
uint8_t ch, dataBlob[NXH2261_DATA_PACKET_SIZE], buf[NXH2261_DATA_PACKET_SIZE >> 1]; | |
// increment through the buffer until we find the packet header | |
do | |
{ | |
if (nxhRxIndex == nxhTxIndex) // we've reached the end of the buffer | |
return 1; | |
ch = nxhRingBuffer[nxhTxIndex]; | |
nxhTxIndex++; | |
nxhTxIndex %= LPUART0_RING_BUFFER_SIZE; | |
} while (ch != 'B'); | |
// extract the data contents from the buffer until we reach the packet footer | |
i = 0; | |
while ((ch = nxhRingBuffer[nxhTxIndex]) != 'E') | |
{ | |
if (nxhRxIndex == nxhTxIndex) // we've reached the end of the buffer | |
return 1; | |
dataBlob[i] = ch; | |
i++; | |
nxhTxIndex++; | |
nxhTxIndex %= LPUART0_RING_BUFFER_SIZE; | |
} | |
// remove 0xD0 padding from each nibble | |
for (i = 0; i < (NXH2261_DATA_PACKET_SIZE >> 1) - 1; i++) | |
{ | |
buf[i] = (dataBlob[i*2] & 0x0F) << 4; | |
buf[i] |= dataBlob[i*2 + 1] & 0x0F; | |
} | |
// Place data into proper structure | |
rxPacket->uid = (uint32_t)((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]); // unique ID | |
rxPacket->type = buf[4]; // badge type | |
rxPacket->magic = buf[5]; // magic token (1 = enabled) | |
rxPacket->flags = buf[6]; // game flags (packed, MSB unused) | |
rxPacket->unused = buf[7]; // unused | |
return 0; | |
} | |
/**************************************************************/ | |
// update NXH2261 with data packet to transmit | |
int KL_UpdatePacket_NXH2261(struct packet_of_infamy txPacket) | |
{ | |
size_t i = 0; | |
uint8_t ch, dataBlob[NXH2261_DATA_PACKET_SIZE]; | |
dataBlob[0] = 'B'; // header | |
dataBlob[NXH2261_DATA_PACKET_SIZE - 1] = 'E'; // footer | |
const unsigned char *buf = (unsigned char*)&txPacket; | |
// pad each nibble of buf with 0xD0 per the custom NXH UART protocol | |
for (i = 0; i < ((NXH2261_DATA_PACKET_SIZE - 2) >> 1); i++) | |
{ | |
dataBlob[i*2 + 1] = (0xD0 + ((buf[i] & 0xF0) >> 4)); | |
dataBlob[i*2 + 2] = (0xD0 + ((buf[i] & 0x0F))); | |
} | |
// flip ordering due to endianness | |
uint8_t index[] = {0, 7, 8, 5, 6, 3, 4, 1, 2, 9, 10, 11, 12, 13, 14, 15, 16, 17}; | |
Reorder_Array(dataBlob, index, NXH2261_DATA_PACKET_SIZE); | |
// display hex data from data packet struct (without header and footer) | |
PRINTF("[*] Loading Data: 0x"); | |
for (i = 1; i < NXH2261_DATA_PACKET_SIZE - 1; ++i) | |
{ | |
PRINTF("%02X", dataBlob[i]); | |
} | |
PRINTF("\n\r"); | |
// Toggle NXH_UPDATE to tell NXH2261 that we want to update data being sent | |
GPIO_PinWrite(BOARD_INITPINS_NXH_UPDATE_GPIO, BOARD_INITPINS_NXH_UPDATE_GPIO_PIN, HIGH); | |
SysTick_DelayTicks(100); | |
GPIO_PinWrite(BOARD_INITPINS_NXH_UPDATE_GPIO, BOARD_INITPINS_NXH_UPDATE_GPIO_PIN, LOW); | |
SysTick_DelayTicks(100); | |
// Wait until we receive "RO" from NXH to indicate that it is ready to receive new data | |
do | |
{ | |
if (nxhRxIndex == nxhTxIndex) // we've reached the end of the buffer | |
return 1; | |
ch = nxhRingBuffer[nxhTxIndex]; | |
nxhTxIndex++; | |
nxhTxIndex %= LPUART0_RING_BUFFER_SIZE; | |
} while (ch != 'R'); | |
// now check if 'O' is next door | |
if (nxhRxIndex == nxhTxIndex) // we've reached the end of the buffer | |
return 1; | |
ch = nxhRingBuffer[nxhTxIndex]; | |
nxhTxIndex++; | |
nxhTxIndex %= LPUART0_RING_BUFFER_SIZE; | |
if (ch == 'O') | |
{ | |
// send our updated data packet to the NXH | |
LPUART_WriteBlocking(LPUART0_PERIPHERAL, dataBlob, sizeof(dataBlob)); | |
} | |
else | |
return 1; | |
return 0; | |
} | |
/**************************************************************/ | |
void KL_Reset_NXH2261(void) | |
{ | |
GPIO_PinWrite(BOARD_INITPINS_NXH_nRESET_GPIO, BOARD_INITPINS_NXH_nRESET_GPIO_PIN, LOW); // Disable NXH2261 | |
SysTick_DelayTicks(250); | |
GPIO_PinWrite(BOARD_INITPINS_NXH_nRESET_GPIO, BOARD_INITPINS_NXH_nRESET_GPIO_PIN, HIGH); // Enable NXH2261 | |
SysTick_DelayTicks(10); | |
} | |
/**************************************************************/ | |
int KL_Setup_LP5569(void) // Configure LP5569 LED Driver | |
{ | |
uint8_t i, err = 0; | |
GPIO_PinWrite(BOARD_INITPINS_LED_EN_GPIO, BOARD_INITPINS_LED_EN_GPIO_PIN, LOW); | |
SysTick_DelayTicks(10); | |
GPIO_PinWrite(BOARD_INITPINS_LED_EN_GPIO, BOARD_INITPINS_LED_EN_GPIO_PIN, HIGH); // Enable LP5569 | |
SysTick_DelayTicks(10); | |
// Load default LED settings | |
err += I2C_WriteRegister(I2C0_PERIPHERAL, I2C_LP5569_ADDR, LP5569_REG_CONFIG, 0x00); // Disable LP5569 while setting MISC register | |
SysTick_DelayTicks(1); // Wait for register to be updated | |
err += I2C_WriteRegister(I2C0_PERIPHERAL, I2C_LP5569_ADDR, LP5569_REG_MISC, 0x39); // Auto-increment disabled, auto-power save, auto-charge pump, internal 32.768kHz oscillator | |
err += I2C_WriteRegister(I2C0_PERIPHERAL, I2C_LP5569_ADDR, LP5569_REG_CONFIG, 0x40); // Turn on LP5569 | |
SysTick_DelayTicks(1); // Wait for register to be updated | |
err += I2C_WriteRegister(I2C0_PERIPHERAL, I2C_LP5569_ADDR, LP5569_REG_LED_ENGINE_CONTROL2, 0xFA); // Halt all engines, enable direct LED control | |
for (i = 0; i < LP5569_LED_NUM; i++) // Control Register | |
{ | |
err += I2C_WriteRegister(I2C0_PERIPHERAL, I2C_LP5569_ADDR, LP5569_REG_LED0_CONTROL + i, LP5569_Control); | |
} | |
for (i = 0; i < LP5569_LED_NUM; i++) // Current Control | |
{ | |
err += I2C_WriteRegister(I2C0_PERIPHERAL, I2C_LP5569_ADDR, LP5569_REG_LED0_CURRENT + i, LP5569_Current); | |
} | |
for (i = 0; i < LP5569_LED_NUM; i++) // PWM Duty Cycle | |
{ | |
err += I2C_WriteRegister(I2C0_PERIPHERAL, I2C_LP5569_ADDR, LP5569_REG_LED0_PWM + i, 0x00); // All LEDs off | |
} | |
// Disable unused LED channels | |
err += I2C_WriteRegister(I2C0_PERIPHERAL, I2C_LP5569_ADDR, LP5569_REG_LED6_CONTROL, 0x00); | |
err += I2C_WriteRegister(I2C0_PERIPHERAL, I2C_LP5569_ADDR, LP5569_REG_LED7_CONTROL, 0x00); | |
err += I2C_WriteRegister(I2C0_PERIPHERAL, I2C_LP5569_ADDR, LP5569_REG_LED8_CONTROL, 0x00); | |
// Map LED to register control (not execution engine) | |
err += I2C_WriteRegister(I2C0_PERIPHERAL, I2C_LP5569_ADDR, LP5569_REG_ENGINE1_MAPPING1, 0x00); // LED8 | |
err += I2C_WriteRegister(I2C0_PERIPHERAL, I2C_LP5569_ADDR, LP5569_REG_ENGINE1_MAPPING2, 0x00); // LED7..0 | |
err += I2C_WriteRegister(I2C0_PERIPHERAL, I2C_LP5569_ADDR, LP5569_REG_ENGINE2_MAPPING1, 0x00); // LED8 | |
err += I2C_WriteRegister(I2C0_PERIPHERAL, I2C_LP5569_ADDR, LP5569_REG_ENGINE2_MAPPING2, 0x00); // LED7..0 | |
err += I2C_WriteRegister(I2C0_PERIPHERAL, I2C_LP5569_ADDR, LP5569_REG_ENGINE3_MAPPING1, 0x00); // LED8 | |
err += I2C_WriteRegister(I2C0_PERIPHERAL, I2C_LP5569_ADDR, LP5569_REG_ENGINE3_MAPPING2, 0x00); // LED7..0 | |
return err; | |
} | |
/**************************************************************/ | |
void LP5569_SetLED(unsigned char led_num, unsigned char led_pwm) | |
{ | |
int i; | |
//PRINTF("[*] Setting LED %d @ PWM %d\n\r", led_num, led_pwm); | |
if (I2C_WriteRegister(I2C0_PERIPHERAL, I2C_LP5569_ADDR, LP5569_REG_LED0_PWM + led_num, led_pwm)) | |
{ | |
// if write fails, try to release the bus | |
PRINTF("[*] I2C Bus Clear..."); | |
I2C_ReleaseBus(); | |
SysTick_DelayTicks(100); | |
// re-initialize I2C | |
I2C_MasterDeinit(I2C0_PERIPHERAL); | |
SysTick_DelayTicks(10); | |
I2C_MasterInit(I2C0_PERIPHERAL, &I2C0_config, I2C0_CLK_FREQ); | |
SysTick_DelayTicks(10); | |
// and re-attempt the transaction | |
if (I2C_WriteRegister(I2C0_PERIPHERAL, I2C_LP5569_ADDR, LP5569_REG_LED0_PWM + led_num, led_pwm)) | |
{ | |
PRINTF("Error!\n\r"); | |
} | |
else | |
{ | |
PRINTF("Done!\n\r"); | |
} | |
} | |
for (i = 0; i < 100; ++i){}; // poor man's delay (us) | |
} | |
/**************************************************************/ | |
void LP5569_SetLED_AllOn(void) | |
{ | |
static volatile int i; | |
for (i = 0; i < LP5569_LED_NUM; i++) // each LED | |
{ | |
LP5569_SetLED(i, LP5569_PWM); // default brightness | |
} | |
} | |
/**************************************************************/ | |
void LP5569_SetLED_AllOff(void) | |
{ | |
static volatile int i; | |
for (i = 0; i < LP5569_LED_NUM; i++) // each LED | |
{ | |
LP5569_SetLED(i, 0); // off | |
} | |
} | |
/**************************************************************/ | |
void LP5569_SetLED_D(unsigned char pwm_val) | |
{ | |
LP5569_SetLED(0, 0); | |
LP5569_SetLED(1, 0); | |
LP5569_SetLED(2, 0); | |
LP5569_SetLED(3, pwm_val); | |
LP5569_SetLED(4, pwm_val); | |
LP5569_SetLED(5, pwm_val); | |
} | |
/**************************************************************/ | |
void LP5569_SetLED_E(unsigned char pwm_val) | |
{ | |
LP5569_SetLED(0, pwm_val); | |
LP5569_SetLED(1, pwm_val); | |
LP5569_SetLED(2, pwm_val); | |
LP5569_SetLED(3, 0); | |
LP5569_SetLED(4, pwm_val); | |
LP5569_SetLED(5, 0); | |
} | |
/**************************************************************/ | |
void LP5569_SetLED_F(unsigned char pwm_val) | |
{ | |
LP5569_SetLED(0, pwm_val); | |
LP5569_SetLED(1, pwm_val); | |
LP5569_SetLED(2, pwm_val); | |
LP5569_SetLED(3, pwm_val); | |
LP5569_SetLED(4, 0); | |
LP5569_SetLED(5, 0); | |
} | |
/**************************************************************/ | |
void LP5569_SetLED_C(unsigned char pwm_val) | |
{ | |
LP5569_SetLED(0, pwm_val); | |
LP5569_SetLED(1, pwm_val); | |
LP5569_SetLED(2, pwm_val); | |
LP5569_SetLED(3, 0); | |
LP5569_SetLED(4, 0); | |
LP5569_SetLED(5, 0); | |
} | |
/**************************************************************/ | |
void LP5569_SetLED_O(unsigned char pwm_val) | |
{ | |
LP5569_SetLED(0, pwm_val); | |
LP5569_SetLED(1, pwm_val); | |
LP5569_SetLED(2, pwm_val); | |
LP5569_SetLED(3, pwm_val); | |
LP5569_SetLED(4, pwm_val); | |
LP5569_SetLED(5, pwm_val); | |
} | |
/**************************************************************/ | |
void LP5569_SetLED_N(unsigned char pwm_val) | |
{ | |
LP5569_SetLED(0, pwm_val); | |
LP5569_SetLED(1, pwm_val); | |
LP5569_SetLED(2, 0); | |
LP5569_SetLED(3, pwm_val); | |
LP5569_SetLED(4, pwm_val); | |
LP5569_SetLED(5, 0); | |
} | |
/**************************************************************/ | |
void LP5569_RampLED(badge_state_t ch) // display heartbeat (ramp up/down) of specified LED pattern | |
{ | |
static volatile int j; | |
for (j = 0; j <= LP5569_PWM; j += 4) // ramp up to maximum defined brightness | |
{ | |
if (g_nxhDetect) | |
{ | |
g_nxhDetect = false; | |
return; // exit the heartbeat if new badge data has just been received | |
} | |
switch (ch) | |
{ | |
case D: | |
LP5569_SetLED_D(j); | |
break; | |
case E: | |
LP5569_SetLED_E(j); | |
break; | |
case F: | |
LP5569_SetLED_F(j); | |
break; | |
case C: | |
LP5569_SetLED_C(j); | |
break; | |
case O: | |
LP5569_SetLED_O(j); | |
break; | |
case N: | |
LP5569_SetLED_N(j); | |
break; | |
case ATTRACT: | |
case COMPLETE: | |
default: | |
break; | |
} | |
SysTick_DelayTicks(LED_HEARTBEAT_FADE_DELAY); | |
} | |
LPTMR_SetTimerPeriod(LPTMR0_PERIPHERAL, LED_HEARTBEAT_WAIT_DELAY); // Set time to sleep at LED maximum brightness | |
KL_Sleep(); // Go to sleep until our delay period is complete | |
if (g_nxhDetect) | |
{ | |
g_nxhDetect = false; | |
return; // exit the heartbeat if new badge data has just been received | |
} | |
for (j = LP5569_PWM; j >= 0; j -= 4) // ramp down from maximum defined brightness | |
{ | |
if (g_nxhDetect) | |
{ | |
g_nxhDetect = false; | |
return; // exit the heartbeat if new badge data has just been received | |
} | |
switch (ch) | |
{ | |
case D: | |
LP5569_SetLED_D(j); | |
break; | |
case E: | |
LP5569_SetLED_E(j); | |
break; | |
case F: | |
LP5569_SetLED_F(j); | |
break; | |
case C: | |
LP5569_SetLED_C(j); | |
break; | |
case O: | |
LP5569_SetLED_O(j); | |
break; | |
case N: | |
LP5569_SetLED_N(j); | |
break; | |
case ATTRACT: | |
case COMPLETE: | |
default: | |
break; | |
} | |
SysTick_DelayTicks(LED_HEARTBEAT_FADE_DELAY); | |
} | |
} | |
/**************************************************************/ | |
// Utility function to read single byte from specified register of the I2C device | |
bool I2C_ReadRegister(I2C_Type *base, uint8_t device_addr, uint8_t reg_addr, uint8_t *rxBuff, uint32_t rxSize) | |
{ | |
i2c_master_transfer_t masterXfer; | |
memset(&masterXfer, 0, sizeof(masterXfer)); | |
masterXfer.slaveAddress = device_addr; | |
masterXfer.direction = kI2C_Read; | |
masterXfer.subaddress = reg_addr; | |
masterXfer.subaddressSize = 1; | |
masterXfer.data = rxBuff; | |
masterXfer.dataSize = rxSize; | |
masterXfer.flags = kI2C_TransferDefaultFlag; | |
/* direction=write : start+device_write;cmdbuff;xBuff; */ | |
/* direction=receive : start+device_write;cmdbuff;repeatStart+device_read;xBuff; */ | |
// does not return until the transfer succeeds or fails due to arbitration lost or receiving a NAK | |
if (I2C_MasterTransferBlocking(base, &masterXfer) == kStatus_Success) | |
return 0; | |
else | |
return 1; | |
} | |
/**************************************************************/ | |
// Utility function to write single byte to specified register of the I2C device | |
bool I2C_WriteRegister(I2C_Type *base, uint8_t device_addr, uint8_t reg_addr, uint8_t value) | |
{ | |
i2c_master_transfer_t masterXfer; | |
memset(&masterXfer, 0, sizeof(masterXfer)); | |
masterXfer.slaveAddress = device_addr; | |
masterXfer.direction = kI2C_Write; | |
masterXfer.subaddress = reg_addr; | |
masterXfer.subaddressSize = 1; | |
masterXfer.data = &value; | |
masterXfer.dataSize = 1; | |
masterXfer.flags = kI2C_TransferDefaultFlag; | |
/* direction=write : start+device_write;cmdbuff;xBuff; */ | |
/* direction=receive : start+device_write;cmdbuff;repeatStart+device_read;xBuff; */ | |
// does not return until the transfer succeeds or fails due to arbitration lost or receiving a NAK | |
if (I2C_MasterTransferBlocking(base, &masterXfer) == kStatus_Success) | |
return 0; | |
else | |
return 1; | |
} | |
/**************************************************************/ | |
// Utility function to read multiple bytes (rxSize) from the specified I2C device | |
bool I2C_ReadBulk(I2C_Type *base, uint8_t device_addr, uint8_t *rxBuff, uint32_t rxSize) | |
{ | |
i2c_master_transfer_t masterXfer; | |
memset(&masterXfer, 0, sizeof(masterXfer)); | |
masterXfer.slaveAddress = device_addr; | |
masterXfer.direction = kI2C_Read; | |
masterXfer.subaddress = 0; | |
masterXfer.subaddressSize = 0; | |
masterXfer.data = rxBuff; | |
masterXfer.dataSize = rxSize; | |
masterXfer.flags = kI2C_TransferDefaultFlag; | |
/* direction=write : start+device_write;cmdbuff;xBuff; */ | |
/* direction=receive : start+device_write;cmdbuff;repeatStart+device_read;xBuff; */ | |
// does not return until the transfer succeeds or fails due to arbitration lost or receiving a NAK | |
if (I2C_MasterTransferBlocking(base, &masterXfer) == kStatus_Success) | |
return 0; | |
else | |
return 1; | |
} | |
/**************************************************************/ | |
// Utility function to write multiple bytes (txSize) to the specified I2C device | |
bool I2C_WriteBulk(I2C_Type *base, uint8_t device_addr, uint8_t *txBuff, uint32_t txSize) | |
{ | |
i2c_master_transfer_t masterXfer; | |
memset(&masterXfer, 0, sizeof(masterXfer)); | |
masterXfer.slaveAddress = device_addr; | |
masterXfer.direction = kI2C_Write; | |
masterXfer.subaddress = 0; | |
masterXfer.subaddressSize = 0; | |
masterXfer.data = txBuff; | |
masterXfer.dataSize = txSize; | |
masterXfer.flags = kI2C_TransferDefaultFlag; | |
/* direction=write : start+device_write;cmdbuff;xBuff; */ | |
/* direction=receive : start+device_write;cmdbuff;repeatStart+device_read;xBuff; */ | |
// does not return until the transfer succeeds or fails due to arbitration lost or receiving a NAK | |
if (I2C_MasterTransferBlocking(base, &masterXfer) == kStatus_Success) | |
return 0; | |
else | |
return 1; | |
} | |
/**************************************************************/ | |
// Release/clear the bus due to an error condition of SDA stuck LOW | |
// From UM10204 I2C bus specification and user manual, pg. 20 | |
// https://www.nxp.com/docs/en/user-guide/UM10204.pdf | |
void I2C_ReleaseBus(void) | |
{ | |
uint8_t i = 0, j; | |
// set SCL pin as GPIO | |
gpio_pin_config_t SCL_config = { | |
.pinDirection = kGPIO_DigitalOutput, | |
.outputLogic = 1U | |
}; | |
GPIO_PinInit(GPIOB, BOARD_INITPINS_SCL_PIN, &SCL_config); | |
PORT_SetPinMux(BOARD_INITPINS_SCL_PORT, BOARD_INITPINS_SCL_PIN, kPORT_MuxAsGpio); | |
SysTick_DelayTicks(10); | |
// send 9 pulses on SCL | |
// the slave device that is holding the bus LOW should release it sometime within these clocks | |
for (i = 0; i < 9; i++) | |
{ | |
GPIO_PinWrite(GPIOB, BOARD_INITPINS_SCL_PIN, 0U); | |
for (j = 0; j < 10; ++j){}; // poor man's delay (us) | |
GPIO_PinWrite(GPIOB, BOARD_INITPINS_SCL_PIN, 1U); | |
for (j = 0; j < 10; ++j){}; // poor man's delay (us) | |
} | |
// set SCL back to I2C0_SCL | |
PORT_SetPinMux(BOARD_INITPINS_SCL_PORT, BOARD_INITPINS_SCL_PIN, kPORT_MuxAlt2); | |
SysTick_DelayTicks(10); | |
} | |
/**************************************************************/ | |
unsigned char Get_Random_Byte(void) // PRNG | |
{ | |
unsigned char sum = 0; | |
// This calculates parity on the selected bits (mask = 0xb4) | |
if(g_random & 0x80) | |
sum = 1; | |
if(g_random & 0x20) | |
sum ^= 1; | |
if(g_random & 0x10) | |
sum ^= 1; | |
if(g_random & 0x04) | |
sum ^= 1; | |
g_random <<= 1; | |
g_random |= sum; | |
return(g_random); | |
} | |
/**************************************************************/ | |
// print all 8 bits of a byte including leading zeros | |
// from https://forum.arduino.cc/index.php?topic=46320.0 | |
void Print_Bits(uint8_t myByte) | |
{ | |
for (uint8_t mask = 0x80; mask; mask >>= 1) | |
{ | |
if(mask & myByte) | |
PRINTF("1"); | |
else | |
PRINTF("0"); | |
} | |
} | |
/**************************************************************/ | |
// Reorder elements in an array of N size based on index array | |
// from https://www.tutorialcup.com/array/reorder-array-indexes.htm | |
void Reorder_Array(uint8_t *array, uint8_t *index, uint8_t N) | |
{ | |
size_t i; | |
uint8_t temp[N]; // temporary array | |
// array[i] should present at index[i] index | |
for (i = 0; i < N; i++) | |
{ | |
temp[index[i]] = array[i]; | |
} | |
for (i = 0; i < N; i++) | |
{ | |
array[i] = temp[i]; | |
index[i] = i; | |
} | |
} | |
/**************************************************************/ | |
void SysTick_DelayTicks(uint32_t n) | |
{ | |
g_systickCounter = n; | |
while(g_systickCounter != 0U){}; // wait here until counter is done | |
} | |
/**************************************************************************** | |
********************* Interrupt Handlers *********************************** | |
***************************************************************************/ | |
void SysTick_Handler(void) | |
{ | |
if (g_systickCounter != 0U) | |
{ | |
g_systickCounter--; | |
} | |
} | |
/**************************************************************/ | |
void LPUART0_SERIAL_RX_TX_IRQHANDLER(void) | |
{ | |
volatile uint8_t data; | |
// If new data has arrived from the NXH... | |
if (kLPUART_RxDataRegFullFlag & LPUART_GetStatusFlags(LPUART0_PERIPHERAL)) | |
{ | |
data = LPUART_ReadByte(LPUART0_PERIPHERAL); | |
// If ring buffer isn't full, add the data | |
if (((nxhRxIndex + 1) % LPUART0_RING_BUFFER_SIZE) != nxhTxIndex) | |
{ | |
nxhRingBuffer[nxhRxIndex] = data; | |
nxhRxIndex++; | |
nxhRxIndex %= LPUART0_RING_BUFFER_SIZE; | |
} | |
} | |
// If the UART buffer has overrun and can't store incoming data... | |
if (kLPUART_RxOverrunFlag & LPUART_GetStatusFlags(LPUART0_PERIPHERAL)) | |
{ | |
data = LPUART_ReadByte(LPUART0_PERIPHERAL); // dummy read to clear buffer (could cause misalignment of data packet) | |
LPUART_ClearStatusFlags(LPUART0_PERIPHERAL, kLPUART_RxOverrunFlag); | |
} | |
} | |
/**************************************************************/ | |
void LPTMR0_IRQHandler(void) | |
{ | |
g_lptmrFlag = true; // Set state of global variable | |
LPTMR_ClearStatusFlags(LPTMR0_PERIPHERAL, kLPTMR_TimerCompareFlag); | |
} | |
/**************************************************************/ | |
void PORTB_PORTC_PORTD_PORTE_IRQHandler(void) | |
{ | |
if (GPIO_PortGetInterruptFlags(GPIOC_GPIO)) // If interrupt was from Port C (NXH_DETECT) | |
{ | |
g_nxhDetect = true; // Set state of global variable | |
GPIO_PortClearInterruptFlags(GPIOC_GPIO, 1U << BOARD_INITPINS_NXH_DETECT_PIN); // Clear external interrupt flag | |
} | |
else // Otherwise, interrupt must have been from Port E (KL_RX) via USB-to-Serial connection | |
{ | |
GPIO_PortClearInterruptFlags(GPIOE, 1U << BOARD_INITPINS_KL_RX_PIN); // Clear external interrupt flag | |
} | |
} | |
/**************************************************************/ | |
// The End! |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment