208 lines
6.0 KiB
C++
208 lines
6.0 KiB
C++
/*
|
|
* File: main.c
|
|
* Author: amr
|
|
*
|
|
* Created on September 26, 2020, 3:32 AM
|
|
* 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/.
|
|
*/
|
|
|
|
#include "fuses.hpp"
|
|
#include <xc.h>
|
|
#include <sys/attribs.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include "i2c.hpp"
|
|
#include "FIFO.hpp"
|
|
#include "FlashStorage.hpp"
|
|
#include "UART.hpp"
|
|
|
|
#define STATE_IDLE 0
|
|
#define STATE_PLAYING 1
|
|
#define STATE_PROGRAMMING 2
|
|
|
|
uint8_t globalState = 1;
|
|
|
|
// Flash data variables
|
|
volatile uint32_t flashAddress = 0;
|
|
volatile uint32_t byteCount = 0;
|
|
volatile uint32_t readByteCount = 0;
|
|
volatile uint8_t currentClip = 0;
|
|
|
|
// Audio clip address table
|
|
#define AUDIO_SIZE 509176
|
|
|
|
const uint32_t audioSize[16] = { 0x0000ba8d, 0x0000d063, 0x000069fa, 0x00007c3e,
|
|
0x000030b4, 0x0000c676, 0x000020fd, 0x00005314, 0x00002db8, 0x00002fbd,
|
|
0x000046c0, 0x0000bc92, 0x0000a257, 0x00002830, 0x00011790, 0x0000a5b7
|
|
};
|
|
const uint32_t audioAddress[16] = { 0x00, 0x0000ba8d, 0x00018af0, 0x0001f4ea,
|
|
0x00027128, 0x0002a1dc, 0x00036852, 0x0003894f, 0x0003dc63, 0x00040a1b,
|
|
0x000439d8, 0x00048098, 0x00053d2a, 0x0005df81, 0x000607b1, 0x00071f41
|
|
};
|
|
|
|
#define dacAddress 0x62
|
|
|
|
void _delay(uint32_t cycles) {
|
|
cycles += (uint32_t) ((double) cycles * 1.4);
|
|
while (cycles--);
|
|
return;
|
|
}
|
|
|
|
uint8_t initSystem(void) {
|
|
/* set up GPIO */
|
|
SYSKEY = 0x0;
|
|
SYSKEY = 0xAA996655;
|
|
SYSKEY = 0x556699AA;
|
|
CFGCON &= ~(1 << 13); // unlock PPS
|
|
|
|
// Pin configs
|
|
/* SPI:
|
|
* SO = RB5
|
|
* SI = RC8
|
|
* CS = RA4
|
|
*
|
|
* I2C:
|
|
* SDA: RB2
|
|
* SCL: RB3
|
|
*
|
|
* UART:
|
|
* TX = RC5
|
|
* RX = RC3
|
|
*/
|
|
SDI1R = 0b0110; // RC8
|
|
U1RXR = 0b0111; // RC3
|
|
RPC0R = 0b0101; // OC1 on LED
|
|
RPB5R = 0b0011; // SD01
|
|
RPC5R = 0b0001; // U1TX
|
|
|
|
OSCCONCLR = 0x00000010; // idle mode
|
|
|
|
SYSKEY = 0x12345678; // lock SYSKEY
|
|
|
|
ANSELACLR = 0xFFFF;
|
|
ANSELBCLR = 0xFFFF;
|
|
ANSELCCLR = 0xFFFF; // clear all analog functions
|
|
// set up LED
|
|
TRISCCLR = 0x01; // PC0 is LED
|
|
PORTCCLR = 0x01; // turn LED off at boot
|
|
|
|
// set up button with change interrupt
|
|
TRISASET = 0x200; // PA9 is button input
|
|
CNENASET = 0x200; // enable on PA9
|
|
CNPUASET = 0x200; // pullup enable
|
|
CNCONASET = 0x8000; // enable for port A
|
|
|
|
IFS1CLR = 0x2000;
|
|
IPC8SET = 0xC0000; // priority 3
|
|
IEC1SET = 0x2000; // enable pin change interrupt port A
|
|
|
|
/* Set up SPI1 */
|
|
TRISACLR = 0x10;
|
|
PORTASET = 0x10;
|
|
TRISBCLR = 0x4000; // RB14 is SCK1
|
|
SPI1BRG = 1; // 2.5MHz
|
|
SPI1CONbits.ENHBUF = 0; // enable enhanced buffer
|
|
SPI1CONbits.MSTEN = 1; // master mode
|
|
SPI1STATCLR = (1 << 6); // clear SPIROV
|
|
SPI1CONbits.ON = 1; // enable SPI
|
|
|
|
// set up timer (needs to be about 8kHz)
|
|
T2CON = 0x0000; // timer continues in idle mode
|
|
PR2 = 0x04E2; // 1250 = 10e6/8e3
|
|
TMR2 = 0x0000;
|
|
|
|
IPC2bits.T2IP = 7; // priority 7
|
|
IFS0bits.T2IF = 0;
|
|
IEC0bits.T2IE = 1; // enable timer interrupt
|
|
|
|
INTCONSET = _INTCON_MVEC_MASK;
|
|
return 0;
|
|
}
|
|
|
|
void updateDAC(i2c *bus, uint16_t data) {
|
|
uint8_t bytes[2] = {0x00, 0x00};
|
|
bytes[0] = (uint8_t) (data >> 8) & 0x0F; // clear top half of top byte
|
|
bytes[1] = (uint8_t) (data & 0x00FF);
|
|
bus->transact(dacAddress, bytes, NULL, 2, MULTIPLE_SEND);
|
|
}
|
|
|
|
int main(void) {
|
|
volatile uint16_t audioSampleShifted = 0;
|
|
volatile uint8_t audioSample = 0;
|
|
initSystem();
|
|
|
|
FIFO audioBuffer(64);
|
|
UART serial(19200, 10e6);
|
|
FlashStorage flash(&audioBuffer, &serial);
|
|
i2c i2cBus(400e3, 10e6);
|
|
|
|
// this needs to be as early as possible, but after SPI and UART setup
|
|
if(!(LATC & 0x20)) {
|
|
// if button is held down at boot, it's programming mode
|
|
globalState = STATE_PROGRAMMING;
|
|
flash.program();
|
|
}
|
|
|
|
const char testString[20] = "Welcome to Canada\n\r";
|
|
// main state machine loop
|
|
//flash.program();
|
|
globalState == STATE_PLAYING;
|
|
while (1) {
|
|
__builtin_disable_interrupts();
|
|
if (globalState == STATE_PLAYING) {
|
|
if (audioBuffer.usedSize < 16) {
|
|
// read 48 bytes into buffer
|
|
flash.read(audioAddress[currentClip] + readByteCount, 48);
|
|
readByteCount += 48;
|
|
}
|
|
audioSample = audioBuffer.read();
|
|
byteCount++;
|
|
if (byteCount >= audioSize[currentClip]) {
|
|
byteCount = 0;
|
|
readByteCount = 0;
|
|
//globalState = STATE_IDLE;
|
|
}
|
|
// Load sample into DAC
|
|
audioSampleShifted = (uint16_t) (audioSample << 4);
|
|
//updateDAC(&i2cBus, audioSampleShifted);
|
|
serial.send(audioSample);
|
|
__builtin_enable_interrupts();
|
|
T2CONbits.ON = 1; // enable timer
|
|
asm volatile("wait");
|
|
} else if (globalState == STATE_IDLE) {
|
|
for (uint8_t i = 0; i < 20; i++) {
|
|
serial.send(testString[i]);
|
|
}
|
|
__builtin_enable_interrupts();
|
|
T2CONbits.ON = 1; // enable timer
|
|
asm volatile("wait");
|
|
}
|
|
}
|
|
}
|
|
|
|
extern "C" {
|
|
|
|
void __ISR(_TIMER_2_VECTOR, IPL7AUTO) Timer2Handler(void) {
|
|
T2CONbits.ON = 0;
|
|
IFS0bits.T2IF = 0;
|
|
}
|
|
|
|
void __ISR(_CHANGE_NOTICE_VECTOR, IPL3AUTO) PinChangeHandler(void) {
|
|
if((IFS1 & 0x2000) || (CNSTATA & 0x200)) {
|
|
if(PORTA) ; // dummy read to reset change notice
|
|
// button is pushed
|
|
if (globalState == STATE_PLAYING) {
|
|
return; // do nothing, we're already playing
|
|
}
|
|
// pick a random number
|
|
currentClip += 1;
|
|
currentClip = currentClip % 8; // roll back around
|
|
globalState = STATE_PLAYING;
|
|
IFS1CLR = 0x00002000;
|
|
}
|
|
}
|
|
|
|
} // end extern C
|