Making Music with the FreeSoC2

Pages
Contributors: Nick Poole
Favorited Favorite 1

Firmware

Now that the hardware is all hooked up and the PSoC device is configured, it's time to look over the firmware. The code isn't very complex but we'll cut it into sections so that we can break it down by function, starting with the initializing code:

language:c
#include <project.h>
#include <stdbool.h>

int main()
{
/* initialization/startup */

A_B_Start();
C_D_Start();
E_F_Start();
G_A_Start();
OutMux_Start();
Pot_Vals_Start();
ADC_Start();
Tremolo_Start();
Amp_Start();

uint8 InputReg;
int16 Pot_A_val;
int16 Pot_B_val;

Amp_SetGain(Amp_GAIN_01);

The initialization portion of the code is fairly straight-forward. The first few lines are procedure calls to start each of the components from the schematic. Then we set up a few variables to keep track of our status register and potentiometer values. And finally, we need to initialize the programmable gain amplifier.

language:c
for(;;)
{
    Pot_Vals_Select(0);
    CyDelay(25);
    ADC_StartConvert();
    ADC_IsEndConversion(ADC_WAIT_FOR_RESULT);
    Pot_A_val = ADC_GetResult16();

    Pot_Vals_Select(1);
    CyDelay(25);
    ADC_StartConvert();
    ADC_IsEndConversion(ADC_WAIT_FOR_RESULT);
    Pot_B_val = ADC_GetResult16();


    InputReg = Status_Reg_Read();

Here we've started the main code loop by reading all of our inputs. For each of our potentiometers, we first select the correct channel on the ADC multiplexer. Then we need to start the conversion which is quick but not instantaneous so before we retrieve the results we need to wait for the conversion to end. The procedure call ADC_IsEndConversion(ADC_WAIT_FOR_RESULT) essentially busy-waits until the ADC conversion is complete and the results are ready to retrieve.

The digital inputs are easier to handle, all we need to do is call Status_Reg_Read() which returns an 8-bit number where each bit represents the status of one input.

language:c
switch(InputReg){

 case 1:
    Control_Reg_Write(0); //0000
    OutMux_Select(0);    
 break;

 case 2:
    Control_Reg_Write(1); //0001
    OutMux_Select(0);   
 break;

 case 4:
    Control_Reg_Write(1); //0001
    OutMux_Select(1);            
 break;

 case 8:
    Control_Reg_Write(2); //0010
    OutMux_Select(1);
 break;

 case 16:
Control_Reg_Write(2); //0010
OutMux_Select(2);            
 break;

 case 32:
Control_Reg_Write(4); //0100
OutMux_Select(2);            
 break;

 case 64:
Control_Reg_Write(4); //0100
OutMux_Select(3);                       
 break;

 case 128:
Control_Reg_Write(8); //1000
OutMux_Select(3);                 
 break;

 default:
    OutMux_DisconnectAll();
 break;

}

This switch/case statement selects the correct output channel and waveshape depending on the key that's being pressed. Two function calls are required to play a tone: first we need to write to the control register in order to select the proper waveshape on the proper WaveDAC and secondly, we need to select the proper output channel on the multiplexer. The default case is OutMux_DisconnectAll() which shuts off the sound when no key is being pressed.

language:c
Pot_A_val = Pot_A_val*20/1024;
Pot_B_val = Pot_B_val*3/1024;

if(Pot_A_val > 1){

    CyDelay(Pot_A_val*5);
    Tremolo_Select(1);
    CyDelay(Pot_A_val*5);
    Tremolo_Select(0);

}else{

    Tremolo_Select(0);

}

The above section determines the rate of the tremolo. Firstly, we re-map the potentiometer values that we retrieved earlier to a smaller and more useful range. Then we check to see if the potentiometer reading is above a certain value because if the tremolo rate is 0, we want to be sure that it isn't stopped on the dead channel. If the potentiometer value is above 1, we cycle the tremolo on and off once using delays in between determined by the potentiometer value.

language:c
switch(Pot_B_val){

 case 0: 
    Amp_SetGain(Amp_GAIN_01);
    break;

 case 1:
    Amp_SetGain(Amp_GAIN_02);
    break;

 case 2: 
    Amp_SetGain(Amp_GAIN_04);
    break;

 default:
    Amp_SetGain(Amp_GAIN_01);
    break;

}

Finally, we switch the gain on our programmable gain amplifier based on the position of the gain potentiometer. Gain on the PGA is configurable in predefined intervals.

That's it, Then the whole loop starts over!