HOW TO: Bus Pirate as dev-board, read a voltage and view in terminal

The Bus Pirate uses the PIC24FJ64GA002 microcontroller, and can be used as a development board. In this tutorial we’ll read an analog voltage with an analog to digital converter, and then send it to the PC terminal via the UART. This is a culmination of the previous 3 tutorials ( blink the MODE LED, ADC demo, UART demo), and much of the code is better explained in them.
The code is broken up and explained section by section. If you want you can download the full MPLAB 8 project, or copy/paste the full code for main.c from the bottom of the page.
Using #define statements
In this tutorial we use ”#define” statements to make the code easier to read. At the top of the source we setup some macros for commonly used parts like pin names. #define is your friend. instead of typing cryptic register names like TRISAbits.TRISA0 all the time, we can ”define” a simple human readable name like MY_PIN.
Syntax
#define TEXT1 TEXT2
TEXT1 is the easy to remember macro name. It could be MY_PIN, LED1, whatever. Traditionally macros are typed in all caps, but it is not a requirement.
TEXT2 is what the macro does. Can replace a register or pin name like TRISAbits.TRISA0, or even a full function.
Usage
#define VREG_DIR TRISAbits.TRISA0
We tell the compiler to replace any instance of VREG33_DIR with TRISAbits.TRISA0. Now we can write VREG33_DIR instead of TRISAbits.TRISA0 throughout the code. As a bonus it’s easier to understand what the code is doing, it’s easy(er) to tell that VREG_DIR controls the direction of the pin associated with the voltage regulator.
Using Functions
Unlike the previous bare-bones tutorials, this time we’ll be using functions. This is a bit of code outside the main loop that does repetitive things and generally makes your source look cleaner.
If you’re not familiar with functions there a ton of resources better than the tiny crash course below, but it might serve as a refresher if you need it. If you’re already ”function”al, just skip this part.
Basic Syntax
[type of return_value] name_of_function([type of argument1] argument1,...)
{
//code to be executed in the function
return return_value;
}
Usage
void UART1TX(char c);
First we need to tell the world we have a function, and how to feed and water it. Our function will send a byte out the UART to a computer, and be called UART1TX. Sending data is a common thing to do, so it’s helpful to have a single function and keep all that code in one place.
void simply means the function doesn’t hand back anything when it runs. if it does return a value it could be a ”char”, ”int”, etc.
We need to give the function something to send out the UART to the computer. We call this variable ”c”, it’s a single byte or ”char”.
void UART1TX(char c){
while(U1STAbits.UTXBF == 1); //if buffer is full, wait
U1TXREG = c;
}
Now the actual function. It starts by repeating the same declaration we put at the top of the source, but this time it’s followed by code.
This function waits for the UART1TX buffer to be empty. Once it is, the value ”c” is put in the buffer and transmitted to the computer via UART1.
UART1TX('a');
Instead of writing the UART code every time we send a byte to the computer, now we can just call on our function to do it for us. This line sends the letter ‘a’ to the function, which transmits it to the computer.
This might not seem like a huge savings, but now all the register specific stuff is in one place and can be quickly changed for a different chip. Functions can be combined with defines for some really easy to modify code.
Basic Configuration
#include _CONFIG2(FNOSC_FRCPLL & OSCIOFNC_ON &POSCMOD_NONE & I2C1SEL_PRI) // Internal FRC OSC = 8MHz _CONFIG1(JTAGEN_OFF & GCP_OFF & GWRP_OFF & COE_OFF & FWDTEN_OFF & ICS_PGx1) //turn off junk we don't need #define VREG_DIR TRISAbits.TRISA0 //sets VREG33_DIR to TRISBIT A0 #define VREG_EN LATAbits.LATA0 //sets VREG_EN to LAT bit A0 (output latch) #define MODE_LED_DIR TRISAbits.TRISA1 //sets the MODE_LED_DIR to TRISBIT A1 #define MODE_LED LATAbits.LATA1 //sets the MODE_LED to LAT bit A! (output latch) #pragma code
As in previous tutorials we start by setting the configurations that control how the chip behaves when it first starts. The first line sets up the internal oscillator to produce a 32 MHz clock. Second line turns off options we wont use in this tutorial.
Several #define statements setup easy to remember names for pins that control various Bus Pirate features. VREG_DIR, VREG_EN (voltage regulator control), MODE_LED_DIR, and MODE_LED (MODE LED control) are much easier to work with.
Function Declarations
unsigned int getADC(); unsigned char UART1RX(); void UART1TX(char c); void InitializeUART1(void); void initADC();
Here we declare all the functions that we’ll use later in the code.
- ‘getADC’ simply takes one voltage measurement from the ADC pin and returns it as an unsigned integer (16bits)
- ‘UART1RX’ reads one character from the UART buffer and returns it
- ‘UART1TX’ sends the ”c” character to the UART buffer
- ‘InitializeUART’ configures the UART module
- ‘initADC’ function configures the ADC peripheral
Start up initialization
CLKDIVbits.RCDIV0=0; //clock divider to 0 AD1PCFG = 0xFFFF; // Default all pins to digital OSCCONbits.SOSCEN=0; //Oscilator setup, registrar-OSCON bits-SOSCEN MODE_LED_DIR = 0; //Mode LED set as output VREG_DIR =0; //VREG is set as output VREG_EN = 1; //Sets the VREG pin to 1, turns on the Voltage regulators. MODE_LED = 1; //Turns the MOD LED 0N
After startup the PIC isn’t full speed yet. The clock divider is configured to 0 which will output the 32 MHz frequency needed for 16 MIPS operation.
All the pins are then set to digital more, and the MODE LED and VREG pins are set as output. Following that, the MODE LED and voltage regulators are turned on.
InitializeUART1(); //Initialize UART1 initADC(); //Initialize ADC
The two hardware modules we’ll use, the ADC for measuring voltage and the UART for transmitting the reading to the computer, are setup here. These functions run several lines of code (shown below) that configure both modules.
The continuous loop
/////FOREVER///LOOP//////////
while(1)
{
if(UART1RX() == 'a') //Checks if the received byte is 'a' if so it sends the two RAW bytes form ADC
{
voltage = getADC(); //reads the ADC pin
UART1TX(voltage>>8); //seds the top byte of ADC to UART
UART1TX(voltage); //sends the bottom byte of ADC to UART
}
}
This loop returns an ADC measurement each time ‘a’ is typed into the computer serial terminal.
The first line calls the ”UART1RX” function and checks if the received character is ”a”. If it is, the ”getADC” function is called and the ADC value is stored in the ”voltage” variable.
Since ”getADC” returns a 10bit value, we first send the highest 2 bits to the computer ”UART1TX(voltage>>8)”. >>8 pushes all the bits down, removing the lower 8bits and and leaving us with the highest 8bits of the 16bit integer measurement. Finally we send the lower 8 bits with ”UART1TX(voltage)”.
Try it

Open a serial terminal that can display HEX values and connect to the Bus Pirate’s port. We use the serial port tab of Hercules (right click and set display type to HEX).
Connect the Bus Pirate ADC probe pin to a voltage, for example the 3.3volt or 5volt power supplies.
Type ”a” in the terminal window. When ”a” is received, the PIC reads the ADC pin and forwards this value to the terminal. Two byte values will show up in the terminal. The first is the most significant byte with the highest-value part of the reading, if you connect to a low voltage this may be 0.
Converting the received values to voltage
The Bus pirate has 10bit ADC peripheral referenced to 3.3V, and a divide by 2 voltage divider on the ADC pin. So the two bytes (”HIGH” first, ”LOW” second) contain a 10bit value compared to 6.6v. The equation to get the voltage from the two bytes is.
Vadc = (HIGH*256 + LOW)*6.6/1023
Where ‘HIGH’ is the first received byte, and ‘LOW’ is the second.
Function Definitions
But wait! There’s still more code!
These are the functions, built from the code in previous tutorials, that make everything happen. They are quickly outlined here, but be sure to see the original peripheral tutorial for a line-by-line explanation.
initADC
The initialization of the ADC peripheral was covered more in a previous tutorial.
void initADC()
{
//////////////////////////////////////////////////////////////
///////////////ADC config//////////////////////////////////
AD1PCFGbits.PCFG12=0;//B12/AN12/ADC1/EXT
//configure the ADC
AD1CON1bits.SSRC = 0b111;// SSRC = 111 implies internal
// counter ends sampling and starts
// converting.
AD1CSSL = 0;
AD1CON3 = 0x1F02; // Sample time = 31Tad,
// Tad = 2 Tcy
AD1CON2 = 0; //CSCNA =0 -does not scan inputs,
//BUFS =0 -A/D is currently filling buffer 00-07,
//SMPI=0 -Interrupts at the completion of conversion for each sample/convert sequence,
//BUFM =0 -Buffer configured as one 16-word buffer,
//ALTS = 0 -Always uses MUX A input multiplexer settings
AD1CHS =12; //chanell selected 12, this is the AN12 pin which is conected to the voltage devider and the ADC pin on the BPv3
/////////////////////////////////////////////////////////////////
}
Setup the ADC to read from analog channel 12, which is connected to the Bus Pirate probe pin.
getADC
Reading the ADC peripheral was described in depth in this previous tutorial.
unsigned int getADC()
{
AD1CON1bits.ADON =1; //turns the ADC module on
AD1CON1bits.SAMP=1; //start sample
AD1CON1bits.DONE=0; //starts conversion
while(AD1CON1bits.DONE==0);//wait for conversion to finish
AD1CON1bits.ADON =0; //turns the ADC module off
return ADC1BUF0;
}
Starts the ADC peripheral, waits while one value is read from the ADC pin, and returns that value.
UART1RX
Reading a character over the UART peripheral was covered in a previous tutorial.
//get a byte from UART
unsigned char UART1RX(void){
while(U1STAbits.URXDA == 0);
return U1RXREG;
}
Reads one value from the UART1RX buffer, waits if the buffer is empty.
UART1TX
Sending a character over the UART peripheral was covered in a previous tutorial.
//add byte to buffer, pause if full
void UART1TX(char c){
while(U1STAbits.UTXBF == 1); //if buffer is full, wait
U1TXREG = c;
}
Sends one character to the UART1TX buffer, waits if full.
InitializeUART1
The initialization of the UART1 peripheral was covered more in a previous tutorial.
//Initialize the terminal UART
void InitializeUART1(void){
TRISBbits.TRISB4=1; //UART 1 RX set as input
//set pin configuration using peripheral pin select
RPINR18bits.U1RXR = 5; // UART1 RX assign to RP5
RPOR2bits.RP4R = 3; //UART1 TX assign to RP4
U1BRG = 34;//13332=300, 1666=2400, 416=9600, 34@32mhz=115200....
U1MODE = 0;
U1MODEbits.BRGH = 1;
U1STA = 0;
U1MODEbits.UARTEN = 1;
U1STAbits.UTXEN = 1;
IFS0bits.U1RXIF = 0;
}
Initializes the UART1 peripheral to the pins connected to the FTDI UART-to-USB IC, and sets it up for 115200 baud communication speed, with 8 bits, and 0 parity, and 1 stop bit. It’s necessary to set this in your terminal software as well.
C30 source code
#include
_CONFIG2(FNOSC_FRCPLL & OSCIOFNC_ON &POSCMOD_NONE & I2C1SEL_PRI) // Internal FRC OSC = 8MHz
_CONFIG1(JTAGEN_OFF & GCP_OFF & GWRP_OFF & COE_OFF & FWDTEN_OFF & ICS_PGx1) //turn off junk we don't need
#define VREG_DIR TRISAbits.TRISA0 //sets VREG33_DIR to TRISBIT A0
#define VREG_EN LATAbits.LATA0 //sets VREG_EN to LAT bit A0 (output latch)
#define MODE_LED_DIR TRISAbits.TRISA1 //sets the MODE_LED_DIR to TRISBIT A1
#define MODE_LED LATAbits.LATA1 //sets the MODE_LED to LAT bit A1 (output latch)
#pragma code
void initADC();
unsigned int getADC();
unsigned char UART1RX(void);
void UART1TX(char c);
void InitializeUART1(void);
//this loop services user input and passes it to be processed on
int main(void){
CLKDIVbits.RCDIV0=0; //clock divider to 0
AD1PCFG = 0xFFFF; // Default all pins to digital
OSCCONbits.SOSCEN=0; //Oscilator setup, registar-OSCON bits-SOSCEN
MODE_LED_DIR = 0; //Mode LED set as output
VREG_DIR =0; //VREG is set as output
VREG_EN = 1; //Sets the VREG pin to 1, turns on the Voltage regulators.
MODE_LED = 1; //Turns the MOD LED 0N
InitializeUART1(); //Initialize UART1
initADC(); //Initialise ADC
unsigned int voltage;
/////FOREVER///LOOP//////////
while(1)
{
if(UART1RX() == 'a') //Checks if the recived byte is 'a' if so it sends the two RAW bytes form ADC
{
voltage = getADC(); //reads the ADC pin
UART1TX(voltage>>8); //seds the top byte of ADC to UART
UART1TX(voltage); //sends the bottom byte of ADC to UART
}
}
}
////////////////////////////////////////////////////////////////////////////////////
/////Analog to digital converter intitilisationn function///////////////////////////
/////Setch the ADC to read the AN12 chanel, whic is connected ot the BPv3 pin ADC///
////////////////////////////////////////////////////////////////////////////////////
void initADC()
{
//////////////////////////////////////////////////////////////
///////////////ADC config//////////////////////////////////
AD1PCFGbits.PCFG12=0;//B12/AN12/ADC1/EXT
//configure the ADC
AD1CON1bits.SSRC = 0b111;// SSRC = 111 implies internal
// counter ends sampling and starts
// converting.
AD1CSSL = 0;
AD1CON3 = 0x1F02; // Sample time = 31Tad,
// Tad = 2 Tcy
AD1CON2 = 0; //CSCNA =0 -does not scan inputs,
//BUFS =0 -A/D is currently filling buffer 00-07,
//SMPI=0 -Interrupts at the completion of conversion for each sample/convert sequence,
//BUFM =0 -Buffer configured as one 16-word buffer,
//ALTS = 0 -Always uses MUX A input multiplexer settings
AD1CHS =12; //chanell selected 12, this is the AN12 pin which is conected to the voltage devider and the ADC pin on the BPv3
/////////////////////////////////////////////////////////////////
}
unsigned int getADC()
{
AD1CON1bits.ADON =1; //turns the ADC module on
AD1CON1bits.SAMP=1; //start sample
AD1CON1bits.DONE=0; //starts conversion
while(AD1CON1bits.DONE==0);//wait for conversion to finish
AD1CON1bits.ADON =0; //turns the ADC module off
return ADC1BUF0;
}
//get a byte from UART
unsigned char UART1RX(void){
while(U1STAbits.URXDA == 0);
return U1RXREG;
}
//add byte to buffer, pause if full
//uses PIC 4 byte UART FIFO buffer
void UART1TX(char c){
while(U1STAbits.UTXBF == 1); //if buffer is full, wait
U1TXREG = c;
}
//Initialize the terminal UART for the speed currently set in bpConfig.termSpeed
void InitializeUART1(void){
TRISBbits.TRISB4=1; //UART 1 RX set as input
//set pin configuration using peripheral pin select
RPINR18bits.U1RXR = 5; // UART1 RX assign to RP5
RPOR2bits.RP4R = 3; //UART1 TX assign to RP4
U1BRG = 34;//13332=300, 1666=2400, 416=9600, 34@32mhz=115200....
U1MODE = 0;
U1MODEbits.BRGH = 1;
U1STA = 0;
U1MODEbits.UARTEN = 1;
U1STAbits.UTXEN = 1;
IFS0bits.U1RXIF = 0;
}
This entry was posted in Bus Pirate, how-to and tagged Bus Pirate v3, tutorial.


Comments
Is there something wrong with my browser that prevents seeing the indentation in the program listings?
I think it was a problem copying from the wiki
The wiki version has the indents.preserved.