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

in Bus Pirate, how-to by DP | 2 comments

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 LEDADC demoUART 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 , .

Comments

  1. Michael says:

    Is there something wrong with my browser that prevents seeing the indentation in the program listings?

  2. ian says:

    I think it was a problem copying from the wiki

    The wiki version has the indents.preserved.

Leave a Comment

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Notify me of followup comments via e-mail. You can also subscribe without commenting.