Hi everyone!
I am struggling for some time with a problem that concerns an Atmega324PA and a PC.I was asked to create a communication protocol between the 2 of them but for some reason either the PC part doesn't work,either Atmega.I am using Atmega as a console as it has a joystick and a matrix display attached to it.My problem comes when I want to send the input values from the PC to the console because it won't read any value inputed from the keyboard although I have already specified both the PC and console side the values which are used as input for moving the paddles(i am using it in a ping-pong game with FreeRTOS).This is how the code looks like:
//on the console side void Input_PC(void *pvParameters) { // The parameters are not used ( void ) pvParameters; #if (configUSE_APPLICATION_TASK_TAG == 1) // Set task no to be used for tracing with R2R-Network vTaskSetApplicationTaskTag( NULL, ( void * ) 1 ); #endif uint8_t Input_PC=0; _x_com_received_chars_queue=xQueueCreate(_COM_RX_QUEUE_LENGTH,(unsigned portBASE_TYPE)sizeof(uint8_t));//creating queue for input values init_com(_x_com_received_chars_queue); while(1) { xQueueReceive(pc_queue,&Input_PC,portMAX_DELAY);//sending the PC queue values pc_value = Input_PC; for(int i=4;i<10;i++) { if(Input_PC==75)//move left on PC { if(twod[i][13]==1 && twod[i-3][13]==twod[0][13]) { twod[i][13] = 0; twod[i-1][13] = 1; twod[i-2][13] = twod[i-1][13]; twod[i-3][13] = twod[i-2][13]; break; } }else if(Input_PC==77)//move right on the PC { if (twod[i][13] == 1 && twod[i+3][0]==twod[9][13]) { twod[i][13] = 0; twod[i+1][13] = 1; twod[i+2][13]= twod[i+1][0]; twod[i+3][13]= twod[i+2][0]; break; } vTaskDelay(100); } } } }
and this is the byte stuffing method along with a CRC method that checks if the data send has any errors or not:
//communication protocol console void COM_Protocol(void *pvParameters) { ( void ) pvParameters; #if (configUSE_APPLICATION_TASK_TAG == 1) // Set task no to be used for tracing with R2R-Network vTaskSetApplicationTaskTag( NULL, ( void * ) 8); #endif uint8_t index = 0; uint8_t unpacked_message[20] = {0};//this value "20" was initialy 10 but for some reason it will not work with 10 and i changed it to 20 uint8_t received=0; uint8_t is_empty = 0; while(1) { if(!xQueueReceive(_x_com_received_chars_queue, &received, portMAX_DELAY)) { vTaskDelay(200); } switch (state) {//start creating the cases case IDLE://if the state is idle and the byte received is flag,the pointer will look next for the data is_empty = 0; if (received == FLAG) state = DATA; break; case DATA://if the data is next,then the ESCCHAR found in it will set the state to ESC if (received == ESCCHAR ) { state = ESC; } else if (received == FLAG && is_empty != 0)//if there is a FLAG received and the queue is not empty,then the state will be idle and the message sent //will be checked using the method check_message which will ack or unack the message depeending on what it receives { state = IDLE; check_message(unpacked_message, index); index = 0; } else if (received == FLAG && is_empty == 0)//if the queue is empty then the message is marked as received { state = IDLE; } else { unpacked_message= received; is_empty = 1; index++; } break; case ESC://in the ESC state the message is received and the queue pointer is set to 1.The state will change to DATA unpacked_message
= received; is_empty = 1; index++; state = DATA; break; } vTaskDelay(50); } } void check_message(uint8_t message[], uint8_t length)//the KEYBOARD_INPUT is the operation code for the pc input. { if(message[1] == s_nr)//check if the message index is the sequence nr { //if(message[length-1] == getCRC(message, length-1)) //if(message[length-1] == 5) if(message[length-1] == CRC_calculation(message, length-1))//check if the message has any errors { uint8_t to_push; if(message[0] == KEYBORD_INPUT)//push the instruction from the pc input to the queue { to_push = message[2]; xQueueSend(pc_queue, &to_push, portMAX_DELAY); //should be message[2] once the seq number and op_code are added answer[0] = FLAG; answer[1] = ACK_OP_CODE;//ackowledge the message answer[2] = s_nr; answer[3] = FLAG; com_send_bytes(&answer[0], 1); com_send_bytes(&answer[1], 1); com_send_bytes(&answer[2], 1); com_send_bytes(&answer[3], 1); s_nr++; } } else { answer[0] = FLAG; answer[1] = NACK_OP_CODE;//unacknowledge the message answer[2] = s_nr; answer[3] = FLAG; com_send_bytes(&answer[0], 1); com_send_bytes(&answer[1], 1); com_send_bytes(&answer[2], 1); com_send_bytes(&answer[3], 1); } } }
Now I am sure that the console side works well since when i declare the tasks and I run it I won't have any problem with the paddle moving.When I run the code in Visual Studio though and I press "A" and "D" for left and right,nothing will work as it will not read the input from the keyboard although I am using an operation code for the keyboard input:
First I open the port "COM4" to use it as a serial to transimt data:
//communication from the PC side //method used to open the serial connection with the board void open_port() { DCB dcbSerialParams = { 0 }; COMMTIMEOUTS timeouts = { 0 }; fprintf(stderr, "Opening serial port..."); // In "Device Manager" find "Ports (COM & LPT)" and see which COM port the game controller is using (here COM4) hSerial = CreateFile( "COM4", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hSerial == INVALID_HANDLE_VALUE) { fprintf(stderr, "Here Error\n"); return; } else fprintf(stderr, "OK\n"); // Set device parameters (115200 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; } 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; } // 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; } }
The inputTask will take the values inputed from the keyboard and will send then on the console side:
/*the input task will wait for an input from the keyboard and then it will send the operation code along with the length of the operation to the console.The method prepare_for_sending will check the message for errors and it will perform a byte framing */ static void inputTask(void *pvParameters) { uint8_t *to_insert; uint8_t value; for (;;) { input = (uint8_t)getch(); value = input; printf(" /n The value is: %d", value); if ( input == 75 || input == 77) { to_insert = &input; prepare_for_sending(to_insert , 1, KEYBORD_INPUT_CODE); input = 0; //prepare_for_sending(to_test, 6); //xQueueSend(input_queue, (void*)&to_insert, portMAX_DELAY); } vTaskDelay(300); } }
There are also some methods that I found interesting suggested by some of my colleagues for sending the message and cheking it for errors with CRC,I format it with an operation code and i use byte framing to send it.The method receiveTask it's the one that concerns me as there is where the magic happens.The method will check the byte stuffing to see if it is correct or not
/*This task will look similar with the COM_Protocol on the console side as it receives the message and checks if it has been sent correctly or not.*/ static void receiveTask(void *pvParameters) { uint8_t i = 0; uint8_t *byte_to_receive = &i;//a pointer that is used as value for every byte from the frame and it checks if it has the value of flag escchar uint8_t is_empty = 0; uint8_t unpacked_message[20];//this one I don't really know why is 20.I tried playing with the values but without any luck. uint8_t index = 0; uint8_t check = 1; uint8_t bytes_to_receive[10]; /* Prevent the compiler warning about the unused parameter. */ (void)pvParameters; for (;; ) { //Read text DWORD bytes_read, total_bytes_read = 0; ReadFile(hSerial, byte_to_receive, 1, &bytes_read, total_bytes_read); fprintf(stderr, "%d bytes read\n", bytes_read); fprintf(stderr, "Text read: %d\n", *byte_to_receive); fprintf(stderr, "End\n"); switch (state) { case IDLE: is_empty = 0; if (*byte_to_receive == FLAG) state = DATA; break; case DATA: if (*byte_to_receive == ESCCHAR) { state = ESC; } else if (*byte_to_receive == FLAG && is_empty != 0) { state = IDLE; check = check_message(unpacked_message, index); if (check == 0) { DWORD bytes_written, total_bytes_written = 0; fprintf(stderr, "Sending bytes..."); if (!WriteFile(hSerial, to_send, to_send_size, &bytes_written, NULL)) { fprintf(stderr, "Error\n"); CloseHandle(hSerial); return 1; } } for (int i = 0; i < 20; i++) { to_send[0] = 0; } check = 1; index = 0; } else if (*byte_to_receive == FLAG && is_empty == 0) { state = IDLE; } else { unpacked_message= *byte_to_receive; is_empty = 1; index++; } break; case ESC: unpacked_message
= *byte_to_receive; is_empty = 1; index++; state = DATA; break; } vTaskDelay(100); } // Close serial port fprintf(stderr, "Closing serial port..."); if (CloseHandle(hSerial) == 0) { fprintf(stderr, "Error\n"); return 1; } } uint8_t getCRC(uint8_t message[], uint8_t length) { uint8_t crc = 0; for (int i = 0; i < length; i++) { crc ^= message[i]; for (int j = 0; j < 8; j++) { if (crc & 1) crc ^= POLYNOMINAL; crc >>= 1; } } return crc; }
Can someone please help me understand what is happening with the code and why the values from the keyboard will not make the paddle move?
Best regards,
M