Skip to content

Instantly share code, notes, and snippets.

@teknogeek
Created August 9, 2019 17:46
Show Gist options
  • Save teknogeek/c13338f7d645cc2762d63b7807e2f9f8 to your computer and use it in GitHub Desktop.
Save teknogeek/c13338f7d645cc2762d63b7807e2f9f8 to your computer and use it in GitHub Desktop.
DC27 badge code
/*
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