I'm struggling with the final steps of sending the results from the 10 bit ADC in a PIC16F819 to RS232. This is my code so far
/* File: Main.c
* Author: David J S Briscoe
* PIC: 16F819 with INTERNAL RC OSC @ 8MHz, 5v
* Program: 04_ADC_RS232
* Compiler: XC8 (v2.46, MPLABX X v6.20)
* Program Version: 1.0
*
* Program Description: This Program sends the DC voltage applied to AN0 through a 10K potentiometer to RS232 via pin RB2. It uses 1 channel
* of the 10 bit A/D convertor. RB4 can be used to Receive data FROM the PC if needed. Change these pins as needed.
* It also shows how to set up the internal pull up resistors.
*
* Hardware Description: Pins are connected to the RS232 PCB or a TTL to USB dongle.
* A 10K potentiometer is connected to PIN A0 and ground.
* Timer 2 is used as the timebase for accurate bit timing.
* There is NO hardware USART on the PIC16F819 so this is why the timer is used.
*
*
*
* Notes:
* Set XC8 C compiler to C90 mode (IN C99 MODE BY DEFAULT) in Project properties -> XC8 global options -> C standard -> APPLY.
* This is so that itoa function can be used.
*
* Synopsis
*#include <stdlib.h>
*char * itoa (char * buf, int val, int base)
*Description
*The function itoa converts the contents of val into a string which is stored into buf.
*The conversion is performed according to the radix specified in base. buf is assumed
*to reference a buffer which has sufficient space allocated to it.
*Example
*#include <stdlib.h>
*#include <stdio.h>
*void
*main (void)
*{
* char buf[10];
* itoa(buf, 1234, 16);
* printf("The buffer holds %s\n", buf);
*}
*See Also
*strtol(), utoa(), ltoa(), ultoa()
*Return Value
*This routine returns a copy of the buffer into which the result is written.
*
* Step size for 5V microcontroller
* step size = 5V / (1024-1)
* step size = 4.8 mV
* Resolution = 4.8875 mV
*
* Step size for 3.3V microcontroller
* step size = 3.3V / (1024-1)
* step size = 3.2 mV
* Resolution = 3.2258 mV
* 10 Bit resolution
* ADCON0 & ADCON1 are the control registers
* ADRESL & ADRESH are the data registers.
*
* Try ((((ADC*2)+1) *500) +1024) /2048
* See discussion here [url]https://forum.allaboutcircuits.com/threads/why-adc-1024-is-correct-and-adc-1023-is-just-plain-wrong.80018/[/url]
*
*
*
* Created February 24th, 2023
* Last Updated: April 25th, 2024
*/
/*******************************************************************************
Change History:
Revision Description
14th March 2024 Initial code.
25th April 2024 Corrections to ADC code and merging of Serial code.
RS232 RX is on RB4
RS232 TX is on RB2
*******************************************************************************/
// Configuration bits if NOT using header file 16F819_Internal.h
//#pragma config FOSC = INTOSCIO
//#pragma config WDTE = OFF
//#pragma config PWRTE = OFF
//#pragma config MCLRE = ON
//#pragma config BOREN = OFF
//#pragma config LVP = ON
//#pragma config CPD = OFF
//#pragma config WRT = OFF
//#pragma config CCPMX = RB2
//#pragma config CP = OFF
// *******************************
/*******************************************************************************
*Includes and defines
******************************************************************************/
#include <stdio.h> // Needed for sprintf(see manual page 261 library functions)
#include "16F819_Internal.h" // Include header which sets config fuses and oscillator frequency (set @ 8MHz) if using a XTAL.
#define _XTAL_FREQ 8000000UL // Needed by compiler.
#define Baudrate 9600 // Adjust as necessary.
#if (Baudrate!=9600) && (Baudrate!=19200) && (Baudrate!=38400)
#error "Please set a valid baudrate: 9600/19200/38400"
#endif
#define OneBitDelay ((_XTAL_FREQ/(4*(unsigned long)Baudrate))-1)
#define UART_RX PORTBbits.RB4
#define UART_RX_DIR TRISBbits.TRISB4
#define UART_TX PORTBbits.RB2
#define UART_TX_DIR TRISBbits.TRISB2
// Bit macros to avoid slow looping (We need it to be as fast as possible)
#define BIT_DELAY() TMR2IF=0; while(!TMR2IF);
#define TX_BIT(v,n) if(v & n){UART_TX=1;} else{UART_TX=0;} BIT_DELAY()
#define RX_BIT(v,n) if(UART_RX){v|=n;} BIT_DELAY()
void SUART_Init(void)
{
UART_TX = 1; // TX pin is high in idle state
UART_RX_DIR = 1; // Input
UART_TX_DIR = 0; // Output
PR2 = OneBitDelay; // Load period counter
T2CON = 0; // Use timer 2 as timebase
T2CONbits.TMR2ON = 1; // Start timer
}
unsigned char SUART_Receive(void)
{
unsigned char DataValue = 0;
while(UART_RX==1); // wait for start bit
TMR2=OneBitDelay*2/3; // Preload the timer with at 2/3 of a bitDelay (1/2 might sample too late with this slow mcu)
BIT_DELAY(); // Wait for it to expire
BIT_DELAY(); // Counter reset, wait for a full bit time now
RX_BIT(DataValue,0x01); // Unrolled loop for fastest processing
RX_BIT(DataValue,0x02);
RX_BIT(DataValue,0x04);
RX_BIT(DataValue,0x08);
RX_BIT(DataValue,0x10);
RX_BIT(DataValue,0x20);
RX_BIT(DataValue,0x40);
RX_BIT(DataValue,0x80);
return DataValue;
}
void SUART_Transmit(char DataValue)
{
UART_TX = 0; // Start
TMR2=0;
BIT_DELAY();
TX_BIT(DataValue,0x01); // Unrolled loop for fastest processing
TX_BIT(DataValue,0x02);
TX_BIT(DataValue,0x04);
TX_BIT(DataValue,0x08);
TX_BIT(DataValue,0x10);
TX_BIT(DataValue,0x20);
TX_BIT(DataValue,0x40);
TX_BIT(DataValue,0x80);
UART_TX = 1; //Stop
BIT_DELAY();
}
void SUART_Write_Text(char *text)
{
while(*text)
SUART_Transmit(*text++);
}
void SUART_Read_Text(char *Output, unsigned int length)
{
while(length--)
*Output++ = SUART_Receive(); // Receive char
}
/*******************************************************************************
* Function: void initMain()
*
* Returns: Nothing
*
* Description: Contains initializations for main
*
* Usage: initMain()
******************************************************************************/
void initMain()
{
OSCCONbits.IRCF = 0b0111; //Set the Clock to 8MHz when using internal oscillator.
/*
111 = 8 MHz (8 MHz source drives clock directly)
110 = 4 MHz
101 = 2 MHz
100 = 1 MHz
011 = 500 kHz
010 = 250 kHz
001 = 125 kHz
000 = 31.25 kHz (INTRC source drives clock directly)
Page 38 of the PIC16F819 data sheet.
*/
ADCON1bits.PCFG = 0b0110; //See page 82 of data sheet..
//#define SW1_PORTB4 PORTBbits.RB4 //Wherever the compiler sees SW1_PORTB4 it substitutes PORTBbits.RB4
//#define LED_PORTB2 PORTBbits.RB2 //Wherever the compiler sees LED_PORTB2 it substitutes PORTBbits.RB2
TRISBbits.TRISB2 = 0; //Ensure RB2 is an output pin (LED).
TRISBbits.TRISB4 = 1; //Ensure RB4 is an input pin (Switch).
PORTA = 0x00; //Clear Port A.
PORTB = 0x00; //Clear Port B.
nRBPU = 0; //Enable ALL PORTB internal pull up resistor.
//If you comment this line out you need to connect a
//10K resistor between +5V and your INPUT pin.
// 16F819 can NOT ENABLE INDIVIDUAL PULL UPS ON PORT B.
// Configure the ADC
ADCON0bits.ADCS = 0b11; // Use the ADCs internal RC oscillator
ADCON0bits.CHS = 0b000; // Use ANO as the analogue source
ADCON0bits.ADON = 1; // Turn on the module
ADCON1bits.PCFG = 0b1110; // Use AN0 as input, VDD and VSS as ref
ADCON1bits.ADFM = 1; // Right justified for 10 bit result
ADCON1bits.ADCS2 = 0; // Normal clock division (don't worry)
}
//********************************
//Main Code
void main(void)
{
//char i;
//char j;
//char k;
unsigned int result;// Change data type as needed.
//unsigned int voltage ;
initMain();
SUART_Init();
__delay_ms(1000);
// Our main loop
while(1)
{
// Make an ADC reading
ADCON0bits.GO = 1;
// Now wait until the ADC has finished
while(ADCON0bits.GO == 1); // The program stays in this while loop until ADCON0bits.GO == 0 and then control jumps to the next line.
// Now transfer the two 8 bit readings into a single 10 bit reading
result = 0x0000; // Clear the variable.
result = ADRESH; // Get the first 8 bits
result = result << 8; // Shift left 8 times
result = result | ADRESL;// Now stick the lower 8 bits in
// Now do any calculations to scale the output of the ADC to represent the voltage actually on the slider of the potentiometer.
// For ASCII output add 0x30 to the value.
// See [url]https://www.youtube.com/watch?v=rKaJGUkjyB4&list=PL_zvrXFdKgZpTrM99mypGVW5JBZ6tQZiR&index=33[/url] for more ideas.
// Now test the serial link and report the baud rate setting to show signs of life!
/* Simple string tests */
SUART_Write_Text("Hello!\r\n");
SUART_Write_Text("Software uart, ");
#if (Baudrate==9600)
SUART_Write_Text("9600");
#elif (Baudrate==19200)
SUART_Write_Text("19200");
#elif(Baudrate==38400)
SUART_Write_Text("38400");
#else
SUART_Write_Text("unknown");
#endif
SUART_Write_Text(" bauds.\r\n\n");
/* TX/RX loop and serial tests. Comment out when not needed.*/
/* Not needed if you are only SENDING data FROM the PIC. Use this to test the link if you want to SEND and RECEIVE data.
SUART_Write_Text("Please type in the console now\r\n,you should see the input below.\r\n\n");
while(1)
{
SUART_Transmit(SUART_Receive());
}
*/
for ( char j = 0; j <= 2; j++) // Send 2 screens of charachters as a test of serial link.
{
for ( char k = 'A'; k <= 'Z'; k++)
{
SUART_Transmit(k);
__delay_ms(250);
}
for ( char k = 'a'; k <= 'z'; k++)
{
SUART_Transmit(k);
__delay_ms(250);
}
for ( char k = '0'; k <= '9'; k++)
{
SUART_Transmit(k);
__delay_ms(250);
}
SUART_Write_Text("\r\n");
}
// Send the data from the Analogue to digital convertor channel 0;
// voltage = (result * 5.0)/1023;
// Explore sprintf and buffers for integer to ASCII conversion.
SUART_Write_Text("\r\n");
SUART_Write_Text("\r\nADC result is: ");
SUART_Write_Text(result);
SUART_Write_Text(" mV.\r\n\n");
}
}
/*
* File: 16F819_Internal.h
* Author: David J S Briscoe
* PIC: 16F819 with INTERNAL OSC @ 8MHz, 5v
* Program: Header file to setup PIC16F819
* Compiler: XC8 (v2.40, MPLAX X v6.00)
* Program Version 1.0
*
* *Changed comments and layout
*
* Program Description: This program header will allows setup of configuration
* bits and provides routines for setting up internal
* oscillator and includes all devices and MCU modules
*
* Created on February 24th, 2023
******************************************************************************/
/*******************************************************************************
*Includes and defines
******************************************************************************/
// PIC16F819 Configuration Bit Settings
/*
Fuses: NOWDT,WDT,PUT,NOPUT,LP,XT,HS,EC_IO,INTRC_IO,INTRC,RC_IO,RC
//////// Fuses: NOMCLR,MCLR,NOBROWNOUT,BROWNOUT,NOLVP,LVP,CPD,NOCPD,WRT_600
//////// Fuses: WRT_400,WRT_200,NOWRT,DEBUG,NODEBUG,CCPB3,CCPB2,PROTECT
//////// Fuses: NOPROTECT
*/
// PIC16F819 Configuration Bit Settings
// 'C' source line config statements
// CONFIG
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = ON // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is MCLR)
#pragma config BOREN = ON // Brown-out Reset Enable bit (BOR enabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB3/PGM pin has digital I/O function, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Code protection off)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off)
#pragma config CCPMX = RB2 // CCP1 Pin Selection bit (CCP1 function on RB2)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
//XC8 Standard Include
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
//Other Includes
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <math.h>
//For delay routines WHEN USING A CRYSTAL OSCILLATOR ONLY
//#define _XTAL_FREQ 8000000
//MCU Modules Includes
Its the conversion of the result from the ADC into a form that can be seen on a serial terminal which I'm struggling with. Is there any simple code that I can use to do this that will work with the example above? Thanks.