Convert nested if into switch - if-statement

I would love to convert these nested if statement into a switch,
i am using finite state machine within these (switch cases) and each case has its own timing.
(working on avr related project.
original switch
for(i=1;i<17;i++)
{
Print_On_LCD_Pos(i,2," Humidity: 67%"); // just example
Print_On_LCD_Pos((i-1),2," ");
_delay_ms(600);
if(i==16)
{
for(j=16;j>0;j--)
{
Print_On_LCD_Pos(j,2,"Humidity:67%");
Print_On_LCD_Pos((j+13),2," ");
_delay_ms(200);
if(j==1)
{
Print_On_LCD_Pos(10,2," ");
_delay_ms(400);
Print_On_LCD_Pos(10,2,"67%");
_delay_ms(400);
Print_On_LCD_Pos(10,2," ");
_delay_ms(400);
Print_On_LCD_Pos(10,2,"67%");
_delay_ms(400);
}
}
}
}
I was having a bit confusion as result as to how to keep in track of incremental/decremental variables without cases sharing data across. The dilemma here is that as you know for finite state machine they run in sequences so while case 1 run it might share some some data with another case unless I put some limitations ofcourses but these is going a lot like unnecessary long code.
Anyone to share a neat way of doing it?

How about something like this:
static uint16_t state = 0;
state++;
if ( state < 17 ) {
const uint16_t i = state;
Print_On_LCD_Pos(i,2," Humidity: 67%"); // just example
Print_On_LCD_Pos((i-1),2," ");
_delay_ms(600);
} else
if ( state < 32 ) {
const uint16_t j = 16 - (32 - state);
Print_On_LCD_Pos(j,2,"Humidity:67%");
Print_On_LCD_Pos((j+13),2," ");
_delay_ms(200);
} else
if ( state == 32 ) {
Print_On_LCD_Pos(10,2," ");
_delay_ms(400);
Print_On_LCD_Pos(10,2,"67%");
_delay_ms(400);
Print_On_LCD_Pos(10,2," ");
_delay_ms(400);
Print_On_LCD_Pos(10,2,"67%");
_delay_ms(400);
} else {
state = 0;
}
Expanding on that, maybe you could/should replace the _delay_ms(...) calls with more state transitions. Since all your delays are multiples of 200ms you could execute your state machine every 200ms, decrement some wait state counter, and immediately return if it's not 0. This frees up a lot of CPU resources and is actually the most common reason to build a state machine in the first place, besides C's absent features for light-weight concurrent programming.

Related

Serial packets causing ESP32 to randomly halt and trigger watchdog timer reboot

I'm using the ESP32-Arduino to continuously receive serial data (2400 baud on Serial1) in an RTOS task. The task tests for a certain byte sequence using a state machine and does packet validation before setting a valid packet flag.
Occasionally, when I have too much data coming in, the system will halt and trigger a watchdog reboot.
I don't have a debugger on me so I can only rely on checking the serial monitor (Serial) and put print statements.
I thought that perhaps the buffer was full and I didn't get enough time to service is but I'm not sure.
I've tried to reduce the code in the RTOS task and let the post processing be done in the main loop. I guess I could trip the task even more but I haven't tried further.
I've also tried to both increase or decrease the frequency of the task and doesn't seem to make a difference.
Here's my code:
void readSerial(void *pvParameters)
{
for (;;)
{
// Serial.print("chars avail: ");
while (Serial1.available())
{
char ch = Serial1.read();
switch (nextSerialRecieveState)
{
case IDLE:
case HEADER_0:
default:
rxByteIndex = 0;
checkSum = 0;
if (ch == 0x5A)
{
nextSerialRecieveState = HEADER_1;
}
else
{
nextSerialRecieveState = IDLE;
}
break;
case HEADER_1:
if (ch == 0x54)
{
nextSerialRecieveState = PACKET_LENGTH;
}
else
{
nextSerialRecieveState = IDLE;
}
break;
case PACKET_LENGTH:
case CHECKSUM_UPPER:
checkSumUpperByte = ch;
nextSerialRecieveState = CHECKSUM_LOWER;
break;
case CHECKSUM_LOWER:
checkSumLowerByte = ch;
if ((((checkSumUpperByte << 8) + checkSumLowerByte) == checkSum))
{
serialPrintBuffer();
Serial.print("VALID PACKET FROM ");
Serial.print(SOURCE_BYTE_0, HEX);
Serial.print(":");
Serial.print(SOURCE_BYTE_1, HEX);
Serial.print(":");
Serial.println(SOURCE_BYTE_2, HEX);
validPacketFlag = 1;
}
}
nextSerialRecieveState = IDLE;
break;
}
//lastByteReceivedTime = millis();
}
delay(10);
}
}
not why is there's some fundamental misunderstanding I have about this task.
I thought that perhaps the buffer was full and I didn't get enough
time to service is but I'm not sure.
So what happens when the input buffer is (nearly) full? Is there a protocol to signal the remote transmitter not to transmit? If not, the buffer may overrun and then the message framing can get messed up, which can confuse the logic of your code.
No debugger: at the very least, check for buffer overrun within the code itself, and make it known. If there is, the code is useless anyway.
I've tried to reduce the code in the RTOS task and let the post
processing be done in the main loop. I guess I could trip the task
even more but I haven't tried further.
Complete that task. As a general rule, do as little as possible within the RTOS.
I've also tried to both increase or decrease the frequency of the task
and doesn't seem to make a difference.
That won't affect the rate the data is arriving. First things first: establish if there is buffer overrun.

Improving performance for a TM simulator

I am trying to simulate a lot of 2 state, 3 symbol (One direction tape) Turing machines. Each simulation will have different input, and will run for a fixed number of steps. The current bottleneck in the program seems to be the simulator, taking a ton of memory on Turing machines which do not halt.
The task is to simulate about 650000 TMs, each with about 200 non-blank inputs. The largest number of steps I am trying is 1 billion (10**9).
Below is the code I am running. vector<vector<int> > TM is a transition table.
vector<int> fast_simulate(vector<vector<int> > TM, string TM_input, int steps) {
/* Return the state reached after supplied steps */
vector<int> tape = itotape(TM_input);
int head = 0;
int current_state = 0;
int halt_state = 2;
for(int i = 0; i < steps; i++){
// Read from tape
if(head >= tape.size()) {
tape.push_back(2);
}
int cell = tape[head];
int data = TM[current_state][cell]; // get transition for this state/input
int move = data % 2;
int write = (data % 10) % 3;
current_state = data / 10;
if(current_state == halt_state) {
// This highlights the last place that is written to in the tape
tape[head] = 4;
vector<int> res = shorten_tape(tape);
res.push_back(i+1);
return res;
}
// Write to tape
tape[head] = write;
// move head
if(move == 0) {
if(head != 0) {
head--;
}
} else {
head++;
}
}
vector<int> res {-1};
return res;
}
vector<int> itotape(string TM_input) {
vector<int> tape;
for(char &c : TM_input) {
tape.push_back(c - '0');
}
return tape;
}
vector<int> shorten_tape(vector<int> tape) {
/* Shorten the tape by removing unnecessary 2's (blanks) from the end of it.
*/
int i = tape.size()-1;
for(; i >= 0; --i) {
if(tape[i] != 2) {
tape.resize(i+1);
return tape;
}
}
return tape;
}
Is there anywhere I can make improvements in terms of performance or memory usage? Even a 2% decrease would make a noticeable difference.
Make sure no allocations happen during the whole TM simulation.
Preallocate a single global array at program startup, which is big enough for any state of the tape (e.g. 10^8 elements). Put the machine at the beginning of this tape array initially. Maintain the segment [0; R] of the all cells which were visited by the current machine simulation: this allows you to avoid clearing the whole tape array when you start the new simulation.
Use the smallest integer type for tape elements which is enough (e.g. use unsigned char if the alphabet surely has less than 256 characters). Perhaps you can even switch to bitsets if alphabet is very small. This reduces memory footprint and improves cache/RAM performance.
Avoid using generic integer divisions in the innermost loop (they are slow), use only divisions by powers-of-two (they turn into bit shifts). As the final optimization, you may try to remove all branches from the innermost loop (there are various clever techniques for this).
Here is another answer with more algorithmic approaches.
Simulation by blocks
Since you have tiny alphabet and tiny number of states, you can accelerate the simulation by processing chunks of the tape at once. This is related to the well-known speedup theorem, although I suggest a slightly different method.
Divide the tape into blocks of 8 characters each. Each such block can be represented with 16-bit number (2 bits per character). Now imagine that the machine is located either at the first or at the last character of a block. Then its subsequent behavior depends only on its initial state and the initial value on the block, until the TM moves out of the block (either to the left or to the right). We can precompute the outcome for all (block value + state + end) combinations, or maybe lazily compute them during simulation.
This method can simulate about 8 steps at once, although if you are unlucky it can do only one step per iteration (moving back and forth around block boundary). Here is the code sample:
//R = table[s][e][V] --- outcome for TM which:
// starts in state s
// runs on a tape block with initial contents V
// starts on the (e = 0: leftmost, e = 1: rightmost) char of the block
//The value R is a bitmask encoding:
// 0..15 bits: the new value of the block
// 16..17 bits: the new state
// 18 bit: TM moved to the (0: left, 1: right) of the block
// ??encode number of steps taken??
uint32_t table[2][2][1<<16];
//contents of the tape (grouped in 8-character blocks)
uint16_t tape[...];
int pos = 0; //index of current block
int end = 0; //TM is currently located at (0: start, 1: end) of the block
int state = 0; //current state
while (state != 2) {
//take the outcome of simulation on the current block
uint32_t res = table[state][end][tape[pos]];
//decode it into parts
uint16_t newValue = res & 0xFFFFU;
int newState = (res >> 16) & 3U;
int move = (res >> 18);
//write new contents to the tape
tape[pos] = newValue;
//switch to the new state
state = newState;
//move to the neighboring block
pos += (2*move-1);
end = !move;
//avoid getting out of tape on the left
if (pos < 0)
pos = 0, move = 0;
}
Halting problem
The comment says that TM simulation is expected either to finish very early, or to run all the steps up to the predefined huge limit. Since you are going to simulate many Turing machines, it might be worth investing some time in solving the halting problem.
The first type of hanging which can be detected is: when machine stays at the same place without moving far away from it. Let's maintain surrounding of TM during simulation, which is the values of segment of characters at distance < 16 from TM's current location. If you have 3 characters, you can encode surrounding in a 62-bit number.
Maintain a hash table for each position of TM (as we'll see later, only 31 tables are necessary). After each step, store tuple (state, surrounding) in the hash table of current position. Now the important part: after each move, clear all hash tables at distance >= 16 from TM (actually, only one such hash table has to be cleared). Before each step, check if (state, surrounding) is already present in the hash table. If it is, then the machine is in infinite loop.
You can also detect another type of hanging: when machine moves to the right infinitely, but never returns back. In order to achieve that, you can use the same hashtables. If TM is located at the currently last character of the tape with index p, check current tuple (state, surrounding) not only in the p-th hashtable, but also in the (p-1)-th, (p-2)-th, ..., (p-15)-th hash tables. If you find a match, then TM is in infinite loop moving to the right.
Change
int move = data % 2;
To
int move = data & 1;
One is a divide, the other is a bitmask, both should give 0 or 1 base on the low bit. You can do this anytime you have % by a power of two.
You're also setting
cell = tape[head];
data = TM[current_state][cell];
int move = data % 2;
int write = (data % 10) % 3;
current_state = data / 10;
Every single step, regardless of whether tape[head] has changed and even on branches where you're not accessing those values at all. Take a careful look at which branches use which data, and only update things just as they're needed. See straight after that you write:
if(current_state == halt_state) {
// This highlights the last place that is written to in the tape
tape[head] = 4;
vector<int> res = shorten_tape(tape);
res.push_back(i+1);
return res;
}
^ This code doesn't reference "move" or "write", so you can put the calculation for "move"/"write" after it and only calculate them if current_state != halt_state
Also the true-branch of an if statement is the optimized branch. By checking for not the halt state, and putting the halt condition in the else branch you can improve the CPU branch prediction a little.

For loop with if statement for ardiuno powered humidity control

Really simple question but I'm not entirely sure how to incorporate a for loop in the if statement I have. Context: I have a humidifier I am trying to automate based on the humidity of the room. I'm using an ardiuno, dht11 humidity sensor and a servo. The humidifier knob has three settings (high low off) and so the servo has three positions. I have the code running so the servo turns appropriately according to the humidity level. The issue is that it fluctuates very easily. To correct that I'm looking to incorporate a for loop so that after let say 60 one second iterations of the humidity being greater than 55 the servo moves. I tried to add a for loop but it doesn't seem to be working.
But this is only my solution based on the little programming I know. If there is a better solution or even an equally viable alternative I'd love to know. I'm currently studying mechanical engineering but I'm finding that to really make something one needs a background in electronics and code. I'm trying to learn both independently through a series of projects and so I'm quite eager to learn. Hopefully this helps explain why I'm asking such a simple questions to begin with.
#include <dht.h>
#include <Servo.h>
Servo myservo;//create servo object to control a servo
dht DHT;
#define DHT11_PIN 7 // pin for humidity sensor ( also measure temp)
void setup() {
myservo.attach(9);//attachs the servo on pin 9 to servo object
myservo.write(0);//statting off position at 0 degrees
delay(1000);//wait for a second
Serial.begin(9600);
}
void loop() {
int chk = DHT.read11(DHT11_PIN); // the follow is just so that I can see the readings come out properly
Serial.print("Temperature = ");
Serial.println(DHT.temperature);
Serial.print("Humidity = ");
Serial.println(DHT.humidity);
delay(500);
if (DHT.humidity > 55) // here is where my code really begins
{
for (int i=0; i>60; i++); // my goal is to execute the follow code after the statement above has been true for 60 one second iterations
{
myservo.write(0);//goes to off position
delay(1000);//wait for a second
}
} else if (DHT.humidity > 40 ) {
for (int i=0; i>60; i++); // same thing here
myservo.write(90);//goes to low position
delay(1000);//wait for a second
}
else
{
for (int i=0; i>60; i++);
myservo.write(180);//goes to high position
delay(1000);
}
} // end of void loop()
Just addressing your question, the following line is incorrect:
for (int i=0; i>60; i++);
Two things:
1) The second statement in the for loop describes the conditions on which it executes. The way it is written, it will only execute when i>60 (not what you want according to the comments).
2) The semicolon after the for statement makes the next block unassociated.
Correct that line to the following:
for (int i=0; i<60; i++)
See the following for more information:
https://www.tutorialspoint.com/cprogramming/c_for_loop.htm
It would probably be helpful to examine your compiler warnings, and/or set a higher warning level, to catch these type of things early (this is, of course, somewhat compiler dependent).
I guess you trying kind of de-bouncing at you need humid level stay in same range for some period.
First, I define conversion function to map humid level to state
#define HUMID_OFF 1
#define HUMID_LOW 2
#define HUMID_HIGH 3
byte state_conv (float humid_level){
if (humid_level > 55) return HUMID_OFF ;
else if (humid_level > 40 ) return HUMID_LOW ;
else return HUMID_HIGH ;
}
Second I will check changing of state and use millis() to count time while current state is steady. if counting time are longer than threshold then change the actual state.
/*Global variable*/
byte actual_state;
byte flag_state;
void setup (){
// Do things that necessary
float humid = dht.readHumidity();
/*Initialize value*/
actual_state = state_conv(humid);
flag_state= state_conv(humid);
}
void loop(){
static unsigned long timer = millis();
float humid = dht.readHumidity();
byte crr_state = state_conv(humid);
if (crr_state != actual_state ){// if state is changing
if (flag_state != crr_state){
/if crr_state change form last iteration then reset timer
flag_state = crr_state;/
timer = millis();
}
else if (millis() - timer > 10000){
//if crr_state not change for 10000 ms (10 second)
actual_state = crr_state; // update actual state to crr_state
}
}
// After this use actual_state to control servo
if (actual_state == HUMID_OFF ){
myservo.write(0);//goes to off position
}
else if (actual_state == HUMID_LOW ){
myservo.write(90);//goes to low position
}
else if (actual_state == HUMID_HIGH ){
myservo.write(180);//goes to high position
}
}
DHT.humidity returns a float, so if you want to compare, then first store this in an int, and then compare.

DMA write to SD card (SSP) doesn't write bytes

I'm currently working on replacing a blocking busy-wait implementation of an SD card driver over SSP with a non-blocking DMA implementation. However, there are no bytes actually written, even though everything seems to go according to plan (no error conditions are ever found).
First some code (C++):
(Disclaimer: I'm still a beginner in embedded programming so code is probably subpar)
namespace SD {
bool initialize() {
//Setup SSP and detect SD card
//... (removed since not relevant for question)
//Setup DMA
LPC_SC->PCONP |= (1UL << 29);
LPC_GPDMA->Config = 0x01;
//Enable DMA interrupts
NVIC_EnableIRQ(DMA_IRQn);
NVIC_SetPriority(DMA_IRQn, 4);
//enable SSP interrupts
NVIC_EnableIRQ(SSP2_IRQn);
NVIC_SetPriority(SSP2_IRQn, 4);
}
bool write (size_t block, uint8_t const * data, size_t blocks) {
//TODO: support more than one block
ASSERT(blocks == 1);
printf("Request sd semaphore (write)\n");
sd_semaphore.take();
printf("Writing to block " ANSI_BLUE "%d" ANSI_RESET "\n", block);
memcpy(SD::write_buffer, data, BLOCKSIZE);
//Start the write
uint8_t argument[4];
reset_argument(argument);
pack_argument(argument, block);
if (!send_command(CMD::WRITE_BLOCK, CMD_RESPONSE_SIZE::WRITE_BLOCK, response, argument)){
return fail();
}
assert_cs();
//needs 8 clock cycles
delay8(1);
//reset pending interrupts
LPC_GPDMA->IntTCClear = 0x01 << SD_DMACH_NR;
LPC_GPDMA->IntErrClr = 0x01 << SD_DMACH_NR;
LPC_GPDMA->SoftSReq = SD_DMA_REQUEST_LINES;
//Prepare channel
SD_DMACH->CSrcAddr = (uint32_t)SD::write_buffer;
SD_DMACH->CDestAddr = (uint32_t)&SD_SSP->DR;
SD_DMACH->CLLI = 0;
SD_DMACH->CControl = (uint32_t)BLOCKSIZE
| 0x01 << 26 //source increment
| 0x01 << 31; //Terminal count interrupt
SD_SSP->DMACR = 0x02; //Enable ssp write dma
SD_DMACH->CConfig = 0x1 //enable
| SD_DMA_DEST_PERIPHERAL << 6
| 0x1 << 11 //mem to peripheral
| 0x1 << 14 //enable error interrupt
| 0x1 << 15; //enable terminal count interrupt
return true;
}
}
extern "C" __attribute__ ((interrupt)) void DMA_IRQHandler(void) {
printf("dma irq\n");
uint8_t channelBit = 1 << SD_DMACH_NR;
if (LPC_GPDMA->IntStat & channelBit) {
if (LPC_GPDMA->IntTCStat & channelBit) {
printf(ANSI_GREEN "terminal count interrupt\n" ANSI_RESET);
LPC_GPDMA->IntTCClear = channelBit;
}
if (LPC_GPDMA->IntErrStat & channelBit) {
printf(ANSI_RED "error interrupt\n" ANSI_RESET);
LPC_GPDMA->IntErrClr = channelBit;
}
SD_DMACH->CConfig = 0;
SD_SSP->IMSC = (1 << 3);
}
}
extern "C" __attribute__ ((interrupt)) void SSP2_IRQHandler(void) {
if (SD_SSP->MIS & (1 << 3)) {
SD_SSP->IMSC &= ~(1 << 3);
printf("waiting until idle\n");
while(SD_SSP->SR & (1UL << 4));
//Stop transfer token
//I'm not sure if the part below up until deassert_cs is necessary.
//Adding or removing it made no difference.
SPI::send(0xFD);
{
uint8_t response;
unsigned int timeout = 4096;
do {
response = SPI::receive();
} while(response != 0x00 && --timeout);
if (timeout == 0){
deassert_cs();
printf("fail");
return;
}
}
//Now wait until the device isn't busy anymore
{
uint8_t response;
unsigned int timeout = 4096;
do {
response = SPI::receive();
} while(response != 0xFF && --timeout);
if (timeout == 0){
deassert_cs();
printf("fail");
return;
}
}
deassert_cs();
printf("idle\n");
SD::sd_semaphore.give_from_isr();
}
}
A few remarks about the code and setup:
Written for the lpc4088 with FreeRTOS
All SD_xxx defines are conditional defines to select the right pins (I need to use SSP2 in my dev setup, SSP0 for the final product)
All external function that are not defined in this snippet (e.g. pack_argument, send_command, semaphore.take() etc.) are known to be working correctly (most of these come from the working busy-wait SD implementation. I can't of course guarantee 100% that they are bugless, but they seem to be working right.).
Since I'm in the process of debugging this there are a lot of printfs and hardcoded SSP2 variables. These are of course temporarily.
I mostly used this as example code.
Now I have already tried the following things:
Write without DMA using busy-wait over SSP. As mentioned before I started with a working implementation of this, so I know the problem has to be in the DMA implementation and not somewhere else.
Write from mem->mem instead of mem->sd to eliminate the SSP peripheral. mem->mem worked fine, so the problem must be in the SSP part of the DMA setup.
Checked if the ISRs are called. They are: first the DMA IRS is called for the terminal count interrupt, and then the SSP2 IRS is called. So the IRSs are (probably) setup correctly.
Made a binary dump of the entire sd content to see if it the content might have been written to the wrong location. Result: the content send over DMA was not present anywhere on the SD card (I did this with any change I made to the code. None of it got the data on the SD card).
Added a long (~1-2 seconds) timeout in the SSP IRS by repeatedly requesting bytes from the SD card to make sure that there wasn't a timeout issue (e.g. that I tried to read the bytes before the SD card had the chance to process everything). This didn't change the outcome at all.
Unfortunately due to lack of hardware tools I haven't been able yet to verify if the bytes are actually send over the data lines.
What is wrong with my code, or where can I look to find the cause of this problem? After spending way more hours on this then I'd like to admit I really have no idea how to get this working and any help is appreciated!
UPDATE: I did a lot more testing, and thus I got some more results. The results below I got by writing 4 blocks of 512 bytes. Each block contains constantly increasing numbers module 256. Thus each block contains 2 sequences going from 0 to 255. Results:
Data is actually written to the SD card. However, it seems that the first block written is lost. I suppose there is some setup done in the write function that needs to be done earlier.
The bytes are put in a very weird (and wrong) order: I basically get alternating all even numbers followed by all odd numbers. Thus I first get even numbers 0x00 - 0xFE and then all odd numbers 0x01 - 0xFF (total number of written bytes seems to be correct, with the exception of the missing first block). However, there's even one exception in this sequence: each block contains 2 of these sequences (sequence is 256 bytes, block is 512), but the first sequence in each block has 0xfe and 0xff "swapped". That is, 0xFF is the end of the even numbers and 0xFE is the end of the odd series. I have no idea what kind of black magic is going on here. Just in case I've done something dumb here's the snippet that writes the bytes:
uint8_t block[512];
for (int i = 0; i < 512; i++) {
block[i] = (uint8_t)(i % 256);
}
if (!SD::write(10240, block, 1)) { //this one isn't actually written
WARN("noWrite", proc);
}
if (!SD::write(10241, block, 1)) {
WARN("noWrite", proc);
}
if (!SD::write(10242, block, 1)) {
WARN("noWrite", proc);
}
if (!SD::write(10243, block, 1)) {
WARN("noWrite", proc);
}
And here is the raw binary dump. Note that this exact pattern is fully reproducible: so far each time I tried this I got this exact same pattern.
Update2: Not sure if it's relevant, but I use sdram for memory.
When I finally got my hands on a logic analyzer I got a lot more information and was able to solve these problems.
There were a few small bugs in my code, but the bug that caused this behaviour was that I didn't send the "start block" token (0xFE) before the block and I didn't send the 16 bit (dummy) crc after the block. When I added these to the transfer buffer everything was written successfully!
So this fix was as followed:
bool write (size_t block, uint8_t const * data, size_t blocks) {
//TODO: support more than one block
ASSERT(blocks == 1);
printf("Request sd semaphore (write)\n");
sd_semaphore.take();
printf("Writing to block " ANSI_BLUE "%d" ANSI_RESET "\n", block);
SD::write_buffer[0] = 0xFE; //start block
memcpy(&SD::write_buffer[1], data, BLOCKSIZE);
SD::write_buffer[BLOCKSIZE + 1] = 0; //dummy crc
SD::write_buffer[BLOCKSIZE + 2] = 0;
//...
}
As a side note, the reason why the first block wasn't written was simply because I didn't wait until the device was ready before sending the first block. Doing so fixed the problem.

fast reading constant data stream from serial port in C++.net

I'm trying to establish a SerialPort connection which transfers 16 bit data packages at a rate of 10-20 kHz. Im programming this in C++/CLI. The sender just enters an infinte while-loop after recieving the letter "s" and constantly sends 2 bytes with the data.
A Problem with the sending side is very unlikely, since a more simple approach works perfectly but too slow (in this approach, the reciever sends always an "a" first, and then gets 1 package consisting of 2 bytes. It leads to a speed of around 500Hz).
Here is the important part of this working but slow approach:
public: SerialPort^ port;
in main:
Parity p = (Parity)Enum::Parse(Parity::typeid, "None");
StopBits s = (StopBits)Enum::Parse(StopBits::typeid, "1");
port = gcnew SerialPort("COM16",384000,p,8,s);
port->Open();
and then doing as often as wanted:
port->Write("a");
int i = port->ReadByte();
int j = port->ReadByte();
This is now the actual approach im working with:
static int values[1000000];
static int counter = 0;
void reader(void)
{
SerialPort^ port;
Parity p = (Parity)Enum::Parse(Parity::typeid, "None");
StopBits s = (StopBits)Enum::Parse(StopBits::typeid, "1");
port = gcnew SerialPort("COM16",384000,p,8,s);
port->Open();
unsigned int i = 0;
unsigned int j = 0;
port->Write("s"); //with this command, the sender starts to send constantly
while(true)
{
i = port->ReadByte();
j = port->ReadByte();
values[counter] = j + (i*256);
counter++;
}
}
in main:
Thread^ readThread = gcnew Thread(gcnew ThreadStart(reader));
readThread->Start();
The counter increases (much more) rapidly at a rate of 18472 packages/s, but the values are somehow wrong.
Here is an example:
The value should look like this, with the last 4 bits changing randomly (its a signal of an analogue-digital converter):
111111001100111
Here are some values of the threaded solution given in the code:
1110011001100111
1110011000100111
1110011000100111
1110011000100111
So it looks like the connection reads the data in the middle of the package (to be exact: 3 bits too late). What can i do? I want to avoid a solution where this error is fixed later in the code while reading the packages like this, because I don't know if the the shifting error gets worse when I edit the reading code later, which I will do most likely.
Thanks in advance,
Nikolas
PS: If this helps, here is the code of the sender-side (an AtMega168), written in C.
uint8_t activate = 0;
void uart_puti16(uint16_t val) //function that writes the data to serial port
{
while ( !( UCSR0A & (1<<UDRE0)) ) //wait until serial port is ready
nop(); // wait 1 cycle
UDR0 = val >> 8; //write first byte to sending register
while ( !( UCSR0A & (1<<UDRE0)) ) //wait until serial port is ready
nop(); // wait 1 cycle
UDR0 = val & 0xFF; //write second byte to sending register
}
in main:
while(1)
{
if(active == 1)
{
uart_puti16(read()); //read is the function that gives a 16bit data set
}
}
ISR(USART_RX_vect) //interrupt-handler for a recieved byte
{
if(UDR0 == 'a') //if only 1 single data package is requested
{
uart_puti16(read());
}
if(UDR0 == 's') //for activating constant sending
{
active = 1;
}
if(UDR0 == 'e') //for deactivating constant sending
{
active = 0;
}
}
At the given bit rate of 384,000 you should get 38,400 bytes of data (8 bits of real data plus 2 framing bits) per second, or 19,200 two-byte values per second.
How fast is counter increasing in both instances? I would expect any modern computer to keep up with that rate whether using events or directly polling.
You do not show your simpler approach which is stated to work. I suggest you post that.
Also, set a breakpoint at the line
values[counter] = j + (i*256);
There, inspect i and j. Share the values you see for those variables on the very first iteration through the loop.
This is a guess based entirely on reading the code at http://msdn.microsoft.com/en-us/library/system.io.ports.serialport.datareceived.aspx#Y228. With this caveat out of the way, here's my guess:
Your event handler is being called when data is available to read -- but you are only consuming two bytes of the available data. Your event handler may only be called every 1024 bytes. Or something similar. You might need to consume all the available data in the event handler for your program to continue as expected.
Try to re-write your handler to include a loop that reads until there is no more data available to consume.