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;

}

}