// FrznChess MCU code // © 2025 A.M. Rowsell // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // This Source Code Form is "Incompatible With Secondary Licenses", as // defined by the Mozilla Public License, v. 2.0. // PIC32MX270F256B Configuration Bit Settings // 'C' source line config statements // DEVCFG3 #pragma config USERID = 0xBEEF // Enter Hexadecimal value (Enter Hexadecimal value) #pragma config PMDL1WAY = OFF // Peripheral Module Disable Configuration (Allow multiple reconfigurations) #pragma config IOL1WAY = OFF // Peripheral Pin Select Configuration (Allow multiple reconfigurations) #pragma config FUSBIDIO = ON // USB USID Selection (Controlled by the USB Module) #pragma config FVBUSONIO = ON // USB VBUS ON Selection (Controlled by USB Module) // DEVCFG2 #pragma config FPLLIDIV = DIV_2 // PLL Input Divider (2x Divider) #pragma config FPLLMUL = MUL_24 // PLL Multiplier (24x Multiplier) 96MHz #pragma config UPLLIDIV = DIV_2 // USB PLL Input Divider (2x Divider) 48MHz #pragma config UPLLEN = ON // USB PLL Enable (Enabled) #pragma config FPLLODIV = DIV_8 // System PLL Output Clock Divider (PLL Divide by 8) 12MHz // DEVCFG1 #pragma config FNOSC = FRCPLL // Oscillator Selection Bits (Fast RC Osc with PLL) #pragma config FSOSCEN = ON // Secondary Oscillator Enable (Enabled) #pragma config IESO = ON // Internal/External Switch Over (Enabled) #pragma config POSCMOD = OFF // Primary Oscillator Configuration (Primary osc disabled) #pragma config OSCIOFNC = OFF // CLKO Output Signal Active on the OSCO Pin (Disabled) #pragma config FPBDIV = DIV_2 // Peripheral Clock Divisor (Pb_Clk is Sys_Clk/2) #pragma config FCKSM = CSDCMD // Clock Switching and Monitor Selection (Clock Switch Disable, FSCM Disabled) #pragma config WDTPS = PS1048576 // Watchdog Timer Postscaler (1:1048576) #pragma config WINDIS = OFF // Watchdog Timer Window Enable (Watchdog Timer is in Non-Window Mode) #pragma config FWDTEN = OFF // Watchdog Timer Enable (WDT Disabled (SWDTEN Bit Controls)) #pragma config FWDTWINSZ = WINSZ_25 // Watchdog Timer Window Size (Window Size is 25%) // DEVCFG0 #pragma config JTAGEN = ON // JTAG Enable (JTAG Port Enabled) #pragma config ICESEL = ICS_PGx2 // ICE/ICD Comm Channel Select (Communicate on PGEC2/PGED2) #pragma config PWP = OFF // Program Flash Write Protect (Disable) #pragma config BWP = OFF // Boot Flash Write Protect bit (Protection Disabled) #pragma config CP = OFF // Code Protect (Protection Disabled) // #pragma config statements should precede project file includes. // Use project enums instead of #define for ON and OFF. #include #include // for ISR macros #include #include "Board.hpp" #include "Piece.hpp" #include "NeoPixel.hpp" #define F_CPU 12000000UL // 12MHz #define F_PER 6000000UL // 6MHz #define SPI_BAUD 1000000 // 1MHz hopefully volatile uint8_t spi_rx_buffer[8]; volatile uint32_t usbBDT[512] __attribute__((aligned(512))); // USB buffer description table // dummy open to get rid of linker error extern "C" int open(const char *buf, int flags, int mode) { // Always return failure — no file system. return -1; } void sendChar(uint8_t c) { U1TXREG = c; while(!U1STAbits.TRMT); // wait for transmission return; } uint8_t appInfo(uint8_t *msg, uint16_t len) { uint16_t offset = 0; do { sendChar(*(msg + offset)); offset++; } while(--len); return 0; } /* * Pin mapping: * * UART: (for debug) * U1TX = RA0 * * SPI: (to 74HC165) * Shift/Load = RB9 * Shift Clock SCK1 = RB14 * Data In SDI1 = RB8 * * NeoPixel: * Pixel Data: RA4 * * USB: * RB10: D+ * RB11: D- * * */ #define SL_TRIS TRISBbits.TRISB9 #define SL_LAT LATBbits.LATB9 uint8_t initSystem(void) { /* set up GPIO */ SYSKEY = 0x0; SYSKEY = 0xAA996655; SYSKEY = 0x556699AA; CFGCON &= ~(1 << 13); // unlock PPS SDI1R = 0b0100; // RB8 RPA0R = 0b0001; // U1TX SYSKEY = 0x12345678; // lock SYSKEY ANSELBCLR = 0xFFFF; // port B all digital SL_TRIS = 0; // RA0 as output SL_LAT = 1; // set high /* Set up SPI1 */ SPI1CON = 0; // reset SPI config SPI1BRG = (F_PER / (2 * SPI_BAUD)) - 1; // calculate for 1MHz SPI1STATCLR = _SPI1STAT_SPIROV_MASK; // Clear overflow SPI1CONbits.MSTEN = 1; // Enable Master mode SPI1CONbits.CKP = 0; // Clock idle low SPI1CONbits.CKE = 1; // Transmit on falling edge SPI1CONbits.SMP = 1; // Input sampled at end SPI1CONbits.ON = 1; // Enable SPI /* set up UART */ U1BRG = 9; // 9600 baud (was 38 @ 24MHz) U1STAbits.UTXEN = 1; // enable transmitter U1MODEbits.ON = 1; // enable UART /* set up DMA */ // clear all 4 DMA channel flags & IE IEC1CLR = 0xF0000000; IFS1CLR = 0xF0000000; DMACONbits.ON = 1; //enable DMA controller // === DMA Channel 1 Init (RX from SPI1BUF to spi_rx_buffer[]) === DCH0CON = 0; // Reset channel config DCH0ECON = 0; DCH0SSA = KVA_TO_PA(&SPI1BUF); // Source: SPI1BUF DCH0DSA = KVA_TO_PA(spi_rx_buffer); // Destination: receive buffer DCH0SSIZ = 1; // Source size = 1 byte DCH0DSIZ = sizeof(spi_rx_buffer); // Destination size = 8 bytes DCH0CSIZ = 1; // Cell transfer size = 1 byte DCH0ECONbits.CHSIRQ = _SPI1_VECTOR; // Trigger on SPI1 receive DCH0ECONbits.SIRQEN = 1; // Enable IRQ trigger DCH0INTCLR = 0x00FF00FF; // Clear all interrupt flags DCH0INTbits.CHBCIE = 1; // Enable block complete interrupt DCH0CONbits.CHPRI = 2; // Priority 2 (TX is higher) DCH0CONbits.CHAEN = 1; // Auto-enable on trigger DCH0CONbits.CHEN = 1; // Enable channel return 0; } void initInterrupts(void) { INTCONbits.MVEC = 1; // Enable multi-vector interrupts __builtin_enable_interrupts(); IPC10bits.DMA0IP = 3; // Priority level 3 IFS1CLR = _IFS1_DMA0IF_MASK; // Clear interrupt flag IEC1SET = _IEC1_DMA0IE_MASK; // Enable DMA1 interrupt } void startSPI_DMA_Transfer(void) { // Pulse PL low to latch inputs from 74HC165 SL_LAT = 0; asm volatile("nop; nop; nop;"); // small delay SL_LAT = 1; DCH0CONbits.CHEN = 1; for(int i = 0; i < 8; i++) { while(SPI1STATbits.SPITBF); // Wait if TX buffer full SPI1BUF = 0x00; // Send dummy byte to clock in data } return; } extern "C" int main(void) { initSystem(); initInterrupts(); Board gameBoard; NeoPixel boardLights(64); while(1) { } } // === Interrupt Service Routine for DMA0 (RX complete) === extern "C" void __ISR(_DMA0_VECTOR, IPL3AUTO) DMA0Handler(void) { __builtin_disable_interrupts(); // stop additional ints from firing if(DCH0INTbits.CHBCIF) { DCH0INTCLR = _DCH0INT_CHBCIF_MASK; // Clear block complete flag // DMA RX completed — spi_rx_buffer[] now contains the data } IFS1CLR = _IFS1_DMA0IF_MASK; // Clear global DMA0 IRQ flag } //extern "C" void __ISR(_USB1_VECTOR, IPL4AUTO) USBHandler(void) { // if (USBE2CSR1bits.RXPKTRDY) { // int count = USB_receive_EP2(); // // Echo back // USB_send_EP2(EP[2].rx_buffer, count); // USBCSR1bits.EP2RXIF = 0; // } // IFS1bits.USBIF = 0; // clear USB interrupt flag //}