Summary: I am writing a ttyUSB snooper. The code is supposed to read(2) data from a serial port and write(2) it over to another serial port. Reading from the serial port works great with /bin/cat but fails with my code.
Hardware Setup is: I made an FTDI cross-over cable, and put one end in the Windows XP machine as Com2 and the other end in a modern Linux machine as /dev/ttyUSB0. The Linux machine has a USB to Serial cable that shows up as /dev/ttyUSB1. It is connected to the actual hardware unit I am trying to snoop. I verified, the hardware works great.
This part works: I will "cat /dev/ttyUSB0 > /tmp/data" and then have the WinXP machine issue the "read from the device over Com2", and the following six (6) bytes data will be sent over.
\x02\x01\x40\x00\x0a\x9e
This "packet" is sent 4 times or so with a very slight delay, this appears to be the WinXP code just trying a few times. If I replay this just once, it works.
If I simply do "cat /tmp/data > /dev/ttyUSB1", the hardware device will respond properly suggesting that it received the command. Great!
Problem and My Code: I am writing some code to be run on the Linux machine that will read(2) from /dev/ttyUSB0 and write it over to /dev/ttyUSB1 and vice versa. However, for some unknown reason it will only receive 4 bytes in the first "packet", then 5 in the subsequent 3 attempts. Sometimes, the 5 bytes appear slightly "damaged", meaning I see \xffffff for the last or second to last byte. Here is my code:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <strings.h>
void hexdump(char *data, int size) {
for (size_t i = 0; i < size; ++i)
printf("%02x ", data[i]);
putchar('\n');
}
int main() {
int fdzero;
int fdone;
int maxfd;
fd_set sockrd;
struct timeval timer;
char data[10];
int size;
char tmp;
fdzero = open("/dev/ttyUSB0", O_RDWR);
if (fdzero == -1) {
perror("Failed to open /dev/ttyUSB0");
return 1;
}
fdone = open("/dev/ttyUSB1", O_RDWR);
if (fdone == -1) {
perror("Failed to open /dev/ttyUSB1");
return 1;
}
if (fdzero > fdone)
maxfd = fdzero;
else
maxfd = fdone;
printf("Enter loop\n");
for(;;) {
bzero(data, 10);
// fflush(NULL);
FD_ZERO(&sockrd);
FD_SET(fdzero, &sockrd);
FD_SET(fdone, &sockrd);
timer.tv_sec = 60;
timer.tv_usec = 0;
select(maxfd+1, &sockrd, NULL, NULL, &timer);
if (FD_ISSET(fdzero, &sockrd)) {
size = read(fdzero, data, 10);
if (size == -1) {
perror("Failed to read /dev/ttyUSB0");
break;
}
size = write(fdone, data, size);
if (size == -1) {
perror("Failed to write to /dev/ttyUSB1");
break;
}
printf("ttyUSB0 -> ttyUSB1: %d\n", size);
}
// This portion does not trigger yet, but its a mirror
// Yes, I know...bad code :(
else {
size = read(fdone, data, 10);
if (size == -1) {
perror("Failed to read /dev/ttyUSB1");
break;
}
size = write(fdzero, data, size);
if (size == -1) {
perror("Failed to write to /dev/ttyUSB0");
break;
}
printf("ttyUSB1 -> ttyUSB0: %d\n", size);
}
// Used to monitor what is read()/write()
hexdump(data, size);
}
return 0;
}
When I actually run this code, I see this:
# cc snoop.c -o snoop
# ./snoop
Enter loop
ttyUSB0 -> ttyUSB1: 4
02 00 40 ffffff9e
ttyUSB0 -> ttyUSB1: 4
02 00 40 ffffff9e
ttyUSB0 -> ttyUSB1: 4
02 00 40 ffffff9e
ttyUSB0 -> ttyUSB1: 5
01 02 00 40 ffffff9e
ttyUSB0 -> ttyUSB1: 5
01 02 00 40 ffffff9e
ttyUSB0 -> ttyUSB1: 5
01 02 00 40 ffffff9e
Notice that only 4 or 5 bytes are being received and subsequently sent over at any given time. Not 6. Also, notice that the "packet" is distorted. What in the world would cause that???
Rationality if you're interested: I have old software that ONLY runs on Windows XP and will NOT work in a VM (this is a known issue). I would love to capture the traffic going over the serial port. I purchased a WinXP machine just to do this.
Okay, so your two questions here:
Sometimes, the 5 bytes appear slightly "damaged", meaning I see \xffffff for the last or second to last byte.
This is because of how printf interprets the data coming in(this may be of interest). It is being passed in as a char, which is signed. The high-order bits are being interpreted in this case. To fix this part, your hexdump(char* data, int len) should be either hexdump(unsigned char* data, int len) or use a byte-sized type such as uint8_t, so that your signature looks like hexdump(uint8_t* data, int len).
However, for some unknown reason it will only receive 4 bytes in the first "packet", then 5 in the subsequent 3 attempts.
This is almost certainly due to the fact that you do not set any settings on the serial port. One of the characters that you have is 0x0A, which is the linefeed character. This is either being ignored by the serial port driver, or translated into a different character all together. To fix this, you must set the serial port settings to be raw and not translate any characters that come in. I generally do something like the following:
struct termios newio;
if( tcgetattr( fd, &newio ) < 0 ){ /* error handling here */ }
/* Set some default settings */
newio.c_iflag |= IGNBRK;
newio.c_iflag &= ~BRKINT;
newio.c_iflag &= ~ICRNL;
newio.c_oflag = 0;
newio.c_lflag = 0;
newio.c_cc[VTIME] = 0;
newio.c_cc[VMIN] = 1;
/* Set our baud rate */
cfsetospeed( &newio, B9600 );
cfsetispeed( &newio, B9600 );
/* Character size = 8 */
newio.c_cflag &= ~CSIZE;
newio.c_cflag |= CS8;
/* One stop bit */
newio.c_cflag &= ~CSTOPB;
/* Parity = none */
newio.c_iflag &= ~IGNPAR;
newio.c_cflag &= ~( PARODD | PARENB );
newio.c_iflag |= IGNPAR;
/* No flow control */
newio.c_iflag &= ~( IXON | IXOFF | IXANY );
/* Set our serial port settings */
if( tcsetattr( fd, TCSANOW, &newio ) < 0 ) { /* error handling code here */ }
If you don't want to set the serial port settings this way, I've written a small library which should abstract the small details out for you.
Related
I have the following code (I'm using Arduino IDE and a NodeMCU with a MAX3232):
#include <SoftwareSerial.h>
SoftwareSerial RS232Serial(D1, D2); // RX | TX
void setup() {
Serial.begin(9600);
while (!Serial);
RS232Serial.begin(9600);
while (!RS232Serial);
}
void loop() {
Serial.println("Sending msg...");
char msg[] = {0x7E, 0x00, 0x78, 0xF0, 0x7E};
RS232Serial.write(msg, sizeof(msg));
Serial.println("Waiting incomming message:");
while(RS232Serial.available() <= 0) {
delay(1);
}
while(RS232Serial.available() > 0) {
Serial.print(RS232Serial.read(), HEX);
}
Serial.println("");
Serial.println("------------ END -----------");
}
I'm using Docklight to test the communication. So far I'm able to receive the HEX that the NodeMCU boards sends first char msg[] = {0x7E, 0x00, 0x78, 0xF0, 0x7E};. But I'm unable to receive the data properly from Docklight.
The HEX I'm trying to send from Docklight is 7E 00 00 70 15 7E. Debugging with serial monitor, sometimes I receive just 7E and sometimes 0FF, never the complete message. Baud rate, start/stop bits and parity are set to default.
What's wrong with my code?
One reason for not working as expected is:
You are processing the answer as soon as one byte has appeared in the receive buffer.
There is a related example in the Docklight Application Note Arduino Serial Communication - check the code listing on page 5. It makes use of the Arduino SerialEvent() function to collect the incoming telegram byte-per-byte. Only after receiving the end of telegram mark (here: a line feed "\n" character), it sets a flag that tells the code in the main loop() to do something with the accumulated data telegram.
The SerialEvent approach used in the Docklight exampleis not a must. You can solve this inside loop() alone, too. But you certainly need to change the
while(RS232Serial.available() <= 0) {
...
while(RS232Serial.available() > 0) {
parts to something that will wait until a full telegram has been received (or a timeout has occurred).
Now what irritates me still is that you write you sometimes receive "0FF" even though you never send it from Docklight. This could indicate still a second problem on the actual RS232 communication, but it's hard to guess without extra information.
But re-working the telegram receiving part as described above should already make things much clearer and then the source of the "0FF" is probably more obvious.
while(RS232Serial.available() <= 0) {
delay(1);
}
You are only waiting for the serial buffer to have at least 1 byte, but you are not guaranteed that your full message (6 bytes) will be present, when you call RS232Serial.available().
You should wait for the serial buffer to accumulate at least 6 bytes, before you try to read them.
while(RS232Serial.available() < 6) {
delay(1);
}
They you can read the full buffer with:
while(RS232Serial.available() > 0) {
Serial.print(RS232Serial.read(), HEX);
}
or only 6 bytes with:
for (int i=0; i<6; i++) {
Serial.print(RS232Serial.read(), HEX);
}
I don't know about your 0FF, but Serial.print(RS232Serial.read(), HEX) doesn't zero pad the hex string and this could actually be 00 0F 0F or 00 FF.
You should print out the bytes with:
byte b = RS232Serial.read();
if (b < 16) Serial.print('0'); // zero pad
Serial.print(b, HEX);
Serial.print(' '); // add space after each byte
I am trying to build a code in arduino, and am having an issue with getting it to compile. I am trying to build a monitor for my MPPT controller, it outputs via serial. i am able to receive the serial data to the pc through the arduino, and am now having an issue getting it to save values from the incomming serial information and store its value as a variable that i can call to perform other functions.
the serial data incomming into the pc is always the same, and is formatted like this
PID 0xA042
FW 123
SER# HQ155154LPY
V 0
I 0
VPV 0
PPV 0
CS 0
ERR 0
LOAD OFF
IL 0
H19 4438
H20 0
H21 0
H22 38
H23 136
HSDS 74
Checksum
and i need the values from the following fields
V, I,VPV,PPV,LOAD,H19,IL
the information is sent from the controller like this
Message format
The device transmits blocks of data at 1 second intervals. Each field is sent using the following format:
<Newline><Field-Label><Tab><Field-Value>
The identifiers are defined as follows:
Identifier Meaning
<Newline> A carriage return followed by a line feed (0x0D, 0x0A).
<Field-Label> An arbitrary length label that identifies the field. Where applicable, this will be the same as the label that is used on the LCD.
<Tab> A horizontal tab (0x09).
<Field-Value> The ASCII formatted value of this field. The number of characters transmitted depends on the magnitude and sign of the value.
Data integrity
The statistics are grouped in blocks with a checksum appended. The last field in a block will always be "Checksum". The value is a single byte, and will not necessarily be a printable ASCII character. The modulo 256 sum of all bytes in a block will equal 0 if there were no transmission errors. Multiple blocks are sent containing different fields.
this is the first time i have actualy tried to use serial for something other than simple debug/operation messages running in the code.
the code
/*
Odroid-SHOW MPPT Intrface Module
Reads information from MPPT controllers and displays it on the LCD, also outputs the information via SERIAL # 9600 BAUD
* **********PINOUTS********
MPPT Controller:
________________________
| 4 3 2 1 |
| . . . . |
| VCC TX RX GND |
| (RED) (YEL)(WHI)(BLK) |
| _______ |
|_______| |________|
Odroid SHOW:
5V - To VCC (RED) Pin (4)
A4 - To TX (YEL) Pin (3)
A5 - To RX (WHI) Pin (2)
GND - To GND (BLK) Pin (1)
*/
const char version[] = {"1.0 27/04/2017"};
//Below is the required Library inclusions
#include <SoftwareSerial.h>
#include <SPI.h>
#include <Wire.h>
#include "TimerOne.h"
#include <Adafruit_GFX.h>
#include <ODROID_Si1132.h>
#include <ODROID_Si70xx.h>
#include <Adafruit_ILI9340.h>
//Below is the required Pin Defines and links for the TFT
#define _sclk 13
#define _miso 12
#define _mosi 11
#define _cs 10
#define _dc 9
#define _rst 8
Adafruit_ILI9340 tft = Adafruit_ILI9340(_cs, _dc, _rst);
ODROID_Si70xx si7020;
ODROID_Si1132 si1132;
// Settings below are to control the Backlight brightness and rotation of the Odroid-SHOW LCD Display
uint8_t ledPin = 5;
uint8_t pwm = 255;
uint8_t textSize = 2;
uint8_t rotation = 1; //0 = Portrait, 1 = Landscape
//Below sets up the SoftwareSerial ports so that the arduino can receive information from the MPPT Controller aswell as pass its information through to the hardware UART
SoftwareSerial MPPTSERIAL(A4, A5); // RX, TX
void setup() {
// initialize the TFT and the digital pins for TFT backlight control
tft.begin();
si1132.begin();
initPins();
delay(50);
// Open serial communications on hardware UART and wait for port to open:
Serial.begin(9600);
delay(50);
//Below Prints the module Version information to both the Serial monitor and the TFT
Serial.println("Odroid-SHOW MPPT Intrface Module");
Serial.print("Module Firmware Version ");
Serial.println(version);
tft.fillScreen(ILI9340_BLACK);
tft.setRotation(rotation);
tft.setTextSize(textSize);
tft.setCursor(25, 25);
tft.print("Odroid-SHOW");
tft.setCursor(25, 45);
tft.print("MPPT Intrface Module");
tft.setCursor(25, 85);
tft.print("Module Firmware Version");
tft.setCursor(25, 105);
tft.print(version);
delay(2500);
// set the data rate for the SoftwareSerial port and print to TFT when initialized
MPPTSERIAL.begin(19200);
Serial.println("MPPT Serial Connection Initialized");
tft.setRotation(rotation);
tft.setTextSize(textSize);
tft.setCursor(25, 145);
tft.print("MPPT Serial");
tft.setCursor(25, 165);
tft.print("Connection Initialized");
delay(2500);
tft.fillScreen(ILI9340_BLACK);
}
void loop() {
if (MPPTSERIAL.available())
{
char inComing = MPPTSERIAL.read(); //read the available byte into a variable
String BattVoltage = getValue(inComing, "/n", 3).toString();
String PVCurrent = getValue(inComing, "/n", 4).toString();
String PVVoltage = getValue(inComing, "/n", 5).toString();
Serial.println(BattVoltage); //output the Battery Voltage to Serial Monitor
Serial.println(PVCurrent); //output the PV Charge Current to Serial Monitor
Serial.println(BattVoltage); //output the PV Voltage to Serial Monitor
tft.setRotation(rotation);
tft.setTextSize(textSize);
tft.setCursor(25, 25);
tft.print("Battery Voltage:")(BattVoltage); //output Battery Voltage to TFT
tft.setCursor(25, 145);
tft.print ("PV Current:")(PVCurrent); //output PV Charge Current to TFT
tft.setCursor(25, 165);
tft.print("PV Voltage:")(PVVoltage); //output PV Voltage to TFT
tft.fillScreen(ILI9340_BLACK);
}
if (Serial.available()) {
MPPTSERIAL.write(Serial.read());
}
}
String getValue(String data, char separator, int index)
{
int found = 0;
int strIndex[] = { 0, -1 };
int maxIndex = data.length() - 1;
for (int i = 0; i <= maxIndex && found <= index; i++) {
if (data.charAt(i) == separator || i == maxIndex) {
found++;
strIndex[0] = strIndex[1] + 1;
strIndex[1] = (i == maxIndex) ? i+1 : i;
}
}
return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
}
//Below is the various Function Calls
//This is the Backlight PWM control pin settings
void initPins(){
pinMode(ledPin, OUTPUT);
analogWrite(ledPin, pwm);
}
The IDE highlights these lines as the source of the error
String BattVoltage = getValue(inComing, "/n", 3).toString();
String PVCurrent = getValue(inComing, "/n", 4).toString();
String PVVoltage = getValue(inComing, "/n", 5).toString();
and that is giving me this error
converting to 'String' from initializer list would use explicit constructor 'String::String(char)'
from what i can gather, i may need to change it from a srting to an INT or a CHAR, but all the examples i have tried and just guessed out of hope have failed.
any pointers?
Jason
I'm actually working with a PIC32MX795F512L and MPLABX V2.10 and XC32 on a little project et I need to send data (for now, just 0 or 1) via RX/TX to "something" that convert it to USB.
The problem is that i'm receiving strange things
#define UART_BAUD_RATE 9600
char* cmd;
int main(void) {
UARTConfigure(UART1, UART_ENABLE_PINS_TX_RX_ONLY);
UARTSetFifoMode(UART1, UART_INTERRUPT_ON_TX_NOT_FULL | UART_INTERRUPT_ON_RX_NOT_EMPTY);
UARTSetLineControl(UART1, UART_DATA_SIZE_8_BITS | UART_PARITY_NONE | UART_STOP_BITS_1);
UARTSetDataRate(UART1, GetPeripheralClock(), UART_BAUD_RATE);
UARTEnable(UART1, UART_ENABLE_FLAGS(UART_PERIPHERAL | UART_RX | UART_TX));
while (1)
{
cmd="1";
uart_send_data((BYTE*)cmd,1);
The uart_send_data function is:
void uart_send_data(BYTE *buffer, UINT8 size)
{
UINT8 i;
for( i=0; i<size; i++ )
{
uart_put_c(*buffer);
buffer++;
}
while(!UARTTransmissionHasCompleted(UART1));
}
And then uart_put_c:
void uart_put_c(char c)
{
while(!UARTTransmitterIsReady(UART1));
UARTSendDataByte(UART1, c);
}
So, I'm sending a 1 or a on TX1. But, when I look at what I'm receiving on my USB port (thanks to Docklight) i get in ascii: ð |ð, in hex: 0C F0 00 F0 0C ect ...
So, does someone knows where my problem is coming from?
Thansk in advance.
Cheers
I'm just learning how to handle sockets and TCP connections in C. I've got an application (a long one) which basically sends and receives char arrays with the system call write from server to client and vice versa (two separate C applications of course). As long as I use it with a local connection, on the same PC, running the server on a terminal and the client on an another, everything just works fine and the data arrives at the destination. But if I try it with the server on one computer and the client on another but on the same internet line, passing to the client an address like 192.168.1.X (took from the machine on which the server is running), after the connection is established, I've got an error that tells me that the number of expected bytes (which I pass before sending the real char[]) isn't arrived. Same thing if I try the server on my PC, and the client on another one with a different line on a different provider.
There's something I'm missing, are there any limitations in sending a bunch of bytes in sequence?
The code where the error pops up.
SERVER SIDE:
r=htonl(lghstr);
w=write(myFd,&r,sizeof(int));//writes the number of incoming bytes
if(w<0) perror("writeServer4"),exit(-1);
w=write(myFd,tmp->string,lghstr);
if(w<0) perror("writeServer5"),exit(-1);
if(w!=lghstr) perror("ERROR");
CLIENT SIDE
rC=read(fdc,&cod,sizeof(int));//read incoming number of bytes
lghstr=ntohl(cod);
if(rC<0) perror("readClient3"),exit(-1);
rC=read(fdc,dest,lghstr);
if(rC<0) perror("readClient4"),exit(-1);
if(rC!=lghstr) perror("error : "), printf("didn't read the right number of bytes"),exit(-1);
Now this is basically repeated a lot of times, let's even say 300 times, and it's with big numbers that the program doesn't work.
This is the problem:
rC=read(fdc,dest,lghstr);
...
if(rC!=lghstr) perror("error : ")
The #1 fallacy with socket programming is expecting that recv() and read() will return exactly the same number of bytes corresponding to the write/send call made by the other side.
In reality, partial data is extremely likely and expected. The simple workaround is to loop on read/recv until you get the exact number of bytes expected:
size_t count = 0;
while (count < lghstr)
{
ssize_t readresult = read(fdc, dest+count, lghstr-count);
if (readresult == -1)
{
// socket error - handle appropriately (typically, just close the connection)
}
else if (readresult == 0)
{
// The other side closed the connection - handle appropriately (close the connection)
}
else
{
count += readresult;
}
}
The other alternative to looping is to the use the MSG_WAITALL flag with the socket. This means, using recv() instead of read(). You'll still need to handle the error cases.
rc = recv(fdc, dest, lghstr, MSG_WAITALL);
if (rc == -1)
{
// socket error
}
else if (rc == 0)
{
// socket closed by remote
}
else if (rc < lghstr)
{
// the other side likely closed the connection and this is residual data (next recv will return 0)
}
You do ntohl() on one side and not the other. That might be interpreting the bytes with the wrong value.
You should printf() the bytes on both sides and see what the int is being evaluated to.
Edit: I'm convinced this is a programming bug for the record.
If I had to guess, I'd say that you are not synchronous with the other side for some reason. You say this runs 'about 300 times'.
Try adding a magic integer to the protocol.
Heres an example of a client that sends in this order.
A magic integer which is always constant.
A lengh of bytes about to be sent.
The bytes to be sent.
This uses scatter gather mechanics (its nicer for serialization) but other than that it effectively is doing the same thing yours is doing, as a client, just adding a magic value.
When the receiver receives the data, it can validate that the data is coming in the right order, by checking what the magic number was that came in. If the magic is wrong it means the client or server has lost themselves positionally in the stream.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <err.h>
#include <time.h>
#define MAGIC 0xDEADBEEFLU
#define GARBAGE_MAX 65536
const int iterations = 3000;
char * create_garbage_buf(
void)
{
int rc = -1;
int fd = -1;
char *buf = NULL;
buf = malloc(GARBAGE_MAX);
if (!buf)
err(1, "Cannot allocate buf");
fd = open("/dev/urandom", O_RDONLY);
if (fd < 0)
err(1, "Cannot open urandom");
rc = read(fd, buf, GARBAGE_MAX);
if (rc < 0)
err(1, "Cannot read from urandom");
else if (rc != GARBAGE_MAX)
errx(1, "Expected %d bytes, but got %d reading from urandom",
GARBAGE_MAX, rc);
close(fd);
return buf;
}
int main() {
int fd, offset, i, rc;
uint32_t magic = MAGIC;
uint32_t blen = 0;
char *buf = NULL;
struct iovec vecs[3];
/* Seed poor random number generator */
srand(time(NULL));
/* Use a file for demonstration, but a socket will do just fine */
fd = open("/dev/null", O_WRONLY);
/* Create some garbage to send */
buf = create_garbage_buf();
if (fd < 0)
err(1, "Cannot open file");
/* The first vector, is always the magic */
vecs[0].iov_len = sizeof(uint32_t);
vecs[0].iov_base = &magic;
for (i=0; i < iterations; i++) {
/* The second vector represents lengh of what we send
* in this demonstration it is a number between 0 and
* GARBAGE_MAX/2.
*/
blen = rand() % (GARBAGE_MAX / 2);
vecs[1].iov_len = sizeof(uint32_t);
vecs[1].iov_base = &blen;
/* The last record is the data to send. Its another random
* number between 0 and GARBAGE_MAX which represents the offset
* in our garbage data to send */
offset = rand() % (GARBAGE_MAX / 2);
vecs[2].iov_len = blen;
vecs[2].iov_base = &buf[offset];
rc = writev(fd, vecs, 3);
if (rc < 0)
err(1, "Could not write data");
if (rc != (sizeof(uint32_t)*2 + blen))
errx(1, "Did not write proper number of bytes to handle");
printf("Wrote %u bytes from offset %u in garbage\n", blen, offset);
}
free(buf);
printf("Done!\n");
return 0;
}
Closely read the documentation for read()/write() and learn that those two functions do not necessarily read()/write() as much bytes as they were told to, but few. So looping around such calls counting until all data expected had been read/written is a good idea, not to say an essential necessity.
For examples how this could be done for writing you might like to have look at this answer: https://stackoverflow.com/a/24260280/694576 and for reading on this answer: https://stackoverflow.com/a/20149925/694576
I try to send the data from pc to the pic microcontroller. I am a beginner in PIC.
I send the data from hyperterminal and the data will display in the led in port B of PIC.
I used 10Mhz clock and the connection in 9600 baudrate.
here my uart.h program:
char UART_Init(const long int baudrate)
{
unsigned int x;
x = (_XTAL_FREQ - baudrate*64)/(baudrate*64);
if(x>255)
{
x = (_XTAL_FREQ - baudrate*16)/(baudrate*16);
BRGH = 1;
}
if(x<256)
{
SPBRG = x;
SYNC = 0;
SPEN = 1;
TRISC7 = 1;
TRISC6 = 1;
CREN = 1;
TXEN = 1;
return 1;
}
return 0;
}
char UART_TX_Empty()
{
return TRMT;
}
char UART_Data_Ready()
{
return RCIF;
}
char UART_Read()
{
while(!RCIF);
return RCREG;
}
void UART_Read_Text(char *Output, unsigned int length)
{
int i;
for(int i=0;i<length;i++)
Output[i] = UART_Read();
}
void UART_Write(char data)
{
while(!TRMT);
TXREG = data;
}
void UART_Write_Text(char *text)
{
int i;
for(i=0;text[i]!='\0';i++)
UART_Write(text[i]);
}
and this is my main program:
#include<htc.h>
#include<pic.h>
#define _XTAL_FREQ 10000000 //Clock Frequency
#include "uart.h"
void main()
{
TRISB = 0x00; //PORTB as Output
UART_Init(9600);
do
{
if(UART_Data_Ready())
PORTB = UART_Read();
__delay_ms(1000);
}while(1);
}
in hyperteminal I send data say 10010010 but the led in port B do not respond, are there any error in my program?
You have several steps: initialize UART, initialize LEDs, communicate over UART and setup your PC's UART. Which components have you successfully written and tested? You say you're a beginner, so what is the smallest functional program you have successfully executed on a PIC? I've been working with microcontrollers for years, but I still schedule about a whole day to get a single LED to turn on because it could be a software problem, a hardware problem, a voltage problem, an oscillator problem, a PCB problem or a compiler problem.
Here are the steps I take for microchip bring up:
Go over the oscillator section, the configuration bits section, the watchdog section and the pinout section (looking for VDD and VSS) in the datasheet. These are some of the hardest parts to get right. (A gotcha about the oscillator: just because you can program a chip, doesn't mean the oscillator is working because the programmer provides it's own clock.)
Write the bare-minimum code to turn on a single LED.
Write the bare-minimum code to make the LED blink (just use a for-loop delay for now, timers come later)
Write UART initialization code and transmit a single character, I use captial U because it's pretty in binary. TXREG = 'U';
Connect the UART to a PC and see if the hyperterminal sees the U. If it doesn't, I connect an oscilscope to the lines to make sure that the PIC is transmitting, that the PC transmits when I type characters and that the timing of the edges matches.
Within the PIC code, have the UART echo characters from the terminal. (TXREG = RXREG;), and then type on the hyperterminal and make sure the characters are echoed back.
One more note:
Do not have the PIC perform the SPBRG calculation. PIC16 are 8-bit processors and 10000000 requires 32-bits to store. There might be hiccups with the integer divison. It might not have a bug in it, but there's no need to have the PIC calculate it each time. Calculate it before-hand and hard-code the value.