[SOLVED]ADC result to RS232 using XC8 on an PIC16F819-How? - Page 1
Forum section: electronics / general

author: djsb
author_rank: Super Contributor
timestamp: May 31, 2024, 07:24:00 am
content: 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 farCode:/* 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;* 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 https://forum.allaboutcircuits.com/threads/why-adc-1024-is-correct-and-adc-1023-is-just-plain-wrong.80018/**** Created February 24th, 2023* Last Updated: April 25th, 2024*//*******************************************************************************Change History:Revision Description14th March 2024 Initial code.25th April 2024 Corrections to ADC code and merging of Serial code.RS232 RX is on RB4RS232 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 stateUART_RX_DIR = 1; // InputUART_TX_DIR = 0; // OutputPR2 = OneBitDelay; // Load period counterT2CON = 0; // Use timer 2 as timebaseT2CONbits.TMR2ON = 1; // Start timer}unsigned char SUART_Receive(void){unsigned char DataValue = 0;while(UART_RX==1); // wait for start bitTMR2=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 expireBIT_DELAY(); // Counter reset, wait for a full bit time nowRX_BIT(DataValue,0x01); // Unrolled loop for fastest processingRX_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; // StartTMR2=0;BIT_DELAY();TX_BIT(DataValue,0x01); // Unrolled loop for fastest processingTX_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; //StopBIT_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 MHz101 = 2 MHz100 = 1 MHz011 = 500 kHz010 = 250 kHz001 = 125 kHz000 = 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.RB2TRISBbits.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 ADCADCON0bits.ADCS = 0b11; // Use the ADCs internal RC oscillatorADCON0bits.CHS = 0b000; // Use ANO as the analogue sourceADCON0bits.ADON = 1; // Turn on the moduleADCON1bits.PCFG = 0b1110; // Use AN0 as input, VDD and VSS as refADCON1bits.ADFM = 1; // Right justified for 10 bit resultADCON1bits.ADCS2 = 0; // Normal clock division (don't worry)}//********************************//Main Codevoid 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 loopwhile(1){// Make an ADC readingADCON0bits.GO = 1;// Now wait until the ADC has finishedwhile(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 readingresult = 0x0000; // Clear the variable.result = ADRESH; // Get the first 8 bitsresult = result << 8; // Shift left 8 timesresult = 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 https://www.youtube.com/watch?v=rKaJGUkjyB4&list=PL_zvrXFdKgZpTrM99mypGVW5JBZ6tQZiR&index=33 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");#elseSUART_Write_Text("unknown");#endifSUART_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");}}Code:/** 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 IncludesIts 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.

author: Ian.M
author_rank: Super Contributor
timestamp: May 31, 2024, 08:18:51 am
content: What's your conceptual problem?You know the ADC result is a 16 bit unsigned integer with the lower 10 bits representing the range 0V to Vref, you know the math needed to convert that to a voltage, you've decided to work in mV so you can use (long) integer math, and you know itoa() can convert an integer into a string (as usual: ASCII, null terminated) which you can pass to your SUART_Write_Text() function.Why haven't you simply tried:Code:int voltage; // voltage in mVchar buff; // space for (sign) + max. 5 digits + null// Calculate result in mV// See: https://forum.allaboutcircuits.com/threads/why-adc-1024-is-correct-and-adc-1023-is-just-plain-wrong.80018/voltage=(int)(((result*2 +1) *5000L +1024L) /2048L);// Explicit cast to int avoids integer truncation warning as we know it must fit in an int.itoa(buff,voltage,10); // convert to base 10 ASCII stringSUART_Write_Text("\r\nADC result is: ");SUART_Write_Text(buff); // itoa() converted voltageSUART_Write_Text(" mV.\r\n\n");If you need to output in units of Volts its a little more complex. If voltage>999, you need to output the first digit from buff, then output '.' then output the rest of buff, i.e. adjust the start pointer to buff +1 and pass it to SUART_Write_Text(). If its 100 to 999 you write "0." then buff, if 10 to 99 you write "0.0" then buff and finally, if 0 to 9, write "0.00" then buff.Hint: Rather than wrangling with XC8's C90 mode, why not simply implement your own itoa(), e.g. use the K&R base 10 only version:https://en.wikibooks.org/wiki/C_Programming/stdlib.h/itoaWith a custom itoa function, if you want to output Volts you can also inject the decimal point while building the string, which is considerably simpler than injecting it when outputting the buffer. To get correct leading zeros, prefill the buffer with "000.0\0" (which will be reversed later) and in the itoa i loop, 'bump' i +1 after the third digit so it doesn't overwrite the '.'. Only add the terminating '\0' if i>5 (as then you've overwritten the '\0' in the string you prefilled the buffer with.Outputting fewer decimal places can be handled by adding an appropriate rounding adjustment to the integer before passing it to itoa, and truncating the converted string by stuffing '\0' where you want to end it, or modding the itoa function to skip incrementing i till done with the digits you want to discard, and modifying the 'prestuff' string and the i bump to skip '.' accordingly.

author: djsb
author_rank: Super Contributor
timestamp: May 31, 2024, 09:48:29 am
content: Thanks for your reply. Here are the compilation results when compiling with the code snippet you kindly supplied.Code:make -f nbproject/Makefile-default.mk SUBPROJECTS= .build-confmake -f nbproject/Makefile-default.mk dist/default/production/04_ADC_RS232.X.production.hexmake: Entering directory 'F:/XC8_WORK/XC8_16F819/04_ADC_RS232.X'"C:\Program Files\Microchip\xc8\v2.46\bin\xc8-cc.exe" -mcpu=16F819 -c -mdfp="C:/Program Files/Microchip/MPLABX/v6.20/packs/Microchip/PIC16Fxxx_DFP/1.6.156/xc8" -fno-short-double -fno-short-float -O0 -fasmfile -maddrqual=ignore -xassembler-with-cpp -mwarn=-3 -Wa,-a -DXPRJ_default=default -msummary=-psect,-class,+mem,-hex,-file -ginhx32 -Wl,--data-init -mno-keep-startup -mno-osccal -mno-resetbits -mno-save-resetbits -mno-download -mno-stackcall -mno-default-config-bits -std=c90 -gdwarf-3 -mstack=compiled:auto:auto -o build/default/production/main.p1 main.cadvisory: (2099) legacy C90 library is deprecated and will be discontinued in a future release; consult the release notes for more detailsadvisory: (2095) legacy HI-TECH support is deprecated and will be discontinued in a future release; consult the release notes for more detailsmain.c:257:30: warning: (359) illegal conversion between pointer typespointer to const unsigned char -> pointer to unsigned charmain.c:258:35: warning: (359) illegal conversion between pointer typespointer to const unsigned char -> pointer to unsigned charmain.c:260:24: warning: (359) illegal conversion between pointer typespointer to const unsigned char -> pointer to unsigned charmain.c:268:33: warning: (359) illegal conversion between pointer typespointer to const unsigned char -> pointer to unsigned charmain.c:297:24: warning: (359) illegal conversion between pointer typespointer to const unsigned char -> pointer to unsigned charmain.c:308:22: warning: (1385) function "itoa" is deprecated (declared at main.c:308)main.c:310:39: warning: (359) illegal conversion between pointer typespointer to const unsigned char -> pointer to unsigned charmain.c:311:25: warning: (357) illegal conversion of integer to pointermain.c:312:30: warning: (359) illegal conversion between pointer typespointer to const unsigned char -> pointer to unsigned charmake: Leaving directory 'F:/XC8_WORK/XC8_16F819/04_ADC_RS232.X'make: Entering directory 'F:/XC8_WORK/XC8_16F819/04_ADC_RS232.X'"C:\Program Files\Microchip\xc8\v2.46\bin\xc8-cc.exe" -mcpu=16F819 -Wl,-Map=dist/default/production/04_ADC_RS232.X.production.map -DXPRJ_default=default -Wl,--defsym=__MPLAB_BUILD=1 -mdfp="C:/Program Files/Microchip/MPLABX/v6.20/packs/Microchip/PIC16Fxxx_DFP/1.6.156/xc8" -fno-short-double -fno-short-float -O0 -fasmfile -maddrqual=ignore -xassembler-with-cpp -mwarn=-3 -Wa,-a -msummary=-psect,-class,+mem,-hex,-file -ginhx32 -Wl,--data-init -mno-keep-startup -mno-osccal -mno-resetbits -mno-save-resetbits -mno-download -mno-stackcall -mno-default-config-bits -std=c90 -gdwarf-3 -mstack=compiled:auto:auto -Wl,--memorysummary,dist/default/production/memoryfile.xml -o dist/default/production/04_ADC_RS232.X.production.elf build/default/production/main.p1advisory: (2099) legacy C90 library is deprecated and will be discontinued in a future release; consult the release notes for more detailsadvisory: (2095) legacy HI-TECH support is deprecated and will be discontinued in a future release; consult the release notes for more detailsNon line specific message::: advisory: (1492) using updated 32-bit floating-point libraries; improved accuracy might increase code sizemain.c:308:: warning: (1385) function "_itoa" is deprecated (declared at C:\Program Files\Microchip\xc8\v2.46\pic\include\c90\stdlib.h:113)C:\Program Files\Microchip\xc8\v2.46\pic\sources\c90\common\itoa.c:12:: warning: (1385) function "_utoa" is deprecated (declared at C:\Program Files\Microchip\xc8\v2.46\pic\include\c90\stdlib.h:114)main.c:125:: warning: (520) function "_SUART_Receive" is never calledmain.c:167:: warning: (520) function "_SUART_Read_Text" is never calledmain.c:170:: advisory: (1498) pointer (SUART_Read_Text@Output) in expression may have no targetsMemory Summary:Program space used 415h ( 1045) of 800h words ( 51.0%)Data space used 33h ( 51) of 100h bytes ( 19.9%)EEPROM space used 0h ( 0) of 100h bytes ( 0.0%)Configuration bits used 1h ( 1) of 1h word (100.0%)ID Location space used 0h ( 0) of 4h bytes ( 0.0%)make: Leaving directory 'F:/XC8_WORK/XC8_16F819/04_ADC_RS232.X'BUILD SUCCESSFUL (total time: 8s)Loading code from F:/XC8_WORK/XC8_16F819/04_ADC_RS232.X/dist/default/production/04_ADC_RS232.X.production.hex...Program loaded with pack,PIC16Fxxx_DFP,1.6.156,MicrochipLoading completedConnecting to programmer...Programming target...Programming completedRunning target...Here is the terminal outputCode:Hello!Software uart, 9600 bauds.ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789ADC result is: w mV.It looks like I might need to implement my ow itoa() routine later but I'd like to avoid that if possible. Just want to get a basic reading from the ADC first. So I just need the "ADC result is: w mv" to read "ADC result is: 5000 mv" for example.

author: Ian.M
author_rank: Super Contributor
timestamp: May 31, 2024, 10:31:12 am
content: The advisories and depreciated warning can be ignored for now but if you upgrade the compiler may be serious.All the pointer conversion warnings need to be investigated. They are scattered over a 65 line number range in your source so cant just be the code snippet I gave you. Many of them *may* be due to the string literals.Code:main.c:311:25: warning: (357) illegal conversion of integer to pointeris the most serious warning. It is *RARE* to need to make a pointer from an integer so this is probably a bug. As itoa() isn't part of the C99 or earlier standards, its implementation varies from compiler to compiler, it is possible the XC8 C90 version of it has arguments in a different order or not of the types you expect, so if this error corresponds to the itoa() conversion line, you need to fix that to match the parameter type and order of the C90 library function.

author: djsb
author_rank: Super Contributor
timestamp: May 31, 2024, 11:03:48 am
content: I've asked for help on the Microchip XC8 subforum. Hoping they can shed some light. Thanks.

author: Ian.M
author_rank: Super Contributor
timestamp: May 31, 2024, 11:19:27 am
content: Hint: When asking for help (here or elsewhere), attach your zipped project so anyone helping you can open it in MPLAB X, and when posting multiple lines of source in code tags and asking about errors or warnings, flag the offending lines with // comments giving the line number e.g. if you have an error on line 123, flag it in your posted code as:Code:a_C_statement_causing_an_error; // #123so those reading your source without opening it in MPLAB can match up the errors with the lines of code.Fix whatever is wrong with line 311 and it should 'just work'. The 'w' where you should have a number in your output, indicates the string is corrupted which strongly indicates a problem with the itoa() call or the library function code for it. You could try initialising buff with a test number e.g. declare it as:Code:char buff="-99999"; // space for (sign) + max. 5 digits + nulland see if commenting out the itoa() call will let that test data through, which would validate the rest of your code (except the result to voltage conversion).

author: Fenstergucker
author_rank: Regular Contributor
timestamp: May 31, 2024, 11:20:40 am
content: In the Microchip forum the code with the line 'SUART_Write_Text(voltage);' is wrong, you want to output the buffer 'buff'.Peter

author: Ian.M
author_rank: Super Contributor
timestamp: May 31, 2024, 11:30:30 am
content: Yes, that would account for theCode:main.c:311:25: warning: (357) illegal conversion of integer to pointerand the corrupted output.http://www.catb.org/jargon/html/D/DWIM.htmlI may be partially to blame here as I think I may have had an incorrect 'voltage' in that line for a few minutes while I was revising my original post for content and accuracy. I'm afraid it was a case of PEBKAC error: insufficiently caffeinated!

author: djsb
author_rank: Super Contributor
timestamp: May 31, 2024, 11:42:30 am
content: Thanks everyone. Problem solved. The error messages have gone, and I'm getting a reading between 2mV and 4978mV which is good enough.

author: djsb
author_rank: Super Contributor
timestamp: May 31, 2024, 11:50:29 am
content: I may however have to do some research into how to get around the C90 itoa() deprecation issue. What would the recommendation be apart from implementing my own itoa() routine (which I have found a few examples of on the microchip forum). Thanks.PS I would also like to thank DavidAlfa for the original help with the RS232 code detailed in an earlier post herehttps://www.eevblog.com/forum/microcontrollers/xc8-soft-uart-on-pic16f819-help-please/msg5477563/#msg5477563

author: Ian.M
author_rank: Super Contributor
timestamp: May 31, 2024, 12:10:50 pm
content: On more powerful embedded processors, with plenty of program and data memory, if you can tolerate the speed penalty, its common to use printf() family functions for number formatting output, and even use floating point maths for the convenience. However on the classic midrange 14 bit core PICs there isn't much RAM or FLASH to start with, and you generally cant afford the bloat of the library printf() routines or floating point support routines, so writing your own minimalist number to ASCII string and/or number formatting functions that do *exactly* what you want, and *NOTHING* else, is quite usual.The only fix for the C90 library itoa() deprecation issue would be to supply an itoa() function with XC8 C99 compliant source . As Microchip choose not to (and probably for good reason:https://stackoverflow.com/questions/10162733/atoi-is-a-standard-function-but-itoa-is-not-why), that's down to you. Fortunately a base 10 only itoa() is fairly trivial to implement, and handling base 16 is not much harder. If you do write your own itoa(), it may be worth extending it to:Code:char* fp_itoa(int num, char* buffer, int base, int dp);with the extra parameter dp to tell it where to conditionally insert a decimal point, by specifying a non-zero number of decimal places.

author: DavidAlfa
author_rank: Super Contributor
timestamp: May 31, 2024, 02:01:18 pm
content: You already asked this and got it solvedhere, with custom itoa or from xc8 lib.Var result is not a string, but an integer.Code:SUART_Write_Text(result);You should do something like:Code:char str;itoa(str, result, 10); -> convert integer into array, i.e. '1234' into .SUART_Write_Text(str);

author: Ian.M
author_rank: Super Contributor
timestamp: May 31, 2024, 04:43:47 pm
content: Please explain where my version of that calculation overflows. result is initially in the range 0 to 1023, so plenty of room to double it. The multiplication by 5000L an explicit long numeric constant, according to the 'Usual Arithmetic Conversions' of C should (as I understand it) force the multiplication operator to promote to long. The rest of the calculation up until the cast (int) maintains long. XC8 int is 16 bits, and (unless you set a depreciated compiler flag to override to 24 bit) long is 32 bit. Admittedly, it *will* overflow a 24 bit int, but they aren't the default.

author: DavidAlfa
author_rank: Super Contributor
timestamp: May 31, 2024, 06:14:23 pm
content: Edit: Deleted the pointless answer!

author: Ian.M
author_rank: Super Contributor
timestamp: May 31, 2024, 07:00:06 pm
content: Doesn't*5000Lpromote the result of the bracketed expression preceding the * operator to long?

author: Nominal Animal
author_rank: Super Contributor
timestamp: May 31, 2024, 07:18:42 pm
content: If reference voltages are \$V_\max\$ and \$V_\min\$ in millivolts, and you have an \$N\$-bit ADC, the reading \$x\$ corresponds to \$v\$ millivolts,$$v = \left\lfloor V_\min + (V_\max - V_\min) \frac{x \, + \, 2^{N-1}}{2^N} \right\rfloor$$For 0..5V,$$v = \left\lfloor \frac{5000 x + 512}{1024} \right\rfloor = \left\lfloor \frac{1250 x + 128}{256} \right\rfloor = \left\lfloor \frac{625 x + 64}{128} \right\rfloor$$because 5000/1024 = 1250/256 =625/128 exactly. In C, I'd use((uint32_t)x * (uint32_t)1250 + 128) >> 8.For 0..3.3V,$$v = \left\lfloor \frac{3300 x + 512}{1024} \right\rfloor = \left\lfloor \frac{825 x + 128}{256} \right\rfloor$$because 3300/1024 = 825/256 exactly. In C, I'd use((uint32_t)x * (uint32_t)825 + 128) >> 8.Quote from: Ian.M on May 31, 2024, 07:00:06 pmFor(a + b) * 825L, the sum is promoted tolongbefore multiplication, yes. (That is, the addition is done at the range and precision dictated by the types ofaandb, but the result is promoted tolongand the multiplication islong×long=long.)And you are right, DavidAlfa missed the promotion.To make things as clear to us humans as they are to the compiler, I do like to explicitly cast both sides of multiplications and divisions. Less cognitive load.The C integer promotion and casting rules are a bit funky. For example, givenint x,(int)(char)xis only equivalent to the originalxif the value also fits in achar.

author: DavidAlfa
author_rank: Super Contributor
timestamp: May 31, 2024, 09:33:17 pm
content: Yep I missed it!djsb, always start with simple tests before going further.Like:Code:char str;SUART_Write_Text("itoa result of 1234 is: ");itoa(str, 1234L, 10);SUART_Write_Text(str);SUART_Write_Text("\r\n");while(1);