sr. PIC32MX Sound Recorder
The sound recorder is based on two PIC32MX microcontrollers. PIC32MX250F128B is used as A/D codec while PIC32MX270F256B gathers and records digitalized sound to a file on an SD card. PIC32MX250F128B buffers A/D data which allows PIC32MX270F256B to maintain communication to the SD card.
PIC32MX270F256B is SPI master. Sufficient data speed must be sustained in both SPI data links to PIC32MX250F128B and to the SD card to enable smooth sound recording. This example records sound at approximate rate of 8000 16-bit samples a second.
HEX files for PIC32MX270F256B and PIC32MX250F128B are available here.
Click on the schematics to enlarge it
Click on the photo to enlarge it
Here are some interesting parts of the source code:
PIC32MX250F128B codec source code (main.c specifics):
void SPI1ClearReceiveBuffer(void){
DWORD wd;
while((SPI1STAT&0xFF000000)>0)wd=SPI1BUF; // dummy read
}
void SWSPI1Init(void){
OC3CON=0;
OC4CON=0;
SPI1CON=0; // Disable SP1
ANSELA=0; // All digital
ODCA=0; // No open drains
ANSELB=0;
ODCB=0;
LATBbits.LATB4=0; // SS1 = RB4 = default = 0
LATAbits.LATA4=0; // SDO1 = RA4 = default = 0
TRISAbits.TRISA4=0; // Clear bit SDO1 = RA4 = output
TRISBbits.TRISB4=0; // Clear bit SS1 = RB4 = output
TRISBbits.TRISB14=1; // Set bit SCK1 = RB14 = input
TRISBbits.TRISB5=1; // Set bit SDI1 = RB5 = input
TRISB=0xFFFF;
SYSKEY=0; // Lock
SYSKEY=0xAA996655;
SYSKEY=0x556699AA; // Unlock
CFGCONbits.IOLOCK=0; // Unlock PSS
SYSKEY=0; // Lock
RPA4R=3; // Map SDO1 = RPA4
SS1R=2; // Map RB4 = SS1
SDI1R=1; // Map RB5 = SDI1
}
void SPI1Init(void){ // HW SPI
DWORD x;
IEC1CLR=0x70;
SPI1CON=0;
x=SPI1BUF;
IFS1CLR=0x70; // Clear interrupt flags
IPC7CLR=0xF0000000; // Clear SPI1 interrupt priorities and sub priorities
SPI1BRG=3; // CLK freq. = (FPB = CPU freq.) / 4 * (1+3) = FPB / 16
SPI1STAT=0x40; // clear overflow
SPI1CON2=0x300; // set framed mode operation
SPI1CON=0x00018507; // Enable SPI and framing support = OFF/ one clock pulse at start of R/W operation
}
int SPI1ReadReg(BYTE addr){
DWORD x;
SPI1ClearReceiveBuffer();
PORTBbits.RB7=0; // NSS=0
x=addr;
x<<=8;
SPI1BUF=x;
x=spi_max_wait;
while((SPI1STAT&0x800)&&(x>0))x--; // Wait SPI BUSY bit to be cleared
if(x<1)return -1; // timeout error
PORTBbits.RB7=1; // NSS=1
x=SPI1BUF;
x&=0xFF;
return x;
}
int SPI1WriteReg(BYTE addr,BYTE dta){
DWORD x;
SPI1ClearReceiveBuffer();
PORTBbits.RB7=0; // NSS=0
x=addr;
x|=0x80;
x<<=8;
x|=dta;
SPI1BUF=x;
x=spi_max_wait;
while((SPI1STAT&0x800)&&(x>0))x--; // Wait SPI BUSY bit to be cleared
PORTBbits.RB7=1; // NSS=1
SPI1ClearReceiveBuffer();
if(x<1)return -1; // timeout error
return 0;
}
int SPI1WriteData(DWORD dta){
DWORD x;
SPI1ClearReceiveBuffer();
PORTBbits.RB7=0; // NSS=0
x=dta;
x|=0x8000; // Set write flag
SPI1BUF=x;
x=spi_max_wait;
while((SPI1STAT&0x800)&&(x>0))x--; // Wait SPI BUSY bit to be cleared
PORTBbits.RB7=1; // NSS=1
SPI1ClearReceiveBuffer();
if(x<1)return -1; // timeout error
return 0;
}
void APP_Initialize ( void )
{
/* Place the App state machine in its initial state. */
appData.state = APP_STATE_INIT;
/* TODO: Initialize your application's state machine and other
* parameters.
*/
}
void SPI1_Restart(void){
SPI1CON=0;
SPI1CON=0x00018507;
}
void ADCInit(void){
AD1CON1=ADC_CON1; //0x00E4; // Disable ADC1 and start making settings
AD1CON2=ADC_CON2; //0x41E;
AD1CON3=ADC_CON3; //0x400;
AD1CHS=0;
AD1CHSbits.CH0NA=0;
AD1CHSbits.CH0SA=0; // AD1 A = channel AN0
AD1CHSbits.CH0NB=0;
AD1CHSbits.CH0SB=0; // AD1 B = channel AN0
AD1CON1bits.ADON=0;
AD1CON1bits.ASAM=0;
AD1CON1bits.FORM=2;
AD1CON1bits.SIDL=0;
AD1CON1bits.SSRC=0; // MANUAL
AD1CON1bits.CLRASAM=0;
AD1CON1bits.SAMP=0;
AD1CON2bits.VCFG=0;
AD1CON2bits.CSCNA=1;
AD1CON2bits.ALTS=0;
AD1CON2bits.BUFM=0;
AD1CON2bits.OFFCAL=0;
AD1CON2bits.SMPI=0;
AD1CON3bits.SAMC=4;
AD1CON3bits.ADCS=0x4F;
AD1CON3bits.ADRC=0;
AD1CSSL=1;// select channel AN0
// AD1 ? ADC1 Convert done 28 23 IFS0<28> IEC0<28> IPC5<28:26> IPC5<25:24> Yes
// 0001 0000 0000 0000 0000 0000 0000 0000
IPC5bits.AD1IS = 3; //Set Priority & Sub priority to 7 <28:26><25:24> = 111 111 --> 0001 1111
IPC5bits.AD1IP = 7;
IFS0bits.AD1IF=0;// = 0x1000;// Ensure the interrupt flag is cleared
IEC0bits.AD1IE=1;// Disable ADC interrupt
AD1CON1bits.FORM=2;
AD1CON1SET=0x8000; // Enable ADC1
AD1CON1bits.SAMP = 1; // start sampling ...
};
void ADC_Tasks(void){
AD1CON1bits.SAMP = 0; // start Converting
}
void TMR5Init(){
T4CON=0x0030; // Disable timer 4 , prescaller = 8
TMR4=0; // Reset timer 4 value
PR4=600; // Set comparator = 5 --> 8 * 5 = 40 --> 1 interrupt / us --> Sampling frequency = 1 MHz
IPC4bits.T4IS = 0; //Set Priority & Sub priority to 7 <28:26><25:24> = 111 111 --> 0001 1111
IPC4bits.T4IP = 1;
IFS0bits.T4IF=0; // Reset timer 4 interrupt flag
IEC0bits.T4IE=0; // Enable timer 4 interrupt
T4CONbits.ON=1;
}
void APP_Tasks ( void )
{
DWORD x;
DWORD rx_cnt;
DWORD tx_cnt;
/* Check the application's current state. */
switch ( appData.state )
{
/* Application's initial state. */
case APP_STATE_INIT:
{
ADCInit();
SPI1Init(); // Initialize SPI1
for(x=0;x<adcBUF_max;x++)appData.adcBUF[x]=0;
appData.write_ptr=0;
appData.read_ptr=0;
appData.adcBUF_cnt=0;
appData.xyz=0xABCD;
appData.adcStart=0;
appData.adcStopAtBufFull=0; // Stop when BUFFER is full
appData.Lock=1;
appData.state = APP_STATE_SERVICE_TASKS;
break;
}
case APP_STATE_SERVICE_TASKS: // DON NOT USE DEBUG MODE FOR THIS CODE! SP1BUF WILL ALWAYS READ 0 IN DEBUG MODE!
{
TMR5Lock=0;
appData.adcStart=0;
TMR5Init();
while(1){ // endless loop
if((TMR4%100)==0){
AD1CON1bits.SAMP = 0; // Start sampling
}
rx_cnt=SPI1STAT>>24;
tx_cnt=(SPI1STAT>>16)&0xFF;
if(appData.adcBUF_cnt>512){
PORTBbits.RB4=0; // Buffer not empty
} else {
PORTBbits.RB4=1; // Buffer empty
}
if(rx_cnt>0){
x=SPI1BUF; // read command or address
switch(x){
case 0xFFFF: // response test
appData.xyz=0xADC1;
PORTBbits.RB7=~PORTBbits.RB7;
break;
case 0xFFFE:
if(appData.read_ptr>=adcBUF_max){ // buffer pointer cycling
appData.read_ptr=0;
}
appData.xyz=appData.adcBUF[appData.read_ptr]; // gets buffer content at a desired location
appData.adcBUF[appData.read_ptr]=0x8000;
if(appData.adcBUF_cnt>0){
appData.read_ptr++;
appData.adcBUF_cnt--; // Decrease buffer sample counter
}
break;
case 0xFFFD:
appData.xyz=appData.write_ptr; // Get write pointer
break;
case 0xFFFC:
appData.xyz=appData.read_ptr; // Get read pointer
break;
case 0xFFFB: // Get buffer sample count
x=spi_max_wait;
while((SPI1STAT&0x800)&&(x>0))x--; // Wait SPI BUSY bit to be cleared
SPI1BUF=appData.adcBUF_cnt&0xFFFF; // lower byte
appData.xyz=appData.adcBUF_cnt>>16; // higher byte
break;
case 0xFFFA:
appData.adcStopAtBufFull=0;
appData.xyz=0xABF0;
PORTBbits.RB7=~PORTBbits.RB7;
break;
case 0xFFF9:
appData.adcStopAtBufFull=1;
appData.xyz=0xABF1;
PORTBbits.RB7=~PORTBbits.RB7;
break;
case 0xFFF8: // Reset SP1
SPI1_Restart();
appData.xyz=0xAD00;
break;
case 0xFFF7: // Reset buffer
appData.read_ptr=0;
appData.write_ptr=0;
appData.adcBUF_cnt=0;
break;
case 0xFFF6: // Reset read ptr
appData.read_ptr=0;
break;
default: // get arbitrary buffer location data
while(x>=adcBUF_max){ // buffer pointer cycling
x-=adcBUF_max;
}
appData.xyz=appData.adcBUF[x]; // Get buffer content at a desired location
appData.read_ptr=x;
}
while(((SPI1STAT>>16)&0xFF)>6);
x=spi_max_wait;
while((SPI1STAT&0x800)&&(x>0))x--; // Wait SPI BUSY bit to be cleared
SPI1BUF=appData.xyz; // Answer
}
}
// IEC0bits.T5IE=1;
//if(DRV_SPI_Status
break;
}
default:
{
break;
}
}
}
PIC32MX270F256B codec source code (app.c specifics):
WORD GetADval(BYTE adnum){
WORD w,wo,wd;
w=0;
wo = adnum & 0x1F;
wd=1U;
if(wo>0)wd<<=wo; // A/D channel number
AD1CON1 = 0x0000; // SAMP bit = 0 ends sampling ...
// and starts converting
AD1CSSL = wd; // Enable A/D channel
wd=wo;
wd<<=16; // multiply wd by 0x10000 = 65536
AD1CHS = wd; // Select ANx, where x = ReceivedDataBuffer[1]&0xF
// in this example RB2/AN2 is the input
AD1CON3 = 0x0002; // Manual Sample, Tad = internal 6 TPB
AD1CON2 = 0;
AD1CON1SET = 0x8000; // turn ADC ON
AD1CON1SET = 0x0002; // start sampling ...
for(w=0;w<1000;w++); //Sample delay, conversion start automatically
AD1CON1CLR = 0x0002; // start Converting
while (!(AD1CON1 & 0x0001));// conversion done?
ADvalX = ADC1BUF0;
return ADvalX;
}
void APP_Reset ()
{
appData.rdComplete = true;
appData.wrComplete = true;
}
static void ToggleLedCallBack( uintptr_t context,uint32_t alarmCount )
{
WORD w;
w=GetADval(0);
// w=GLcnt++;
appData.sampleResult = w;
appData.ADCInt = true;
appData.data[appData.dataWritePtr++]=w; // Write to buffer
appData.dataCnt++;
if(appData.dataWritePtr>=dataMaxBuff){ // Cycle date write pointer
appData.dataWritePtr=0;
}
}
void APP_Initialize ( void )
{
/* Place the App state machine in its initial state. */
appData.state = APP_STATE_INIT_SPI;
/* Set device layer handle as invalid */
appData.usbDeviceHandle = USB_DEVICE_HANDLE_INVALID;
appData.masterState = APP_STATE_SPI1_MASTER_WR;
appData.slaveState = APP_STATE_SPI2_SLAVE_RD;
appData.drvSPI1Handle= DRV_HANDLE_INVALID;
appData.drvSPI2Handle= DRV_HANDLE_INVALID;
}
/******************************************************************************
Function:
void APP_Tasks ( void )
Remarks:
See prototype in app.h.
*/
void ADC_Tasks(void){
AD1CON1bits.SAMP = 0; // start Converting
uint32_t x=SPI2BUF;
SPI2BUF=cnt_i;
if(cnt_i>100)cnt_i=0;
}
void SPI2Init(void){ // HW SPI
DWORD x;
IEC1CLR=0x1C00;
SPI2CON=0;
x=SPI2BUF;
IFS1CLR=0x1C00; // Clear interrupt flags
IPC9CLR=0xF; // Clear SPI1 interrupt priorities and sub priorities
SPI2BRG=1; // CLK freq. = (FPB = CPU freq.) / 4 * (1+3) = FPB / 16
SPI2STAT=0x40; // clear overflow
SPI2CON2=0x300; // set framed mode operation
SPI2CON=0x00018527; // Enable SPI and framing support = OFF/ one clock pulse at start of R/W operation
// SPI2ClearReceiveBuffer();
/*/
PIC.MemWrite(IEC1_CLR, &H1C00) ' disable SPI2 interrupts
PIC.MemWrite(SPI2CON, 0) ' stop SPI operation
PIC.MemRead(SPI2BUF) ' clear recive buffer
PIC.MemWrite(IFS1_CLR, &H1C00) ' clear SPI2 interrupt flags
PIC.MemWrite(IPC9_CLR, &HF)
PIC.MemWrite(SPI2BRG, 1) ' CLK freq. = (FPB = CPU freq.) / 4
PIC.MemWrite(SPI2STAT, &H40) ' clear overflow
PIC.MemWrite(SPI2CON2, &H300) ' set framed mode operation
PIC.MemWrite(SPI2CON, &H18527)
*/
}
int32_t SPI2ReadData(WORD i){
// SPI1ClearReceiveBuffer();
DWORD x;
PORTBbits.RB7=0; // NSS=0
SPI2BUF=i;
x=spi_max_wait;
while((SPI2STAT&0x800)&&(x>0))x--; // Wait SPI BUSY bit to be cleared
if(x<1)return -1; // timeout error
PORTBbits.RB7=1; // NSS=1
i=SPI1BUF;
return i;
}
void SPI2ClearReceiveBuffer(void){
DWORD wd;
while((SPI2STAT&0xFF000000)>0)wd=SPI2BUF; // Initiate dummy ready --> Enhanced buffer mode must te selected for this rutine to work properly
// wd=spi_max_wait;
// while((SPI1STAT&800)&&(wd>0))wd--; // Wait SPI BUSY bit to be cleared
}
void APP_Tasks ( void )
{
DWORD x,i,j=0,k;
int n;
/* Check the application's current state. */
switch ( appData.state )
{
/* Application's initial state. */
case APP_STATE_INIT_SPI:
{
// appData.drvSPI1Handle = DRV_SPI_Open( DRV_SPI_INDEX_1,DRV_IO_INTENT_READWRITE );
// if(appData.drvSPI1Handle != DRV_HANDLE_INVALID)
// {
SPI2Init();
appData.state = APP_STATE_INIT;
appData.FileOpenCloseCNT = 0;
// }
}
break;
case APP_STATE_INIT:
{
if(SYS_FS_Mount("/dev/mmcblka1", "/mnt/myDrive", FAT, 0, NULL) != 0)
{
/* The disk could not be mounted. Try
* mounting again untill success. */
appData.state = APP_STATE_INIT;
}
else
{
/* Mount was successful. Unmount the disk, for testing. */
appData.state = APP_INITIAL_DISK;// APP_UNMOUNT_DISK;
}
//break;
// appData.usbDeviceHandle = USB_DEVICE_Open(USB_DEVICE_INDEX_0, DRV_IO_INTENT_READWRITE);
//if(appData.usbDeviceHandle != USB_DEVICE_HANDLE_INVALID)
//{
/* Set the Event Handler. We will start receving events after
* the handler is set */
// USB_DEVICE_EventHandlerSet(appData.usbDeviceHandle, APP_USBDeviceEventHandler, (uintptr_t)&appData);
/* Move the application to the next state */
//appData.state = APP_STATE_RUNNING;
//}
break;
}
case APP_INITIAL_DISK:
if(SYS_FS_CurrentDriveSet("/mnt/myDrive") == SYS_FS_RES_FAILURE)
{
/* Error while setting current drive */
appData.state = APP_INITIAL_DISK;
}
else
{
/* Open a file for reading. */
appData.state = APP_INITIAL_OPEN_FILE; // APP_OPEN_FIRST_FILE;
}
break;
case APP_INITIAL_OPEN_FILE:
appData.fileHandle = SYS_FS_FileOpen("SND.bin",(SYS_FS_FILE_OPEN_WRITE));
if(appData.fileHandle == SYS_FS_HANDLE_INVALID)
{
/* Could not open the file. Error out*/
appData.state = APP_INITIAL_OPEN_FILE;
}
else
{
/* Create a directory. */
appData.state = APP_WRITE_TO_FILE;
}
break;
case APP_WRITE_TO_FILE:
appData.data[0]='S';
appData.data[1]='N';
appData.data[2]='D';
appData.data[3]='R';
appData.data[4]='E';
appData.data[5]='C';
appData.data[6]=13;
appData.data[7]=10;
if(SYS_FS_FileWrite(appData.fileHandle, (const void *)appData.data, 16) == -1) // Write file header
{
/* Write was not successful. Close the file
* and error out.*/
appData.state = APP_INITIAL_OPEN_FILE;
} else {
appData.dataCnt=0;
appData.dataReadPtr=0;
appData.dataWritePtr=0;
appData.gcnt=0;
for(n=0;n<dataMaxBuff;n++)appData.data[n]=0; // delete buffer
// AD1CON3bits.GSWTRG = 1; // Start A/D conv
//DRV_ADC_Open();
//DRV_ADC_Start();
AD1CON1=ADC_CON1; //0x00E4; // Disable ADC1 and start making settings
AD1CON2=ADC_CON2; //0x41E;
AD1CON3=ADC_CON3; //0x400;
AD1CHS=0;
AD1CHSbits.CH0NA=0;
AD1CHSbits.CH0SA=0; // AD1 A = channel AN0
AD1CHSbits.CH0NB=0;
AD1CHSbits.CH0SB=0; // AD1 B = channel AN0
AD1CON1bits.ADON=0;
AD1CON1bits.ASAM=0;
AD1CON1bits.FORM=2;
AD1CON1bits.SIDL=0;
AD1CON1bits.SSRC=0; // MANUAL
AD1CON1bits.CLRASAM=0;
AD1CON1bits.SAMP=0;
AD1CON2bits.VCFG=0;
AD1CON2bits.CSCNA=1;
AD1CON2bits.ALTS=0;
AD1CON2bits.BUFM=0;
AD1CON2bits.OFFCAL=0;
AD1CON2bits.SMPI=0;
AD1CON3bits.SAMC=4;
AD1CON3bits.ADCS=0x4F;
AD1CON3bits.ADRC=0;
AD1CSSL=1;// select channel AN0
// AD1 ? ADC1 Convert done 28 23 IFS0<28> IEC0<28> IPC5<28:26> IPC5<25:24> Yes
// 0001 0000 0000 0000 0000 0000 0000 0000
IPC5bits.AD1IS = 3; //Set Priority & Sub priority to 7 <28:26><25:24> = 111 111 --> 0001 1111
IPC5bits.AD1IP = 7;
IFS0bits.AD1IF=0;// = 0x1000;// Ensure the interrupt flag is cleared
//IEC0bits.AD1IE=1;// Enable ADC interrupt
IEC0bits.AD1IE=1;// Disable ADC interrupt
appData.dataWritePtr=0;
AD1CON1bits.FORM=2;
AD1CON1SET=0x8000; // Enable ADC1
AD1CON1bits.SAMP = 1; // start sampling ...
// SYS_FS_FileClose(appData.fileHandle);
/*
PIC.MemWrite(SPI2BUF, sbRESET)
PIC.MemRead(SPI2BUF) ' Dummy read
PIC.MemWrite(SPI2BUF, sbRESET_rp)
PIC.MemRead(SPI2BUF) ' Dummy read
PIC.MemWrite(SPI2BUF, sbREAD_seq)
PIC.MemRead(SPI2BUF) ' Dummy read
For i = 1 To 15000
PIC.MemWrite(SPI2BUF, sbREAD_seq)
x = PIC.MemRead(SPI2BUF) ' Dummy read
a(i) = x
If (i Mod 100) = 0 Then
ListBox1.Items.Add("[" + CStr(i) + "] =" + Hex(x))
ListBox1.Refresh()
End If
Next
*/
SPI2BUF=sbRESET_rp;
x=spi_max_wait;
while((SPI2STAT&0x800)&&(x>0))x--; // Wait SPI BUSY bit to be cleared
x=SPI2BUF; // Dummy read
SPI2BUF=sbREAD_seq;
x=spi_max_wait;
while((SPI2STAT&0x800)&&(x>0))x--; // Wait SPI BUSY bit to be cleared
x=SPI2BUF; // Dummy read
appData.state = APP_STATE_READING;
}
SYS_FS_FileClose(appData.fileHandle);
break;
case APP_STATE_READING:
while(PORTBbits.RB3); // Wait until buffer is not empty
for(i=0;i<seclen;i++){
k=1;
/* x=spi_max_wait;
while((PORTBbits.RB3)&&(x>0))x--; // Wait until buffer is not empty
if(x<=0){
PORTBbits.RB7=1; // Error
}*/
//while((k%2)==1){
SPI2BUF=sbREAD_seq;
x=spi_max_wait;
while((SPI2STAT&0x800)&&(x>0))x--; // Wait SPI BUSY bit to be cleared
if(x<=0){
PORTBbits.RB7=1; // Error
}
k=SPI2BUF;
appData.data[i]=k; // Store a new sample
x=spi_max_wait;
while((SPI2STAT&0x800)&&(x>0))x--; // Wait SPI BUSY bit to be cleared
//}
}
SPI2ClearReceiveBuffer();
if((appData.FileOpenCloseCNT%FileOpenCloseInterval)==0){
appData.state = APP_STATE_OPEN_FILE;
} else {
appData.state = APP_STATE_WRITING;
}
break;
case APP_STATE_OPEN_FILE:
appData.fileHandle = SYS_FS_FileOpen("SND.bin",(SYS_FS_FILE_OPEN_APPEND));
if(appData.fileHandle != SYS_FS_HANDLE_INVALID){
appData.state = APP_STATE_WRITING;
PORTBbits.RB7=1;
}
break;
case APP_STATE_WRITING:
// ADC_Tasks();
if(SYS_FS_FileWrite(appData.fileHandle, (const void *)&appData.data[0], (seclen<<1))==-1) // 2 bytes per word --> sector length is in words
{
PORTBbits.RB7=1;
} else {
//appData.state = APP_STATE_CLOSE;
appData.FileOpenCloseCNT++;
if((appData.FileOpenCloseCNT%FileOpenCloseInterval)==0){
appData.state = APP_STATE_CLOSE;
} else {
appData.state = APP_STATE_READING;
}
}; // flush data to file
break;
case APP_STATE_CLOSE:
SYS_FS_FileClose(appData.fileHandle);
appData.state = APP_STATE_READING;
// appData.dataCnt-=seclen;
/* The MSD Device is maintained completely by the MSD function
* driver and does not require application intervention. So there
* is nothing related to MSD Device to do here. */
break;
/* The default state should never be executed. */
default:
break;
}
}