Jump to content
Sign in to follow this  
skyrat

How To Transmitt A Variable Via Rs232 - Pic16f877a

Recommended Posts

I am trying to transmit the 10-bit ADC value measured on the PIC16F877A to the serial port on a PC.

 

I have successfully got the RS232 port working by testing it with the following code...

 

#include <system.h>

#define bit_time 104 // 9600 baud at 4MHz (not used but required for compiling)

// PIC16F87x defaults for hardware USART support
#define TX_PORT 0x07
#define TX_TRIS 0x87
#define TX_BIT 6
#define RX_PORT 0x07
#define RX_TRIS 0x87
#define RX_BIT 7
#define e_SPBRG 0x99
#define e_RCREG 0x1a
#define e_TXREG 0x019
#define e_TXSTA 0x98
#define e_RCSTA 0x18
#define e_TXIF_PIR 0x0c
#define e_RCIF_PIR 0x0c
#define e_TXIF_BIT 4
#define e_RCIF_BIT 5
#define MODE (USART_reset_wdt | USART_HW)

#include <rs232_driver.h>

#pragma CLOCK_FREQ 1000000 
void main()
{
uart_init(1,64);	//gives a baudrate of 9600 at 10MHz
puts("abcdefghijklm");
}

 

However, this simply transmits a string of 'abcdefghijklm'

 

How do I transmitt a variable ?

 

The ADC value is stored in a 16-bit variable (unsigned short).

 

On the CCS compiler, I transmitted varibales via RS232 using the following command...

 

  printf("%d",data);

 

Is there a similar command I can use with Sourceboost ?

Share this post


Link to post
Share on other sites

Side question:

 

Why is it that most people use 9600 baud for their serial connections? I've had plenty of luck over the years with 115K baud but use a 20MHz crystal. Even at 10MHz 56K would work fine (115K is possible also but there may not be enough cycles left for the PIC to do any work between bytes coming in). 9600 baud is less than 1K byte/second.

 

Back to the real question:

 

You could transmit the data as a hex string with something like this:

 

void SendAdcValue(unsigned int AdcValue)
{
   unsigned char Buf[7];

   Buf[0] = '0';  Buf[1] = 'x';
   Buf[2] = (AdcValue>>12) & 0xf);
   Buf[2] += ((Buf[2]>9) ? ('a'-10) : '0');
   Buf[3] = (AdcValue>>8) & 0xf);
   Buf[3] += ((Buf[3]>9) ? ('a'-10) : '0');
   Buf[4] = (AdcValue>>4) & 0xf);
   Buf[4] += ((Buf[4]>9) ? ('a'-10) : '0');
   Buf[5] = (AdcValue>>0) & 0xf);
   Buf[5] += ((Buf[5]>9) ? ('a'-10) : '0');
   Buf[6] = 0;
   puts(Buf);
}

 

Then, on the PC side you could use sscanf(Buf,"%x",&AdcValue) to recover your data.

 

Or, if you want to conserve bandwidth of the RS-232 port your could just send two bytes:

 

putc(AdcValue>>8);
putc(AdcValue&0xff);

 

On the PC side just put the 16-bit value back together again:

 

AdcValue = (getc()<<8);

AdcValue |= getc();

Share this post


Link to post
Share on other sites
Side question:

 

Why is it that most people use 9600 baud for their serial connections? I've had plenty of luck over the years with 115K baud but use a 20MHz crystal.  Even at 10MHz 56K would work fine (115K is possible also but there may not be enough cycles left for the PIC to do any work between bytes coming in).  9600 baud is less than 1K byte/second.

 

Ted, I believe this is due to the fact that most coding examples spread over the net and the books are either outdated (as far as a PC is involved in the communication) or aimed at controlling non-PC serial devices that would not support more than 19200 bauds. Defaulting at 9600 is a wise blind choice: you can never mistake.

 

Of course, 115k is a good choice for modern PC-attached serial devices running at 20 MHz or more. But there are better solutions, of course, if you really have to move back and forth lots of data.

Share this post


Link to post
Share on other sites

Thanks trossin,

 

Side answer: Ive used 115k baud rate on previous PIC projects. Ive just got it set to 9600 until I get this one up and running !

 

Back to my problem....

 

Your example code is fine for the PIC, but Ive no idea how to implement the code for the PC side to recover my data !

 

The idea was to use hyperterminal or serial comms program.

 

At the end of the day all Im trying to do is send some data out as a decimal value, but there doesnt seem to be an easy way of doing this ?

 

The printf statement in CCS did the job great.

 

Hasnt someone out there already written a similar function to printf using Sourceboost, to transmit data to the serial port ?

Share this post


Link to post
Share on other sites
Hasn't someone out there already written a similar function to printf using Sourceboost, to transmit data to the serial port ?

The source code for the C standard library function of printf can be found many places on the net.

 

Implementing a generic printf does present many challenges for embedded applications using microcontrollers with restricted resources like the 16F87x.

 

With BoostC you will be better off to write functions that convert your ADC data to a stream of ASCII characters to be sent to the UART.

 

The code offered by trossin is quite good at getting the job done.

 

There are many other techniques than can be used to convert binary data to ASCII streams of characters.

 

This is a very poor example of converting an integer to a decimal ASCII stream:

/*

  Output signed decimal integer range of -327678 to +32767 
  suppress leading zeros and plus sign.

  Equivalent to: printf("%d",out)

  Note: There are more efficient ways to do this on a PIC.
  
*/

void SignedIntOut(int out)
{
 char c;

 if (out < 0)
 {
   putc('-');
   out = -out;
 }
 if (out > 10000)
 {
   c = out / 10000;
   putc('0'+c);
   out -= c * 10000;
 }
 if (out > 1000)
 {
   c = out / 1000;
   putc('0'+c);
   out -= c * 1000;
 }
 if (out > 100)
 {
   c = out / 100;
   putc('0'+c);
   out -= c * 100;
 }
 if (out > 10)
 {
   c = out / 10;
   putc('0'+c);
   out -= c * 10;
 }
 c = out;
 putc('0'+c);
}

Edited by cac001

Share this post


Link to post
Share on other sites

I send 5 a/d results from one ez controller to another. It uses an 18F252@20Mhz.

 

// Read A1 for desired setpoint
adcon0 = 10000001b;			// Read from AN0
delay_us(20);
set_bit(adcon0, 2);			// INNER GATES
while (A2D_not_done);		// Wait for a2d done flag to change
AdRes[0] = adresh;			//
AdRes[1] = adresl;	
...
...
...to A5

 

for (i=0; i < 10; i++)		// send analog data to control display
	putc(AdRes[i]);

Share this post


Link to post
Share on other sites
Your example code is fine for the PIC, but Ive no idea how to implement the code for the PC side to recover my data !

 

The idea was to use hyperterminal or serial comms program.

 

Here is a C++ class that I wrote years ago to deal with the serial port on a PC. The code works with various flavors of Windows and compiles under VisualC++ but I would guess that gcc would handle it as well if you remove the stdafx.h include line.

 

SerialPort.cpp

#include "stdafx.h"
#include "SerialPort.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

CSerialPort::CSerialPort()
{
   m_hComm = INVALID_HANDLE_VALUE;
}

CSerialPort::~CSerialPort()
{
   if(m_hComm != INVALID_HANDLE_VALUE){
CloseHandle(m_hComm);
   }
}

int CSerialPort::Open(char *PortName, int BaudRate, int ReadTimeOutms)
{
   char s[100];

   DCB dcb;
   COMMTIMEOUTS timeouts;	

   m_hComm = CreateFile(PortName,GENERIC_READ|GENERIC_WRITE
		,0,0,OPEN_EXISTING,0,0);
   if(m_hComm == INVALID_HANDLE_VALUE){
return(-1);
   }

/* Set baud rate */

   FillMemory(&dcb,sizeof(dcb),0);
   dcb.DCBlength = sizeof(dcb);

   sprintf(s,"baud=%d parity=N data=8 stop=1",BaudRate);
   if(!BuildCommDCB(s,&dcb)){
m_hComm = INVALID_HANDLE_VALUE;
CloseHandle(m_hComm);
return(-1);
   }

   if(!SetCommState(m_hComm,&dcb)){
m_hComm = INVALID_HANDLE_VALUE;
CloseHandle(m_hComm);
return(-1);
   }

/* Set time out */

   GetCommTimeouts(m_hComm,&timeouts); 
   timeouts.ReadTotalTimeoutMultiplier = 1;
   timeouts.ReadTotalTimeoutConstant = ReadTimeOutms;
   if(!SetCommTimeouts(m_hComm,&timeouts)){
m_hComm = INVALID_HANDLE_VALUE;
CloseHandle(m_hComm);
return(-1);
   }
   return(0);
}

int CSerialPort::Read(unsigned char *data, int NumBytes)
{
   unsigned long num_bytes_read;

   ReadFile(m_hComm,data,NumBytes,&num_bytes_read,NULL);
   return(num_bytes_read);
}

int CSerialPort::Write(unsigned char *data, int NumBytes)
{
   unsigned long num_bytes_written;

   WriteFile(m_hComm,data,NumBytes,&num_bytes_written,NULL);

   return(num_bytes_written);
}

void CSerialPort::Close(void)
{
   CloseHandle(m_hComm); m_hComm = INVALID_HANDLE_VALUE;
}

 

SerialPort.h

class CSerialPort
{
 protected:
   HANDLE m_hComm;

 public:
   CSerialPort();
   ~CSerialPort();
   int Open(char *PortName, int BaudRate, int ReadTimeOutms);
   int Read(unsigned char *data, int NumBytes);
   int Write(unsigned char *data, int NumBytes);
   void Close(void);
   void Delay(unsigned long);
};

 

Example:

#include "SerialPort.h"

#define WRITE_BLOCK  0x01
#define READ_BLOCK 0x02

main(int argc,char *argv[])
{
   CSerialPort CSerial;
   unsigned char buf[1000];

//    if(CSerial.Open("COM2",19200,400)){    
//    if(CSerial.Open("COM2",115200,400)){

          /* 115200 Baud and timeout of 0.2 seconds */
   if(CSerial.Open("COM1",115200,200)){
printf("Can't open serial port");
exit(1);
   }

   buf[0] = WRITE_BLOCK; 
   CSerial.Write(buf,1);
  
        // Write 256 bytes to the PIC
   for(i=0;i<256;i++){
buf[i] = ~i;
   }
   CSerial.Write(buf,256);

   buf[0] = READ_BLOCK;
   CSerial.Write(buf,1);

   if(CSerial.Read(buf,256)!=256){
          printf("Can't read back port data");
          exit(1);
   }
   for(i=0;i<256;i++){
          printf("ReturnData[%d] = 0x%02x\n",i,buf[i]);
   }
}

Share this post


Link to post
Share on other sites
Implementing a generic printf does present many challenges for embedded applications using microcontrollers with restricted resources like the 16F87x.

 

The printf() function in itself is heavily deprecated for performance (and "security", whatever this means) reasons, and not only in the embedded world. OTOH, printf() implementations in "small devices" C runtime libraries are nothing but a limited subset of the bloated ANSI function.

 

A good standard approach is using itoa() and strcat() the smart way to build the buffer, which in turn is then sent out over the serial link or whatever.

 

My advice is then: if you don't feel like you would mess with a good, structured, robust serial protocol (maybe the easy MODBUS ASCII or RTU), then look for an acceptable itoa() implementation. Not wanting to mention competitors here, but a top notch $$$ aussie PIC C compiler comes with itoa.c among the examples.

 

 

As regards the PC side, well: there are just too many serial libraries - DLL - OCX - .NET components available, threaded and not, priced from zero to more than 2,000 bucks. Some of my beloved evergreens are MarshallSoft and TurboPower (yes, I'm just too lazy to avoid using BCB, even though it's cycles & space greedy - but what's not for "modern" PCs with "modern" OSes ?).

Share this post


Link to post
Share on other sites
...Not wanting to mention competitors here, but a top notch $$$ aussie PIC C compiler comes with itoa.c among the examples...

 

This is an excellent suggestion to to add such example to the BoostC distribution. If anyone wants to donate such code to include into the SourceBoost distribution please send it to support@sourceboost.com. If not than it's not a problem either. We will try to make some time and create such sample ourself.

 

Regards,

Pavel

Share this post


Link to post
Share on other sites
Side question:

 

Why is it that most people use 9600 baud for their serial connections? I've had plenty of luck over the years with 115K baud but use a 20MHz crystal.  Even at 10MHz 56K would work fine (115K is possible also but there may not be enough cycles left for the PIC to do any work between bytes coming in).  9600 baud is less than 1K byte/second.

 

Does using a higher baud use more power though? I'm by no means a hardware expert, but I would think that a higher transmission rate would draw more power. I was wondering about that, maybe someone knows for sure?

 

I'm working ona project that sends 7 bytes about every 10 minutes. I need the battery life of my circuit to be as good as possible, but I don't really need a fast transmission rate.

 

I'm not even sure how one would calculate the power used by an IC. Do you just connect an ammeter and multiply? Anyway, it would be an interesting test. Would any additional power used even be significant? I am still learning hardware so I'm not sure either way. Any ideas?

Share this post


Link to post
Share on other sites
Does using a higher baud use more power though?  I'm by no means a hardware expert, but I would think that a higher transmission rate would draw more power. I was wondering about that, maybe someone knows for sure?

 

I'm working ona project that sends 7 bytes about every 10 minutes. I need the battery life of my circuit to be as good as possible, but I don't really need a fast transmission rate.

 

I'm not even sure how one would calculate the power used by an IC. Do you just connect an ammeter and multiply? Anyway, it would be an interesting test. Would any additional power used even be significant? I am still learning hardware so I'm not sure either way. Any ideas?

 

Using an ammeter to calculate the power of an IC is legitimate, but IC's tend to be VERY dynamic with vastly differing peaks of power.

 

Within the IC itself, the vast majority of power is as a result of transitions, wheras a much much smaller amount is caused by transistor leakage. I'm not sure what the internal structure of the PIC and the SPBRG circuitry is. Assuming on receiving a character the device starts to divide down the system clock to the desired baud rate, it will take MORE system cycles for a slow baud rate than a fast one, so you could argue a faster baud offers greater savings for a give packet of data. Basically, using the SLOWEST system clock to minimise the difference between BAUD and SYSTEM clock is the way to have the most power efficient serial communications. If, as you say, you only send data every several minutes, the power consumed handling serial comms will be negligible IF the SPBRG is stopped when no TX/RX is taking place.

 

If you are really after battery savings putting the device to sleep (if these facilities are available) is the way to go as this ensure only essential transitions are taking place (such as polling of interrupts) when the device isn't busy.

 

Wayne M

Share this post


Link to post
Share on other sites

Your content will need to be approved by a moderator

Guest
You are commenting as a guest. If you have an account, please sign in.
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this  

×