Bug related to UART FIFO under multi-tasks - c

Program attached:
Those codes are written by me based on the TI uart.c driver library.
while(1){
//check if message on Queue -> read or check UART input
if(uxQueueMessagesWaiting( UART_TASKQ ) != 0){ //may have bugs
// deQueue
xQueueReceive( UART_TASKQ, &UARTTaskHandle, 0x0A );
//do the task's mission using the data in the stucture(put by control
task)
//Print out the input data.
//**********debugging data
/*
testPointer = UARTTaskHandle->dataBuffer;
testAmount = UARTTaskHandle->dataSize;
while(testAmount){
if(*testPointer != 1){
error = error + 1;
}
if(*(testPointer + 1) != 2){
error = error + 1;
}
if(*(testPointer + 2) != 3){
error = error + 1;
}
if(*(testPointer + 3) != 4){
error = error + 1;
}
if(*(testPointer + 4) != 5){
error = error + 1;
}
if(*(testPointer + 5) != 6){
error = error + 1;
}
if(*(testPointer + 6) != 7){
error = error + 1;
}
if(*(testPointer + 7) != 8){
error = error + 1;
}
testPointer = testPointer + 8;
testAmount = testAmount - 8;
}
*/
if(UART_write(UART_Handle, UARTTaskHandle->dataBuffer, UARTTaskHandle->dataSize, 0xff ) >= 0){
UARTwriteCount = UARTwriteCount + 1;
}
//let control task take new command
//free allocated memory
free(UARTTaskHandle->dataBuffer);
free(UARTTaskHandle); // free memory space
//(above is code using UART)
//here are UART driver code:
unsigned long UARTStatus(unsigned long ulBase){
ASSERT(UARTBaseValid(ulBase));
return(HWREG(ulBase + UART_O_FR));
}
//*****************************************
//UART_ISR
//Interrupt service routine for
//the UART read and write process
//*****************************************
void UART_ISR(){
//read FIFO full or read time out
if(UARTIntStatus(UART_Handle->UART_PORT,false) & (UART_INT_RX | UART_INT_RT)){
UARTIntClear(UART_Handle->UART_PORT, UART_INT_RX | UART_INT_RT); //clear INT flag
while (!(UARTStatus(UART_Handle->UART_PORT) & UART_FR_RXFE)){
//data reading
*UART_Handle->pCurrentRead = UARTCharGet(UART_Handle->UART_PORT); //read autoly clear INT
UART_Handle->pCurrentRead++;
UART_Handle->ReadLength--;
//adjust code here:
if(UART_Handle->ReadLength == 0){
break;
}
}
//check if read certain bytes finished
if(UART_Handle->ReadLength == 0){
memcpy(UART_Handle->dataput, UART_Handle->pReadBuf,UART_Handle->ReadLengthcpy); // copy data back
xSemaphoreGiveFromISR( UART_Handle->UARTRead_Semaphore, &xHigherPriorityTaskWoken );// release semaphore
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );//forcing context exchange
}
}
//send FIFO empty
if(UARTIntStatus(UART_Handle->UART_PORT,false) & UART_INT_TX){
UARTIntClear(UART_Handle->UART_PORT, UART_INT_TX); //clear INT flag
if(UART_Handle->WriteLength == BUFFEMPTY){
UART_Handle->UART_SendComplete = true;
xSemaphoreGiveFromISR( UART_Handle->UARTWrite_Semaphore, &xHigherPriorityTaskWoken );// release semaphore
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );//forcing context exchange
}
//putting data into send FIFO
if(UART_Handle->WriteLength > FIFOMAX){
for( Cindex = 0 ; Cindex < FIFOMAX ;){
if(UARTCharPutNonBlocking(UART_Handle->UART_PORT, *(UART_Handle->pCurrentWrite))){//write autoly clear INT
(UART_Handle->pCurrentWrite) = (UART_Handle->pCurrentWrite) + 1;
(UART_Handle->WriteLength) = (UART_Handle->WriteLength) - 1;
Cindex = Cindex + 1;
UART_Handle->sentCount = UART_Handle->sentCount + 1;
}
}
}else{
templength = UART_Handle->WriteLength;
for( Cindex = 0; Cindex < templength ;){
if(UARTCharPutNonBlocking(UART_Handle->UART_PORT, *(UART_Handle->pCurrentWrite))){//write autoly clear INT
(UART_Handle->pCurrentWrite) = (UART_Handle->pCurrentWrite) + 1;
(UART_Handle->WriteLength) = (UART_Handle->WriteLength) - 1;
Cindex = Cindex + 1;
UART_Handle->sentCount = UART_Handle->sentCount + 1;
}
}
}
}
}
//*****************************************
//UART_write
//write certain length of data to UART port
//*****************************************
int32_t UART_write( UART_STATE *UART_Handle, uint8_t *pData, uint32_t length, uint32_t time_out ){
while(!UART_Handle->UART_SendComplete); //debugging purpose
UART_Handle->UART_SendComplete = false;//debugging purpose
UART_Handle->WriteLength = length;
if(UART_Handle->WriteLength <= UART_Handle->WriteBufSize){
UARTIntClear(UART_Handle->UART_PORT, UART_INT_TX); //clear INT flag
memcpy(UART_Handle->pWriteBuf,pData,UART_Handle->WriteLength); //copy data into writebuff
UART_Handle->pCurrentWrite = UART_Handle->pWriteBuf;
//putting data into send FIFO
if(UART_Handle->WriteLength > FIFOMAX){
// if
for( Cindex = 0 ; Cindex < FIFOMAX ;){
if(UARTCharPutNonBlocking(UART_Handle->UART_PORT, *(UART_Handle->pCurrentWrite))){//write autoly clear INT
(UART_Handle->pCurrentWrite) = (UART_Handle->pCurrentWrite) + 1;
(UART_Handle->WriteLength) = (UART_Handle->WriteLength) - 1;
Cindex = Cindex + 1;
UART_Handle->sentCount = UART_Handle->sentCount + 1;
}
}
}else{
for( Cindex = 0 ; Cindex < FIFOMAX ;){
if(UARTCharPutNonBlocking(UART_Handle->UART_PORT, *(UART_Handle->pCurrentWrite))){//write autoly clear INT
(UART_Handle->pCurrentWrite) = (UART_Handle->pCurrentWrite) + 1;
(UART_Handle->WriteLength) = (UART_Handle->WriteLength) - 1;
Cindex = Cindex + 1;
UART_Handle->sentCount = UART_Handle->sentCount + 1;
}
}
}
//start sending
UARTEnable(UART_Handle->UART_PORT);
if(UART_Handle->UARTWrite_Semaphore != NULL ) {
if(xSemaphoreTake(UART_Handle->UARTWrite_Semaphore, time_out/(portTICK_PERIOD_MS) ) == pdTRUE){
reValue = WRITESUCCESS; //wait return till write complete
}else{
reValue = WRITETIMEOUT; // timeout (ms)
}
}else{
while(1); //no Semaphore
}
return reValue;
}else{
return FAILURE; //wrong length
}
}
//*****************************************
//UART_read
//read certain length of data from UART port
//*****************************************
int32_t UART_read(UART_STATE *UART_Handle, uint8_t *pData, uint32_t length, uint32_t time_out){
//later added part
UARTDisable(UART_Handle->UART_PORT); //clearUART
UARTFIFOEnable(UART_Handle->UART_PORT);
//
UART_Handle->ReadLength = length; // set readlength
UART_Handle->ReadLengthcpy = length;
if(UART_Handle->ReadLength <= UART_Handle->ReadBufSize){
UARTIntClear(UART_Handle->UART_PORT, UART_INT_RX | UART_INT_RT); //clear INT flag
UART_Handle->dataput = pData; //store the destination buffer address
UART_Handle->pCurrentRead = UART_Handle->pReadBuf; //set current read
UARTEnable(UART_Handle->UART_PORT); //start receiving
//suspend before read ISR finish whole process
if(UART_Handle->UARTRead_Semaphore != NULL ) {
if(xSemaphoreTake(UART_Handle->UARTRead_Semaphore, time_out/(portTICK_PERIOD_MS) ) == pdTRUE){
reValue = READSUCCESS; //wait return till write complete
}else{
reValue = READTIMEOUT; // timeout (ms)
}
}else{
while(1); //no Semaphore
}
return reValue;
}else{
return FAILURE; //wrong length
}
}
//*****************************************
//UART_open
//open UART for certain port and bandrate
//*****************************************
UART_HANDLE UART_open(uint32_t UART_port, uint32_t UART_portperiph, uint32_t UART_baudrate){
//initialize structure
UART_Handle = (UART_HANDLE)malloc(sizeof(UART_STATE));
UART_Handle->ReadBufSize = UARTBUFFERSIZE;
UART_Handle->WriteBufSize = UARTBUFFERSIZE;
UART_Handle->UART_PORT = UART_port;
UART_Handle->UART_PORTPERIPH = UART_portperiph;
UART_Handle->UART_BRATE = UART_baudrate;
UART_Handle->pWriteBuf = (uint8_t*)malloc(UART_Handle->WriteBufSize * sizeof(uint8_t));
UART_Handle->pReadBuf = (uint8_t*)malloc(UART_Handle->ReadBufSize * sizeof(uint8_t));
UART_Handle->pCurrentWrite = UART_Handle->pWriteBuf;
UART_Handle->pCurrentRead = UART_Handle->pReadBuf;
UART_Handle->UARTWrite_Semaphore = NULL;
UART_Handle->UARTRead_Semaphore = NULL;
UART_Handle->UARTprotect_Semaphore = NULL;
UART_Handle->UART_SendComplete = true;
UART_Handle->sentCount = 0;//debugging purpose
vSemaphoreCreateBinary( UART_Handle->UARTWrite_Semaphore ); //semaphore create
vSemaphoreCreateBinary( UART_Handle->UARTRead_Semaphore ); //semaphore create
// vSemaphoreCreateBinary( UART_Handle->UARTprotect_Semaphore ); //debugging purpose
xSemaphoreTake( UART_Handle->UARTRead_Semaphore, portMAX_DELAY ); //semaphore take
xSemaphoreTake( UART_Handle->UARTWrite_Semaphore, portMAX_DELAY ); //semaphore take
// Enable Peripheral Clocks
MAP_PRCMPeripheralClkEnable(UART_Handle->UART_PORTPERIPH, PRCM_RUN_MODE_CLK);
// Configure PIN_55 for UART0 UART0_TX
MAP_PinTypeUART(PIN_55, PIN_MODE_3);
// Configure PIN_57 for UART0 UART0_RX
MAP_PinTypeUART(PIN_57, PIN_MODE_3);
// configuration, 8 bits length data width, 1 stop bit, no parity check
UARTConfigSetExpClk(UART_Handle->UART_PORT,PRCMPeripheralClockGet( UART_Handle->UART_PORTPERIPH),
UART_Handle->UART_BRATE, (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
UART_CONFIG_PAR_NONE));
// disable UART since function above contained UARTenable
UARTDisable(UART_Handle->UART_PORT);
UARTIntEnable(UART_Handle->UART_PORT, UART_INT_TX | UART_INT_RX | UART_INT_RT); // enable interrupt for send and receive and receive timeout
UARTIntRegister(UART_Handle->UART_PORT, UART_ISR); //hook ISR
UARTFIFOEnable(UART_Handle->UART_PORT); //enable FIFO for send and receive
UARTFIFOLevelSet(UART_Handle->UART_PORT, UART_FIFO_TX1_8, UART_FIFO_RX4_8); //Interrupt occur when 7 bytes send from FIFO or read in FIFO
return UART_Handle;
}
I am dealing with a multi-thread bug for long time. After many test, I aware that the bug most possibly related to the UART FIFO transmitting.
Here is my description of my program:
The program is based on Free-RTOS. It has two threads running in parallel.
one thread is reading from sensor by I2C driver. another is sending data from the reading task to the computer by UART driver. I have a multi-task frame to transfer data from reading task to sending task. I have a timer file to control real time measuring. My I2C driver and UART driver are all interrupt based.
Problem description:
My data reading is good only for 2 mins. after that, my data shift. When do testing, I blocked the I2C driver, only put constant data "1 2 3 4 5 6 7 8" into the I2C read buffer, and transfer them to sending task to do UART send. And after 2 mins, my data read out will become "8 1 2 3 4 5 6 7" and after the first change, later changes happen quickly, "7 8 1 2 3 4 5 6" and continue till the end of timing.
this is plot of one channel of my data, begin with 8 and changing
I set many test to my program already, and I am sure my multi-task frame (trans data from reading task to sending task) don't alter the data.
Important observations:
1.when I put read and send into one thread, the data is very good, without this bug.
2.I set counting in my UART driver, found that the amount of sending is correct, here the sending is putting bytes into the TX FIFO. However, in the Excel, I read by a JAVA UART program, data missed. And JAVA UART program should be ok, since it works well when I use the single thread testing.
3. no memory leak error pump up by CCS while debugging.
So I am thinking, under the multi-thread environment, something stopped my UART TX FIFO transmit after I put bytes into that. But I can't find out where is wrong in my codes, they all seems correct.
And another reasonable reason can be something altered my pointer in the interrupt, in linked picture the "pCurrentWrite" pointer. But that pointer only accessed by UART driver, and before the interrupt finished, the next UART write can't enter.
I include partial code in the link, where I want to show that the data is put into the TX FIFO, and the send count is correct.
I can't include all codes, I don't think anyone want to check those long codes. and without running it, it's hard to find out where is wrong.
So, I just want to describe the situation i met, and see if someone had similar bug before. And maybe someone know the problem of UART FIFO under multi-thread environment. If someone who can help do want to see the code, we can do further contact.
thanks (My new account can't include picture yet, picture linked below)
This code is part of my UART driver, in the sending interrupt. I use the PutCharNonBlocking to put byte, since it return 1 if success, return 0 if FIFO full, so in this way I make sure the program will send all my data into the FIFO, which turn out to be true, all data was put into the TX FIFO

Although you have included the use of semaphores, I would suggest further reading on mutual exclusion and shared resources.
The solution is overly complex and it would be pertinent to better abstract your code and simplify.
Note: you should not cast the result of malloc.
UART_Handle = (UART_HANDLE)malloc(sizeof(UART_STATE));

Related

There are some problems when I use C code to control TDC (Time to Digital converter, a hardware),the problem of the "main"

My problems
I want to use C code to realize a simple function: When I run the program, the program can control the TDC (Time to Digital converter) to start the histogram (a mode in TDC) running, then, run the histogram for the programmed time, and finally, it can stop and save the data in my computer automatically.
Unfortunately, the result is that it just saves a file full of zeros in each channel (I am sure that TDC had connected with signal perfectly because I have good data from the software the company provides).
The error I checked is that the argc and argv[] in main() function are not fit for the program and return error code, which the .dll file has set.
For hardware, are these parameters in the main() function like argc and argv[] in this code automatically valued? And if they are wrong, what can I do? (The hardware just only has some interfaces for signals and power).
The .dll file for TDC and its manual are provided.
Code
int main(Sword argc, Sbyte *argv[]) {
HRM_STATUS error;
Uword tcspc, delay, chann;
Uword period, clock;
Ulong moduleCount, offset, range, i;
HANDLE hdl[10];
FILE *fp = nullptr;
HRM_RefreshConnectedModuleList();
moduleCount = HRM_GetConnectedModuleCount();
/* If a module is present connect to the first module.
* If no module set error and reason in last error report.
* */
if (moduleCount)
HRM_GetConnectedModuleList(hdl);
else {
HRM_GetLastError(HRM_OPEN_USB);
error = HRM_ERROR;
}
/* Initialise variables and clear error report */
error = HRM_OK;
tcspc = 1; // 1 means Histogram Single-stop mode ;;;0 means MULTI-STOP mode。
delay = 20000; // the programmed time in ms。
chann = 0; // channel 0 = 0
// channel 1 = 0x80000
// channel 2 = 0x100000
// channel 3 = 0x180000
period = 2000; // clock_period
HRM_GetLastError(HRM_OK);
/* Set error if too few input parameters
* and set reason in last error.
* */
if (argc < 4) {
HRM_GetLastError(HRM_INV_PARAMETER);
error = HRM_ERROR;
}
/* If define frequency then read period in ns and ensure
* not greater than maximum allowable value of 2500.
* */
if (argc > 4)
period = (Uword) atoi(argv[4]);
if (period > 2500)
period = 2500;
/* Read from ~20ns before 1/2 cycle to ~20ns after.
* Form the offset in the memory where reading is to start.
* This is ~ 20ns before half period of clock. Then set the
* range for reading results of 40ns.
* */
clock = (Uword)(period / 2);
i = (Ulong) clock;
if (i >= 20)
i = (Ulong)(i - 20);
offset = (Ulong)(i * 1000 / 27);
range = (Ulong)(40 * 1000 / 27);
/* Form the programmable clock value. Upper byte and lower byte
* are set to 1/2 period in 5ns per bit - 1.
* */
clock = (Uword)(clock / 5);
if (clock)
clock--;
clock = (Uword)((clock << 8) | clock);
/* 1. Set the flag for TCSPC or MULTI-SCALAR mode
* 2. Read in the time in ms for running the histogram.
* 3. Form the 4 channel edge enables based on the input parameter.
* */
if (error == HRM_OK) {
tcspc = (Uword)(argv[1][0] & 1);
delay = (Sword) atoi(argv[3]);
if (argv[2][0] == '1')
chann |= 0x9000;
if (argv[2][1] == '1')
chann |= 0x0900;
if (argv[2][2] == '1')
chann |= 0x0090;
if (argv[2][3] == '1')
chann |= 0x0009;
}
/* Set the clock frequency. */
if (error == HRM_OK)
error = HRM_SetFrequencySelectionRegister(hdl[0], clock);
/* Set the channel edge enables. */
if (error == HRM_OK)
error = HRM_SetEdgeSensitivityRegister(hdl[0], chann);
/* Clear the memory. */
if (error == HRM_OK)
error = HRM_InitMemory(hdl[0], 0, 0x200000, 0);
/* Start the histogram running. */
if (error == HRM_OK) {
/* the third parameters 0=27ps, 1=54ps, 2=108ps */
error = HRM_StartHistogramFSM(hdl[0], tcspc, 0);
}
/* Run the histogram for the programmed time in ms. */
if (error == HRM_OK)
for (i = GetTickCount(); (Uword)(GetTickCount() - i) < delay;);
/* Stop the histogram process. */
if (error == HRM_OK)
error = HRM_SetModeBitsRegister(hdl[0], 0x0030);
/* Read all the memory into the buffer. */
if (error == HRM_OK)
error = HRM_ReadMemory(hdl[0], 0x0030, chann, 0x200000,
buffer); //byte数据类型的buffer
/* Open the file and set the headings. */
if (error == HRM_OK) {
fopen_s(&fp, "HISTOGRAM.txt", "w + t");
fprintf(fp, " Chan 2, Chan 3\n");
}
/* For each channel read all the time-bins from 20ns before to 20ns
* after the 1/2 clock cycle and save then in the file.
* */
for (i = 0; i != range && error == HRM_OK; i++) {
fprintf(fp, " % ld, % ld, % ld, % ld\n",
buffer[i + CH0_ADDR + offset], buffer[i + CH1_ADDR + offset],
buffer[i + CH2_ADDR + offset], buffer[i + CH3_ADDR + offset]);
}
/* Close the file and print error code if failed. */
if (fp)
fclose(fp);
if (error == HRM_OK)
printf("Done");
else {
error = HRM_GetLastError(HRM_OK);
printf("Failed: Error code = % d", error);
}
}
Something I note is that you have no return statements, even after error checking (which is great!). If those errors are severe enough as to interpret that the code shouldn't run when encountered, then you should return 1 (or any number you like - you could use a different return number for each error if you wish).
At the end of the main() function, if everything went alright, return 0.
As I said, I strongly suspect that there may be errors somewhere, which although being checked, they are not handled.
Also, that main is huuuge, try to split it up a bit making dedicated functions for each part, so that each function has its own responsibility, and try checking each part individually. Can you debug it? At least adding some prints? (Ideally, you should debug with a dedicated debugger, but as an emergency exit, prints are better than nothing!)

Establishing Serial Connection With RS232 using C

I am trying to establish a serial connection with a device via RS232 and the C code. The purpose is to connect arduino to computer later and show the ip adress retrieved from the device on the LED screen.
Usually I connect the device to the computer via RS232, open PuTTY and establish the serial connection at 115200 baud rate. Then I press enter, type login, press enter, type password, press enter, type 'ip show' and then retrieve the ip adress.
The problem is I am not good at C programming (studied it only for 1 year in University). The code I come up with (copy-pasted and edited) is attached below. The questions are:
1) How do I get the information printed on the terminal screen. For example, after I type login and then press enter, there is a sentence saying type your password. How do I retrieve that to IDE's console?
2) On the final step (retrieving the ip), how do I retrieve the ip? It is in text format, after it's shown I need to copy it and paste it into another document).
As for now, my limited amount of knowledge about C prohibits me to go further.
Any kind of help (even the name of the helpful function) is appreciated!
//
// serial.c / serial.cpp
// A simple serial port writing example
// Written by Ted Burke - last updated 13-2-2013
//
// To compile with MinGW:
//
// gcc -o serial.exe serial.c
//
// To compile with cl, the Microsoft compiler:
//
// cl serial.cpp
//
// To run:
//
// serial.exe
//
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
// Define the five bytes to send ("hello")
char bytes_to_send[15];
bytes_to_send[0] = '\n';
bytes_to_send[1] = 'a';
bytes_to_send[2] = 'd';
bytes_to_send[3] = 'm';
bytes_to_send[4] = 'i';
bytes_to_send[5] = 'n';
bytes_to_send[6] = '\n';
bytes_to_send[7] = 's';
bytes_to_send[8] = 'h';
bytes_to_send[9] = 'o';
bytes_to_send[10] = 'w';
bytes_to_send[11] = ' ';
bytes_to_send[12] = 'i';
bytes_to_send[13] = 'p';
bytes_to_send[14] = '\n';
// Declare variables and structures
HANDLE hSerial;
DCB dcbSerialParams = {0};
COMMTIMEOUTS timeouts = {0};
// Open the highest available serial port number
fprintf(stderr, "Opening serial port...");
hSerial = CreateFile(
"\\\\.\\COM6", GENERIC_READ|GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
if (hSerial == INVALID_HANDLE_VALUE)
{
fprintf(stderr, "Error\n");
return 1;
}
else fprintf(stderr, "OK\n");
// Set device parameters (38400 baud, 1 start bit,
// 1 stop bit, no parity)
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
if (GetCommState(hSerial, &dcbSerialParams) == 0)
{
fprintf(stderr, "Error getting device state\n");
CloseHandle(hSerial);
return 1;
}
dcbSerialParams.BaudRate = CBR_115200;
dcbSerialParams.ByteSize = 8;
dcbSerialParams.StopBits = ONESTOPBIT;
dcbSerialParams.Parity = NOPARITY;
if(SetCommState(hSerial, &dcbSerialParams) == 0)
{
fprintf(stderr, "Error setting device parameters\n");
CloseHandle(hSerial);
return 1;
}
// Set COM port timeout settings
timeouts.ReadIntervalTimeout = 50;
timeouts.ReadTotalTimeoutConstant = 50;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 50;
timeouts.WriteTotalTimeoutMultiplier = 10;
if(SetCommTimeouts(hSerial, &timeouts) == 0)
{
fprintf(stderr, "Error setting timeouts\n");
CloseHandle(hSerial);
return 1;
}
// Send specified text (remaining command line arguments)
DWORD bytes_written, total_bytes_written = 0;
fprintf(stderr, "Sending bytes...");
if(!WriteFile(hSerial, bytes_to_send, sizeof(bytes_to_send), &bytes_written, NULL))
{
fprintf(stderr, "Error\n");
CloseHandle(hSerial);
return 1;
}
fprintf(stderr, "%d bytes written\n", bytes_written);
// Close serial port
fprintf(stderr, "Closing serial port...");
if (CloseHandle(hSerial) == 0)
{
fprintf(stderr, "Error\n");
return 1;
}
fprintf(stderr, "OK\n");
fprintf(stderr, "the sent sentence is: ");
for(int i=0;i<sizeof(bytes_to_send);i++){
fprintf(stderr,"%c",bytes_to_send[i]);
}
// exit normally
return 0;
}
For setting up the port and read/write commands you can use the code from this answer.
Then you only have to worry about what you send and what you receive.
Putty actually reads the serial port in the background and then prints it on the console so you can see it. You will have to do the same thing in your app. After you read the data, you have to print it.
For example, for login, as you described it:
write (fd, "login\r\n", 7); // send 7 character login command
// (similar to writing login + enter in Putty)
usleep ((7 + 25) * 100); // allow time for sending & receiving
// (sleep enough to transmit the 7 plus
// receive 25: approx 100 uS per char transmit)
char buf [100];
int n = read (fd, buf, sizeof(buf)); // read rx characters (Putty does this automatically)
// expect to have "the sentence saying type your password" in this buffer
// 100 characters are read, adjust it if you expect a longer answer
printf("Rx:"); // display the rx data (Putty does this automatically)
for (int i = 0; i < sizeof(buf); i++)
printf("%c",buf[i]);
printf("\n");
// continue with sending the password + reading the response
// then sending the ip command + reading the answer

ENC28J60 Stops receiving

I'm currently using an stm32f405 and an ENC28J60 and lwip as tcp/ip stack. Everything runs fine at startup but after about a minute or so the ENC stops receiving packets. Transmitting keeps working fine. I've tried both polling it and using interrupts.
I'm using https://github.com/wolfgangr/enc28j60 to communicate to the ENC. And this is the code that handles incoming packets:
while (true) {
eventmask_t mask = chEvtWaitAnyTimeout(ALL_EVENTS, LWIP_PACKET_POLL_INTERVAL);
if(mask & ENC_INTERRUPT_ID)
{
/* Handle ENC28J60 interrupt */
ENC_IRQHandler(&encHandle);
/* Reenable interrupts */
ENC_EnableInterrupts(EIE_INTIE);
}
if (mask & PERIODIC_LINK_TIMER_ID)
{
bool current_link_status = ((encHandle.LinkStatus) & PHSTAT2_LSTAT) != 0;
if (current_link_status != prev_link_status) {
if (current_link_status) {
dhcp_start(&thisif);
}
else {
dhcp_stop(&thisif);
}
}
prev_link_status = current_link_status;
}
/* Check if new frames where received */
struct pbuf *p;
while ((p = low_level_input(&thisif)) != NULL) {
struct eth_hdr *ethhdr = p->payload;
switch (htons(ethhdr->type)) {
/* IP or ARP packet? */
case ETHTYPE_IP:
case ETHTYPE_ARP:
/* full packet send to tcpip_thread to process */
if (tcpip_input(p, &thisif) == ERR_OK)
break;
LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
default:
pbuf_free(p);
}
}
}
Function low_level_input:
static struct pbuf *low_level_input(struct netif *netif) {
struct pbuf *p = NULL;
struct pbuf *q;
uint16_t len;
uint8_t *buffer;
uint32_t bufferoffset = 0;
if (!ENC_GetReceivedFrame(&encHandle)) {
return NULL;
}
/* Obtain the size of the packet and put it into the "len" variable. */
len = encHandle.RxFrameInfos.length;
buffer = (uint8_t *)encHandle.RxFrameInfos.buffer;
if (len > 0)
{
/* We allocate a pbuf chain of pbufs from the Lwip buffer pool */
p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
}
if (p != NULL)
{
bufferoffset = 0;
for(q = p; q != NULL; q = q->next)
{
/* Copy data in pbuf */
memcpy( (uint8_t*)((uint8_t*)q->payload), (uint8_t*)((uint8_t*)buffer + bufferoffset), q->len);
bufferoffset = bufferoffset + q->len;
}
}
return p;
}
After a while the function ENC_GetReceivedFrame keeps returning false, even if I know for sure some packets should have been received.
I've debugged the function (found in enc28j60.c) and this line:
pktcnt = enc_rdbreg(handle, ENC_EPKTCNT);
pktcnt is always 0. I've looked at the SPI bus with a logic analyzer and the ENC truly anwsers 0. The SPI bus works fine.
Just before this happens some packets are received that are not flagged as RXSTAT_OK (look at line 1259 in enc28j60.c)
I've been at this for day's now, and truly have no ideas left.
I encountered a similar problem..
The EPKTCNT register was times to times decreased with no reason( without setting the ECON2_PKTDEC bit).
I noticed that when it happened it was after setting the ECON2_AUTOINC bit.
Not every time ECON2_AUTOINC was set but often.
I just set ECON2_AUTOINC at the initialization of the ENC28J60, no more during the reading process.
Since EPKTCNT stopped to decrease with no reason.
Hope it can help

C - send multiple messages over a single socket - Data not transferred until I close the socket

I am attempting to have a TCP communication from my laptop to a SoC board where I send one message to initiate a process and It sends me a series of status messages until it has completed the process. This means that I would call send() multiple times over the socket from the SoC. The problem is that my laptop does not receive any status message unless the socket connection is closed by the SoC. As soon as the socket closes, I get a burst of all the data that the SoC sent. Is there a way to make this communication realtime so that I get the status messages immediately as it sends?
Code on the SoC that sends the data: This function is called multiple times:
INT16 upload(INT16* socket, INT8 HOB, INT8 LOB, INT8 msg_type, INT8* data) {
INT8* Tx_Data= NULL;
INT8 size = 0;
INT16 num= 0;
int temp = 0;
size = 256*HOB + LOB + NAME_SIZE + 3;
Tx_Data = (INT8*) malloc(size*sizeof(INT8));
for (num=0;num<NAME_SIZE ;num++) {
Tx_Data[num] = NAME[num];
}
Tx_Data[num++] = HOB;
Tx_Data[num++] = LOB;
Tx_Data[num++] = msg_type; // 11 byte header ends here
for(temp = 0;num<size;num++,temp++) {
Tx_Data[num] = data[temp]; // data
}
send(*socket, (INT8*)Tx_Data,num,0);
return PASS;
}
Code on my Laptop:
for(;;) {
printf("Start of for loop socket id: %d \n",socket_id);
RX_Data = (unsigned char*)malloc(2048);
unsigned char Command;
int No_of_Data_Bytes=0;
printf("Before read.... \n");
nbytes = read(socket_id,RX_Data,11); // get 11 bytes from EZ80
printf("Received %d bytes from socket\n",nbytes);
No_of_Data_Bytes = RX_Data[8]*256 + RX_Data[9];
Command = RX_Data[10];
printf("\n command:%c %d \n",Command,No_of_Data_Bytes);
if(Command=='I' || Command=='E'|| Command=='V') {
//read and analyse data
}
else {
break;
}
Command = '\0';
free(RX_Data);
}
In Linux you could just use fdopen, write and flush the stream. Not sure if it's applicable in your case however. Example:
FILE *f = fdopen(socketdescriptor, "w+");
. . .
n = write(socketdescriptor, "this is a message", 17);
fflush(f);
...
The reason for this behaviour is Nagel algorithm that is enabled on a socket by default. You can disable it via setsockopt() call.
You can use fflush() or just add '\n' character to the end of string being writen into the socket, but this will flush only stdio buffers in userland, which is not what you aim at.

Interacting with a serial port using the C functions fopen, fread, and fwrite?

I have been attempting to communicate with a device of mine via an RS232 serial port(in my case it is COM6). My code is supposed to write a string of ascii values to the device and then read the response, however I cannot seem to get any response. The program seems to work relatively fine when I have it write and read to a file in my computer, but not for when I designate COM6. Here is the latest edition of my code:
using namespace std;
const char ASCII[ ]= "0123456789ABCDEF";
char *Checksum (char *buffer)
{
static char Hex[10];
static int a1, a2;
register unsigned int i;
int sum;
printf("%s \n", buffer);
sum = 256;
for ( i=0 ; i<strlen(buffer) ; i++ )
{
sum-=buffer[i];
if ( sum<0 )
sum+= 256;
}
a1 = (sum & 0xF0) >> 4;
a2 = sum & 0x0F;
Hex[0] = ASCII[a1];
Hex[1] = ASCII[a2];
Hex[2] = 0;
printf("the checksum is %s \n",Hex);
return(Hex);
}
int main()
{
char data[80], input[80], *data2;
char *response;
DCB dcb;
bool retVal;
DWORD dwBytesTransferred;
DWORD byteswritten;
printf("the variable response is initially: %d\n", response);
dcb.BaudRate = CBR_19200; //19200 Baud
dcb.ByteSize = 8; //8 data bits
dcb.Parity = NOPARITY; //no parity
dcb.StopBits = ONESTOPBIT; //1 stop
//New open port area
HANDLE hPort;
if ((hPort = CreateFile ( "\\\\.\\COM6",
GENERIC_READ | GENERIC_WRITE,
0, // exclusive access
NULL, // no security attrs
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL)) != INVALID_HANDLE_VALUE)
{
printf("SUCCESS opening the port\n");// success
}
//GetCommState
DCB Dcb;
GetCommState (hPort, &Dcb);
Dcb.BaudRate = CBR_19200;
Dcb.StopBits = ONESTOPBIT;
Dcb.ByteSize = 8;
Dcb.Parity = NOPARITY;
Dcb.fParity = 0;
Dcb.fOutxCtsFlow = 0;
Dcb.fOutxDsrFlow = 0;
Dcb.fDsrSensitivity = 0;
Dcb.fTXContinueOnXoff = TRUE;
Dcb.fOutX = 0;
Dcb.fInX = 0;
Dcb.fNull = 0;
Dcb.fErrorChar = 0;
Dcb.fAbortOnError = 0;
Dcb.fRtsControl = RTS_CONTROL_DISABLE;
Dcb.fDtrControl = DTR_CONTROL_DISABLE;
//Flushing
FlushFileBuffers( hPort );
PurgeComm (hPort, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
COMSTAT comStat;
DWORD dwErrorFlags;
ClearCommError ( hPort, &dwErrorFlags, &comStat );
//NEW commtimeouts area
COMMTIMEOUTS CommTimeOuts;
DWORD dwTimeout = 3000; // <- set timeout in milliseconds
if(!dwTimeout)
{ // Don't use timeout -> Read the bytes already in input buffer and return immediately
CommTimeOuts.ReadIntervalTimeout = MAXDWORD;
CommTimeOuts.ReadTotalTimeoutConstant = 0;
} else
{ // Use given timeout, wait until the requested number of bytes are read - or timeout
CommTimeOuts.ReadIntervalTimeout = 0;
CommTimeOuts.ReadTotalTimeoutConstant = dwTimeout;
}
CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
CommTimeOuts.WriteTotalTimeoutConstant = 0;
SetCommTimeouts (hPort, &CommTimeOuts);
printf("insert ASCII code string you wish to send:");
scanf("%s", input);
strcpy(data, "{0x02}");
strcat(data, input);
printf("%s \n", data);
data2=Checksum(data);
strcat(data, data2);
strcat(data, "{0x03}");
printf("the final sent message will be: %s \n",data);
retVal = WriteFile(hPort,data, strlen(data), &byteswritten, NULL);
printf("Number of bytes written: %d\n", byteswritten);
printf("Write Success? %d\n", retVal);
retVal=ReadFile (hPort, &response, 20, &dwBytesTransferred, NULL);
printf("Read Success? %d\n", retVal);
printf("Port Response: %d\n", response);
free(response);
return 0;
}
Summary of latest discoveries: Using the Free Serial Port Monitor that Habi suggested I now know for sure that WriteFile is functioning correctly and COM6 is in receiving the message. I'm still looking for a crossover cable to double check that the message is being transferred across the line. I figure while I try to figure that out if someone could look at this new edition and tell me if there is anything wrong, particularly in relation to the ReadFile function, it would be much appreciated. It bothers me that the Free Serial Port software is only showing the data passed from my computer and not a response from the device at all. =\
Instead of
"COM6"
try
"\\\\.\\COM6"
And I would recommend to use CreateFile(), ReadFile(), WriteFile().
To open the COM port try this:
HANDLE hComDev;
if ((hComDev = CreateFile ( "\\\\.\\COM6",
GENERIC_READ | GENERIC_WRITE,
0, // exclusive access
NULL, // no security attrs
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL)) != INVALID_HANDLE_VALUE)
{
// success
}
It seems that calling GetCommState() is missing in your code. Try this to configure the COM port:
DCB Dcb;
GetCommState (hComDev, &Dcb);
Dcb.BaudRate = CBR_19200;
Dcb.StopBits = ONESTOPBIT;
Dcb.ByteSize = 8;
Dcb.Parity = NOPARITY;
Dcb.fParity = 0;
Dcb.fOutxCtsFlow = 0;
Dcb.fOutxDsrFlow = 0;
Dcb.fDsrSensitivity = 0;
Dcb.fTXContinueOnXoff = TRUE;
Dcb.fOutX = 0;
Dcb.fInX = 0;
Dcb.fNull = 0;
Dcb.fErrorChar = 0;
Dcb.fAbortOnError = 0;
Dcb.fRtsControl = RTS_CONTROL_DISABLE;
Dcb.fDtrControl = DTR_CONTROL_DISABLE;
And to initially clear the COM port, I would do a reset like this before starting to send and receive bytes:
FlushFileBuffers( hComDev );
PurgeComm (hComDev, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
COMSTAT comStat;
DWORD dwErrorFlags;
ClearCommError ( hComDev, &dwErrorFlags, &comStat );
You ask for timeouts? To configure time outs try this:
COMMTIMEOUTS CommTimeOuts;
DWORD dwTimeout = .... // <- set timeout in milliseconds
if(!dwTimeout)
{ // Don't use timeout -> Read the bytes already in input buffer and return immediately
CommTimeOuts.ReadIntervalTimeout = MAXDWORD;
CommTimeOuts.ReadTotalTimeoutConstant = 0;
}
else
{ // Use given timeout, wait until the requested number of bytes are read - or timeout
CommTimeOuts.ReadIntervalTimeout = 0;
CommTimeOuts.ReadTotalTimeoutConstant = dwTimeout;
}
CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
CommTimeOuts.WriteTotalTimeoutConstant = 0;
SetCommTimeouts (hComDev, &CommTimeOuts);
My code is supposed to write a string of ascii values to the device
and then read the response, however I cannot seem to get any response.
Are you sure that the bytes are really sent to the device? If you have the option, take a oscilloscope and monitor the PC's Tx line. Send some bytes and check baudrate and start/stop bits. If you have no hardware to monitor this signal, take a software based serial monitor, e.g. Free Serial Port Monitor. I have no experience with that kind of software tools but they should show you at least that the Windows drivers try to send something via the COM port of your choice.
Greetings
Habi
If you have been trying for a month then I would recommend you look at the windows function CreateFile. I have used this for serial port comms and it works fine. You might also want to look at the DCB struct and GetCommState and SetCommState functions to configure the serial port. WriteFile can be used to write data to the serial port. I cannot post all of my code as it owned by my company, but this should help you get started.
Also ClearCommError may be of help to you to query if any data is on the serial port buffer when you are waiting to read from it.
I encountered a similar problem and found the other answers helpful, but I was not receiving characters.
Ultimately I found the problem was with RTS_CONTROL_DISABLE or DTR_CONTROL_DISABLE.
By having RTS and DTR held low, the program was indicating to the modem/device that the program was not ready to receive data, and the modem was respecting this signal and dutifully not sending.
By changing these to RTS_CONTROL_ENABLE and DTR_CONTROL_ENABLE, the program indicates to the modem that data may be sent, and I began receiving characters. Not all devices respect these signals so it may work in some configurations with DTR and RTS disabled -- the modem may send data anyway.
It's possible, but you need to config com port via cmd mode command,
and there is no way to config timeout value via cmd command.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv)
{
// use mode command to config port
// mode com2 dtr=off rts=off baud=9600 parity=N data=8 stop=1 to=on
FILE *f = fopen("\\\\.\\COM2", "wb+");
setvbuf(f, NULL, _IONBF, 0);
if(f == NULL) return -1;
while(1)
{
static char p[5];
int cnt = fread(&p, 1, sizeof(p), f);
printf("read[%d]:%s\n", cnt, p);
if(p[0] == '0') break;
memset(p, 0, sizeof(p));
}
fclose(f);
return 0;
}

Resources