Major UART communication improvement

- the UART communication is improved based on UART Idle line detection interrupt
- an Rx ring buffer is used to manage the UART incoming data
- both Tx and Rx are efficiently handled using DMA
- fixed #1

Other:
- minor visual improvements
This commit is contained in:
EmanuelFeru 2020-06-21 23:09:27 +02:00
parent 9edd22b6b7
commit ee6ab3a886
9 changed files with 273 additions and 185 deletions

View File

@ -27,54 +27,54 @@
// Ubuntu: define the desired build variant here if you want to use make in console
// or use VARIANT environment variable for example like "make -e VARIANT=VARIANT_DEBUG". Select only one at a time.
#if !defined(PLATFORMIO)
// #define VARIANT_DEBUG // Variant for debugging and checking the capabilities of the side-board
// #define VARIANT_HOVERBOARD // Variant for using the side-boards connected to the Hoverboard mainboard
// #define VARIANT_DEBUG // Variant for debugging and checking the capabilities of the side-board
// #define VARIANT_HOVERBOARD // Variant for using the side-boards connected to the Hoverboard mainboard
#endif
/* ==================================== DO NOT TOUCH SETTINGS ==================================== */
#define BYPASS_CUBEMX_DEFINES // [-] Use this flag to bypass the Ports definitions generated by CUBE MX in main.h and use the ones in defines.h
#define MPU6050 // [-] Define IMU sensor type
#define MPU_GYRO_FSR 2000 // [deg/s] Set Gyroscope Full Scale Range: 250 deg/s, 500 deg/s, 1000 deg/s, 2000 deg/s. !! DMP sensor fusion works only with 2000 deg/s !!
#define MPU_ACCEL_FSR 2 // [g] Set Acceleromenter Full Scale Range: 2g, 4g, 8g, 16g. !! DMP sensor fusion works only with 2g !!
#define MPU6050 // [-] Define IMU sensor type
#define MPU_GYRO_FSR 2000 // [deg/s] Set Gyroscope Full Scale Range: 250 deg/s, 500 deg/s, 1000 deg/s, 2000 deg/s. !! DMP sensor fusion works only with 2000 deg/s !!
#define MPU_ACCEL_FSR 2 // [g] Set Acceleromenter Full Scale Range: 2g, 4g, 8g, 16g. !! DMP sensor fusion works only with 2g !!
#ifdef BYPASS_CUBEMX_DEFINES
#define MPU_I2C_SPEED 400000 // [bit/s] Define I2C speed for communicating with the MPU6050
#define MPU_I2C_SPEED 400000 // [bit/s] Define I2C speed for communicating with the MPU6050
#endif
#define DELAY_IN_MAIN_LOOP 1 // [ms] Delay in the main loop
// #define PRINTF_FLOAT_SUPPORT // [-] Uncomment this for printf to support float on Serial Debug. It will increase code size! Better to avoid it!
#define DELAY_IN_MAIN_LOOP 1 // [ms] Delay in the main loop
// #define PRINTF_FLOAT_SUPPORT // [-] Uncomment this for printf to support float on Serial Debug. It will increase code size! Better to avoid it!
/* =============================================================================================== */
/* ==================================== SETTINGS MPU-6050 ==================================== */
#define MPU_SENSOR_ENABLE // [-] Enable flag for MPU-6050 sensor. Comment-out this flag to Disable the MPU sensor and reduce code size.
#define MPU_DMP_ENABLE // [-] Enable flag for MPU-6050 DMP (Digital Motion Processing) functionality.
#define MPU_DEFAULT_HZ 20 // [Hz] Default MPU frequecy: must be between 1Hz and 200Hz.
#define TEMP_READ_MS 500 // [ms] Temperature read time interval
#define PEDO_READ_MS 1000 // [ms] Pedometer read time interval
// #define USE_CAL_HW_REGISTERS // [-] Uncommnent this to SAVE the sensor calibration to the MPU-6050 registers after the Self-test was run
#define MPU_DMP_ENABLE // [-] Enable flag for MPU-6050 DMP (Digital Motion Processing) functionality.
#define MPU_DEFAULT_HZ 20 // [Hz] Default MPU frequecy: must be between 1Hz and 200Hz.
#define TEMP_READ_MS 500 // [ms] Temperature read time interval
#define PEDO_READ_MS 1000 // [ms] Pedometer read time interval
// #define USE_CAL_HW_REGISTERS // [-] Uncommnent this to SAVE the sensor calibration to the MPU-6050 registers after the Self-test was run
// DMP Tap Detection Settings
#define DMP_TAP_AXES TAP_XYZ // [-] Set which axes will register a tap: TAP_XYZ, TAP_X, TAP_Y, TAP_Z
#define DMP_TAP_THRESH 250 // [mg/ms] Set tap threshold for the selected axis.
#define DMP_TAP_COUNT 1 // [-] Set minimum number of taps needed for an interrupt. Minimum consecutive taps: 1 to 4
#define DMP_TAP_TIME 100 // [ms] Set time length between valid taps.
#define DMP_TAP_TIME_MULTI 500 // [ms] Set max time between taps to register as a multi-tap.
#define DMP_SHAKE_REJECT_THRESH 200 // [deg/s] Set shake rejection threshold in degree per second (dps). If the DMP detects a gyro sample larger than the thresh, taps are rejected.
#define DMP_SHAKE_REJECT_TIME 40 // [ms] Set shake rejection time. Sets the length of time that the gyro must be outside of the DMP_SHAKE_REJECT_THRESH before taps are rejected. A mandatory 60 ms is added to this parameter.
#define DMP_SHAKE_REJECT_TIMEOUT 10 // [ms] Set shake rejection timeout. Sets the length of time after a shake rejection that the gyro must stay inside of the threshold before taps can be detected again. A mandatory 60 ms is added to this parameter.
#define DMP_TAP_AXES TAP_XYZ // [-] Set which axes will register a tap: TAP_XYZ, TAP_X, TAP_Y, TAP_Z
#define DMP_TAP_THRESH 250 // [mg/ms] Set tap threshold for the selected axis.
#define DMP_TAP_COUNT 1 // [-] Set minimum number of taps needed for an interrupt. Minimum consecutive taps: 1 to 4
#define DMP_TAP_TIME 100 // [ms] Set time length between valid taps.
#define DMP_TAP_TIME_MULTI 500 // [ms] Set max time between taps to register as a multi-tap.
#define DMP_SHAKE_REJECT_THRESH 200 // [deg/s] Set shake rejection threshold in degree per second (dps). If the DMP detects a gyro sample larger than the thresh, taps are rejected.
#define DMP_SHAKE_REJECT_TIME 40 // [ms] Set shake rejection time. Sets the length of time that the gyro must be outside of the DMP_SHAKE_REJECT_THRESH before taps are rejected. A mandatory 60 ms is added to this parameter.
#define DMP_SHAKE_REJECT_TIMEOUT 10 // [ms] Set shake rejection timeout. Sets the length of time after a shake rejection that the gyro must stay inside of the threshold before taps can be detected again. A mandatory 60 ms is added to this parameter.
/* ==================================== SETTINGS USART ==================================== */
#if defined(VARIANT_DEBUG)
#define SERIAL_DEBUG // [-] Define for Serial Debug via the serial port
#define SERIAL_DEBUG // [-] Define for Serial Debug via the serial port
#elif defined(VARIANT_HOVERBOARD)
#define SERIAL_CONTROL // [-] Define for Serial Control via the serial port
#define SERIAL_FEEDBACK // [-] Define for Serial Feedback via the serial port
#define SERIAL_CONTROL // [-] Define for Serial Control via the serial port
#define SERIAL_FEEDBACK // [-] Define for Serial Feedback via the serial port
#endif
#ifdef BYPASS_CUBEMX_DEFINES
#define USART_MAIN_BAUD 38400 // [bit/s] MAIN Serial Tx/Rx baud rate
#define USART_MAIN_BAUD 38400 // [bit/s] MAIN Serial Tx/Rx baud rate
#endif
#define SERIAL_START_FRAME 0xABCD // [-] Start frame definition for reliable serial communication
#define SERIAL_TIMEOUT 800 // [-] Numer of wrong received data for Serial timeout detection
#define SERIAL_START_FRAME 0xABCD // [-] Start frame definition for reliable serial communication
#define SERIAL_TIMEOUT 500 // [-] Number of wrong received data for Serial timeout detection. Depends on DELAY_IN_MAIN_LOOP
#define SERIAL_BUFFER_SIZE 64 // [bytes] Size of Serial Rx buffer. Make sure it is always larger than the 'Feedback' structure size
/* ==================================== VALIDATE SETTINGS ==================================== */
@ -82,6 +82,9 @@
#error SERIAL_DEBUG and SERIAL_CONTROL not allowed. It is on the same cable.
#endif
#if defined(SERIAL_DEBUG) && defined(SERIAL_FEEDBACK)
#error SERIAL_DEBUG and SERIAL_FEEDBACK not allowed. It is on the same cable.
#endif
#endif

View File

@ -35,14 +35,15 @@
#endif
/* =========================== Defines General =========================== */
// #define _BV(bit) (1 << (bit))
// #define ARRAYNUM(arr_nanme) (uint32_t)(sizeof(arr_nanme) / sizeof(*(arr_nanme)))
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define max(a, b) (((a) > (b)) ? (a) : (b))
#define i2c_write i2c_writeBytes
#define i2c_read i2c_readBytes
#define delay_ms HAL_Delay
#define get_ms get_tick_count_ms
// #define _BV(bit) (1 << (bit))
#define ARRAY_LEN(x) (uint32_t)(sizeof(x) / sizeof(*(x)))
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define max(a, b) (((a) > (b)) ? (a) : (b))
#define i2c_write i2c_writeBytes
#define i2c_read i2c_readBytes
#define delay_ms HAL_Delay
#define get_ms get_tick_count_ms
#define log_i printf // redirect the log_i debug function to printf
#ifdef BYPASS_CUBEMX_DEFINES
@ -88,7 +89,6 @@
#endif
/* =========================== Defines MPU-6050 =========================== */
#define log_i printf // redirect the log_i debug function to printf
#define RAD2DEG 57.295779513082323 // RAD2DEG = 180/pi. Example: angle[deg] = angle[rad] * RAD2DEG
#define q30 1073741824 // 1073741824 = 2^30
#define ACCEL_ON (0x01)

View File

@ -29,6 +29,41 @@ void consoleLog(char *message);
void get_tick_count_ms(unsigned long *count);
void intro_demo_led(uint32_t tDelay);
/* input initialization function */
void input_init(void);
/* usart read functions */
void usart_rx_check(void);
#ifdef SERIAL_DEBUG
void usart_process_debug(uint8_t *userCommand, uint32_t len);
#endif
#ifdef SERIAL_CONTROL
typedef struct{
uint16_t start;
int16_t roll;
int16_t pitch;
int16_t yaw;
uint16_t sensors;
uint16_t checksum;
} SerialSideboard;
#endif
#ifdef SERIAL_FEEDBACK
typedef struct{
uint16_t start;
int16_t cmd1;
int16_t cmd2;
int16_t speedR_meas;
int16_t speedL_meas;
int16_t batVoltage;
int16_t boardTemp;
uint16_t cmdLed;
uint16_t checksum;
} SerialFeedback;
void usart_process_data(SerialFeedback *Feedback_in, SerialFeedback *Feedback_out);
#endif
/* i2c write/read functions */
int8_t i2c_writeBytes(uint8_t slaveAddr, uint8_t regAddr, uint8_t length, uint8_t *data);
int8_t i2c_writeByte (uint8_t slaveAddr, uint8_t regAddr, uint8_t data);

View File

@ -17,7 +17,7 @@ The original sideboard hardware supports one 4-pin cable that originally was con
The LED boards consist of colored LEDs (blue, red, green, orange) used for design and to inform the user about the current hoverboard state. Below the pinout of the LED boards is shown.
![ledboard](/docs/pictures/ledboard_pinout.png)
The heart of the sideboard is a [STM32F103C8T6](/docs/stm32f103c8-Datasheet.pdf) with the pinout shown in the follwing figure:
The sideboard brain is an [STM32F103C8T6](/docs/stm32f103c8-Datasheet.pdf) with the pinout shown in the follwing figure:
![MCU_pinout](/docs/pictures/MCU_pinout.png)
A very important component of the sideboard is the IMU [MPU-6050](https://www.invensense.com/products/motion-tracking/6-axis/mpu-6050/) from Invensense. The [MPU-6050](https://www.invensense.com/products/motion-tracking/6-axis/mpu-6050/) determines the board orientation by combining a 3-axis gyroscope and a 3-axis accelerometer on the same silicon die, together with an onboard Digital Motion Processor™ (DMP™), which processes complex 6-axis MotionFusion algorithms. The DMP™ offers many features, such as:

View File

@ -49,24 +49,7 @@
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
extern UART_HandleTypeDef huart2;
uint8_t rxBuffer, userCommand = 0; // holds the user command input
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
__HAL_UART_FLUSH_DRREGISTER(&huart2); // Clear the buffer to prevent overrun
#ifdef SERIAL_DEBUG
if (rxBuffer != '\n' && rxBuffer != '\r') { // Do not accept 'new line' (ascii 10) and 'carriage return' (ascii 13) commands
userCommand = rxBuffer;
}
#endif
}
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
@ -83,44 +66,25 @@ void SystemClock_Config(void);
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
extern UART_HandleTypeDef huart2;
#ifdef SERIAL_CONTROL
typedef struct{
uint16_t start;
int16_t roll;
int16_t pitch;
int16_t yaw;
uint16_t sensors;
uint16_t checksum;
} SerialSideboard;
SerialSideboard Sideboard;
extern SerialSideboard Sideboard;
#endif
#ifdef SERIAL_FEEDBACK
typedef struct{
uint16_t start;
int16_t cmd1;
int16_t cmd2;
int16_t speedR_meas;
int16_t speedL_meas;
int16_t batVoltage;
int16_t boardTemp;
uint16_t cmdLed;
uint16_t checksum;
} SerialFeedback;
SerialFeedback Feedback;
SerialFeedback NewFeedback;
static int16_t timeoutCntSerial = 0; // Timeout counter for Rx Serial command
static uint8_t timeoutFlagSerial = 0; // Timeout Flag for Rx Serial command: 0 = OK, 1 = Problem detected (line disconnected or wrong Rx data)
extern SerialFeedback Feedback;
extern uint16_t timeoutCntSerial; // Timeout counter for Rx Serial command
extern uint8_t timeoutFlagSerial; // Timeout Flag for Rx Serial command: 0 = OK, 1 = Problem detected (line disconnected or wrong Rx data)
#endif
extern MPU_Data mpu; // holds the MPU-6050 data
ErrorStatus mpuStatus; // holds the MPU-6050 status: SUCCESS or ERROR
extern MPU_Data mpu; // holds the MPU-6050 data
extern ErrorStatus mpuStatus; // holds the MPU-6050 status: SUCCESS or ERROR
GPIO_PinState sensor1, sensor2; // holds the sensor1 and sensor 2 values
GPIO_PinState sensor1_read, sensor2_read; // holds the instantaneous Read for sensor1 and sensor 2
GPIO_PinState sensor1, sensor2; // holds the sensor1 and sensor 2 values
GPIO_PinState sensor1_read, sensor2_read; // holds the instantaneous Read for sensor1 and sensor 2
static uint32_t main_loop_counter; // main loop counter to perform task squeduling inside main()
static uint32_t main_loop_counter; // main loop counter to perform task squeduling inside main()
/* USER CODE END 0 */
/**
@ -155,36 +119,7 @@ int main(void)
MX_USART2_UART_Init();
MX_I2C1_Init();
/* USER CODE BEGIN 2 */
#ifdef SERIAL_DEBUG
__HAL_UART_FLUSH_DRREGISTER(&huart2);
HAL_UART_Receive_DMA (&huart2, (uint8_t *)&rxBuffer, sizeof(rxBuffer));
#endif
#ifdef SERIAL_CONTROL
__HAL_UART_FLUSH_DRREGISTER(&huart2);
HAL_UART_Transmit_DMA(&huart2, (uint8_t *)&Sideboard, sizeof(Sideboard));
#endif
#ifdef SERIAL_FEEDBACK
__HAL_UART_FLUSH_DRREGISTER(&huart2);
HAL_UART_Receive_DMA (&huart2, (uint8_t *)&NewFeedback, sizeof(NewFeedback));
#endif
intro_demo_led(100); // Short LEDs intro demo with 100 ms delay. This also gives some time for the MPU-6050 to power-up.
#ifdef MPU_SENSOR_ENABLE
if(mpu_config()) { // IMU MPU-6050 config
mpuStatus = ERROR;
HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET); // Turn on RED LED
}
else {
mpuStatus = SUCCESS;
HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET); // Turn on GREEN LED
}
mpu_handle_input('h'); // Print the User Help commands to serial
#else
HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET); // Turn on GREEN LED
#endif
input_init(); // Input initialization
/* USER CODE END 2 */
/* Infinite loop */
@ -203,19 +138,9 @@ int main(void)
if (Feedback.cmdLed & LED4_SET) { HAL_GPIO_WritePin(LED4_GPIO_Port, LED4_Pin, GPIO_PIN_SET); } else { HAL_GPIO_WritePin(LED4_GPIO_Port, LED4_Pin, GPIO_PIN_RESET); }
if (Feedback.cmdLed & LED5_SET) { HAL_GPIO_WritePin(LED5_GPIO_Port, LED5_Pin, GPIO_PIN_SET); } else { HAL_GPIO_WritePin(LED5_GPIO_Port, LED5_Pin, GPIO_PIN_RESET); }
}
#endif
// ==================================== USER Handling ====================================
#if defined(MPU_SENSOR_ENABLE) && defined(SERIAL_DEBUG)
// Get the user Input as one character from Serial
if (userCommand != 0) { // Check the availability of a user command set by the UART DMA
log_i("Command = %c\n", userCommand);
mpu_handle_input(userCommand);
userCommand = 0;
}
#endif
#endif
// ==================================== MPU-6050 Handling ====================================
#if defined(MPU_SENSOR_ENABLE) && defined(SERIAL_DEBUG)
// Get MPU data. Because the MPU-6050 interrupt pin is not wired we have to check DMP data by pooling periodically
@ -242,6 +167,7 @@ int main(void)
HAL_GPIO_WritePin(LED4_GPIO_Port, LED4_Pin, GPIO_PIN_SET);
consoleLog("-- SENSOR 1 Active --\n");
} else if(sensor1 == GPIO_PIN_SET && sensor1_read == GPIO_PIN_RESET) {
// Sensor DEACTIVE: Do something here (one time task on deactivation)
sensor1 = GPIO_PIN_RESET;
HAL_GPIO_WritePin(LED4_GPIO_Port, LED4_Pin, GPIO_PIN_RESET);
consoleLog("-- SENSOR 1 Deactive --\n");
@ -254,6 +180,7 @@ int main(void)
HAL_GPIO_WritePin(LED5_GPIO_Port, LED5_Pin, GPIO_PIN_SET);
consoleLog("-- SENSOR 2 Active --\n");
} else if (sensor2 == GPIO_PIN_SET && sensor2_read == GPIO_PIN_RESET) {
// Sensor DEACTIVE: Do something here (one time task on deactivation)
sensor2 = GPIO_PIN_RESET;
HAL_GPIO_WritePin(LED5_GPIO_Port, LED5_Pin, GPIO_PIN_RESET);
consoleLog("-- SENSOR 2 Deactive --\n");
@ -269,7 +196,7 @@ int main(void)
// ==================================== SERIAL Tx/Rx Handling ====================================
#ifdef SERIAL_CONTROL
if (main_loop_counter % 5 == 0) { // Transmit Tx data periodically using DMA
if (main_loop_counter % 5 == 0 && __HAL_DMA_GET_COUNTER(huart2.hdmatx) == 0) { // Check if DMA channel counter is 0 (meaning all data has been transferred)
Sideboard.start = (uint16_t)SERIAL_START_FRAME;
Sideboard.roll = (int16_t)mpu.euler.roll;
Sideboard.pitch = (int16_t)mpu.euler.pitch;
@ -282,32 +209,12 @@ int main(void)
#endif
#ifdef SERIAL_FEEDBACK
uint16_t checksum;
checksum = (uint16_t)(NewFeedback.start ^ NewFeedback.cmd1 ^ NewFeedback.cmd2 ^ NewFeedback.speedR_meas ^ NewFeedback.speedL_meas
^ NewFeedback.batVoltage ^ NewFeedback.boardTemp ^ NewFeedback.cmdLed);
if (NewFeedback.start == SERIAL_START_FRAME && NewFeedback.checksum == checksum) {
if (timeoutFlagSerial) { // Check for previous timeout flag
if (timeoutCntSerial-- <= 0) // Timeout de-qualification
timeoutFlagSerial = 0; // Timeout flag cleared
} else {
memcpy(&Feedback, &NewFeedback, sizeof(Feedback)); // Copy the new data
NewFeedback.start = 0xFFFF; // Change the Start Frame for timeout detection in the next cycle
timeoutCntSerial = 0; // Reset the timeout counter
}
} else {
if (timeoutCntSerial++ >= SERIAL_TIMEOUT) { // Timeout qualification
timeoutFlagSerial = 1; // Timeout detected
timeoutCntSerial = SERIAL_TIMEOUT; // Limit timout counter value
}
// Most probably we are out-of-sync. Try to re-sync by reseting the DMA
if (NewFeedback.start != SERIAL_START_FRAME && NewFeedback.start != 0xFFFF && main_loop_counter % 5 == 0) {
HAL_UART_DMAStop(&huart2);
HAL_UART_Receive_DMA(&huart2, (uint8_t *)&NewFeedback, sizeof(NewFeedback));
}
}
if (timeoutFlagSerial && main_loop_counter % 100 == 0) { // In case of timeout bring the system to a Safe State and indicate error if desired
HAL_GPIO_TogglePin(LED3_GPIO_Port, LED3_Pin); // Toggle the Yellow LED every 100 ms
if (timeoutCntSerial++ >= SERIAL_TIMEOUT) { // Timeout qualification
timeoutFlagSerial = 1; // Timeout detected
timeoutCntSerial = SERIAL_TIMEOUT; // Limit timout counter value
}
if (timeoutFlagSerial && main_loop_counter % 100 == 0) { // In case of timeout bring the system to a Safe State and indicate error if desired
HAL_GPIO_TogglePin(LED3_GPIO_Port, LED3_Pin); // Toggle the Yellow LED every 100 ms
}
#endif

View File

@ -3714,10 +3714,11 @@ void mpu_android_orient_func(unsigned char orientation)
/* =========================== User Input Handling =========================== */
void mpu_handle_input(char c)
{
#ifdef SERIAL_DEBUG
switch (c) {
/* This command prints the Help text. */
case 'h':
consoleLog("=================== HELP COMMANDS ===================\n");
consoleLog("====== HELP COMMANDS ======\n");
consoleLog("h: Print Help commands\n");
consoleLog("8: Set Accelerometer sensor on/off\n");
consoleLog("9: Set Gyroscope sensor on/off\n");
@ -3730,17 +3731,15 @@ void mpu_handle_input(char c)
consoleLog("p: Print Pedometer data\n");
consoleLog("0: Reset Pedometer\n");
consoleLog("1: Set DMP/MPU frequency 10 Hz\n");
consoleLog("2: Set DMP/MPU frequency 20 Hz\n");
consoleLog("3: Set DMP/MPU frequency 40 Hz\n");
consoleLog("4: Set DMP/MPU frequency 50 Hz\n");
consoleLog("5: Set DMP/MPU frequency 100 Hz\n");
consoleLog("2: Set DMP/MPU frequency 50 Hz\n");
consoleLog("3: Set DMP/MPU frequency 100 Hz\n");
consoleLog(",: Set DMP interrupt to gesture event only\n");
consoleLog(".: Set DMP interrupt to continuous\n");
consoleLog("f: Set DMP on/off\n");
consoleLog("v: Set Quaternion on/off\n");
consoleLog("w: Test out low-power accel mode\n");
consoleLog("w: Test low-power accel mode\n");
consoleLog("s: Run self-test (device must be facing up or down)\n");
consoleLog("=====================================================\n");
consoleLog("===========================\n");
break;
/* These commands turn off individual sensors. */
@ -3793,33 +3792,21 @@ void mpu_handle_input(char c)
*/
case '1':
if (hal.dmp_on) {
if (0 == dmp_set_fifo_rate(10)) {consoleLog("DMP set to 10 Hz.\n");}
if (0 == dmp_set_fifo_rate(10)) {consoleLog("DMP: 10 Hz\n");}
} else
if (0 == mpu_set_sample_rate(10)) {consoleLog("MPU set to 10 Hz.\n");}
if (0 == mpu_set_sample_rate(10)) {consoleLog("MPU: 10 Hz\n");}
break;
case '2':
if (hal.dmp_on) {
if (0 == dmp_set_fifo_rate(20)) {consoleLog("DMP set to 20 Hz.\n");}
if (0 == dmp_set_fifo_rate(50)) {consoleLog("DMP: 50 Hz\n");}
} else
if (0 == mpu_set_sample_rate(20)) {consoleLog("MPU set to 20 Hz.\n");}
if (0 == mpu_set_sample_rate(50)) {consoleLog("MPU: 50 Hz\n");}
break;
case '3':
if (hal.dmp_on) {
if (0 == dmp_set_fifo_rate(40)) {consoleLog("DMP set to 40 Hz.\n");}
if (0 == dmp_set_fifo_rate(100)) {consoleLog("DMP: 100 Hz\n");}
} else
if (0 == mpu_set_sample_rate(40)) {consoleLog("MPU set to 40 Hz.\n");}
break;
case '4':
if (hal.dmp_on) {
if (0 == dmp_set_fifo_rate(50)) {consoleLog("DMP set to 50 Hz.\n");}
} else
if (0 == mpu_set_sample_rate(50)) {consoleLog("MPU set to 50 Hz.\n");}
break;
case '5':
if (hal.dmp_on) {
if (0 == dmp_set_fifo_rate(100)) {consoleLog("DMP set to 100 Hz.\n");}
} else
if (0 == mpu_set_sample_rate(100)) {consoleLog("MPU set to 100 Hz.\n");}
if (0 == mpu_set_sample_rate(100)) {consoleLog("MPU: 100 Hz\n");}
break;
/* Set hardware to interrupt on gesture event only. This feature is
@ -3918,7 +3905,7 @@ void mpu_handle_input(char c)
default:
break;
}
#endif // SERIAL_DEBUG
}

View File

@ -23,7 +23,7 @@
#include "stm32f1xx_it.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "util.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
@ -267,7 +267,10 @@ void USART2_IRQHandler(void)
/* USER CODE END USART2_IRQn 0 */
HAL_UART_IRQHandler(&huart2);
/* USER CODE BEGIN USART2_IRQn 1 */
if(RESET != __HAL_UART_GET_IT_SOURCE(&huart2, UART_IT_IDLE)) { // Check for IDLE line interrupt
__HAL_UART_CLEAR_IDLEFLAG(&huart2); // Clear IDLE line flag (otherwise it will continue to enter interrupt)
usart_rx_check(); // Check for data to process
}
/* USER CODE END USART2_IRQn 1 */
}

View File

@ -112,9 +112,7 @@ void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART2_IRQn);
/* USER CODE BEGIN USART2_MspInit 1 */
// DMA1_Channel7->CPAR = (uint32_t) & (USART2->DR);
// DMA1_Channel7->CNDTR = 0;
// DMA1->IFCR = DMA_IFCR_CTCIF7 | DMA_IFCR_CHTIF7 | DMA_IFCR_CGIF7;
__HAL_UART_ENABLE_IT (uartHandle, UART_IT_IDLE); // Enable the USART IDLE line detection interrupt
/* USER CODE END USART2_MspInit 1 */
}
}

View File

@ -26,10 +26,32 @@
#include "defines.h"
#include "config.h"
#include "util.h"
#include "mpu6050.h"
extern UART_HandleTypeDef huart2;
extern I2C_HandleTypeDef hi2c1;
// USART variables
#ifdef SERIAL_CONTROL
SerialSideboard Sideboard;
#endif
#if defined(SERIAL_DEBUG) || defined(SERIAL_FEEDBACK)
static uint8_t rx_buffer[SERIAL_BUFFER_SIZE]; // USART Rx DMA circular buffer
static uint32_t rx_buffer_len = ARRAY_LEN(rx_buffer);
#endif
#ifdef SERIAL_FEEDBACK
SerialFeedback Feedback;
SerialFeedback FeedbackRaw;
uint16_t timeoutCntSerial = 0; // Timeout counter for Rx Serial command
uint8_t timeoutFlagSerial = 0; // Timeout Flag for Rx Serial command: 0 = OK, 1 = Problem detected (line disconnected or wrong Rx data)
static uint32_t Feedback_len = sizeof(Feedback);
#endif
// MPU variables
ErrorStatus mpuStatus; // holds the MPU-6050 status: SUCCESS or ERROR
/* =========================== General Functions =========================== */
void consoleLog(char *message)
@ -97,6 +119,139 @@ void intro_demo_led(uint32_t tDelay)
}
/* =========================== Input Initialization Function =========================== */
void input_init(void) {
#ifdef SERIAL_CONTROL
HAL_UART_Transmit_DMA(&huart2, (uint8_t *)&Sideboard, sizeof(Sideboard));
#endif
#if defined(SERIAL_DEBUG) || defined(SERIAL_FEEDBACK)
HAL_UART_Receive_DMA (&huart2, (uint8_t *)rx_buffer, sizeof(rx_buffer));
#endif
intro_demo_led(100); // Short LEDs intro demo with 100 ms delay. This also gives some time for the MPU-6050 to power-up.
#ifdef MPU_SENSOR_ENABLE
if(mpu_config()) { // IMU MPU-6050 config
mpuStatus = ERROR;
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET); // Turn on RED LED
}
else {
mpuStatus = SUCCESS;
HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET); // Turn on GREEN LED
}
mpu_handle_input('h'); // Print the User Help commands to serial
#else
HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET); // Turn on GREEN LED
#endif
}
/* =========================== USART READ Functions =========================== */
/*
* Check for new data received on USART with DMA: refactored function from https://github.com/MaJerle/stm32-usart-uart-dma-rx-tx
* - this function is called for every USART IDLE line detection, in the USART interrupt handler
*/
void usart_rx_check(void)
{
#ifdef SERIAL_DEBUG
static uint32_t old_pos;
uint32_t pos;
pos = rx_buffer_len - __HAL_DMA_GET_COUNTER(huart2.hdmarx); // Calculate current position in buffer, Rx: DMA1_Channel6->CNDTR, Tx: DMA1_Channel7
if (pos != old_pos) { // Check change in received data
if (pos > old_pos) { // "Linear" buffer mode: check if current position is over previous one
usart_process_debug(&rx_buffer[old_pos], pos - old_pos); // Process data
} else { // "Overflow" buffer mode
usart_process_debug(&rx_buffer[old_pos], rx_buffer_len - old_pos); // First Process data from the end of buffer
if (pos > 0) { // Check and continue with beginning of buffer
usart_process_debug(&rx_buffer[0], pos); // Process remaining data
}
}
}
old_pos = pos; // Updated old position
if (old_pos == rx_buffer_len) { // Check and manually update if we reached end of buffer
old_pos = 0;
}
#endif // SERIAL_DEBUG
#ifdef SERIAL_FEEDBACK
static uint32_t old_pos;
uint32_t pos;
uint8_t *ptr;
pos = rx_buffer_len - __HAL_DMA_GET_COUNTER(huart2.hdmarx); // Calculate current position in buffer, Rx: DMA1_Channel6->CNDTR, Tx: DMA1_Channel7
if (pos != old_pos) { // Check change in received data
ptr = (uint8_t *)&FeedbackRaw; // Initialize the pointer with FeedbackRaw address
if (pos > old_pos && (pos - old_pos) == Feedback_len) { // "Linear" buffer mode: check if current position is over previous one AND data length equals expected length
memcpy(ptr, &rx_buffer[old_pos], Feedback_len); // Copy data. This is possible only if FeedbackRaw is contiguous! (meaning all the structure members have the same size)
usart_process_data(&FeedbackRaw, &Feedback); // Process data
} else if ((rx_buffer_len - old_pos + pos) == Feedback_len) { // "Overflow" buffer mode: check if data length equals expected length
memcpy(ptr, &rx_buffer[old_pos], rx_buffer_len - old_pos); // First copy data from the end of buffer
if (pos > 0) { // Check and continue with beginning of buffer
ptr += rx_buffer_len - old_pos; // Move to correct position in FeedbackRaw
memcpy(ptr, &rx_buffer[0], pos); // Copy remaining data
}
usart_process_data(&FeedbackRaw, &Feedback); // Process data
}
}
old_pos = pos; // Update old position
if (old_pos == rx_buffer_len) { // Check and manually update if we reached end of buffer
old_pos = 0;
}
#endif // SERIAL_FEEDBACK
}
/*
* Process Rx debug user command input
*/
#ifdef SERIAL_DEBUG
void usart_process_debug(uint8_t *userCommand, uint32_t len)
{
for (; len > 0; len--, userCommand++) {
if (*userCommand != '\n' && *userCommand != '\r') { // Do not accept 'new line' and 'carriage return' commands
log_i("Command = %c\n", *userCommand);
mpu_handle_input(*userCommand);
}
}
}
#endif // SERIAL_DEBUG
/*
* Process Rx data
* - if the Feedback_in data is valid (correct START_FRAME and checksum) copy the Feedback_in to Feedback_out
*/
#ifdef SERIAL_FEEDBACK
void usart_process_data(SerialFeedback *Feedback_in, SerialFeedback *Feedback_out)
{
uint16_t checksum;
if (Feedback_in->start == SERIAL_START_FRAME) {
checksum = (uint16_t)(Feedback_in->start ^ Feedback_in->cmd1 ^ Feedback_in->cmd2 ^ Feedback_in->speedR_meas ^ Feedback_in->speedL_meas
^ Feedback_in->batVoltage ^ Feedback_in->boardTemp ^ Feedback_in->cmdLed);
if (Feedback_in->checksum == checksum) {
*Feedback_out = *Feedback_in;
timeoutCntSerial = 0; // Reset timeout counter
timeoutFlagSerial = 0; // Clear timeout flag
}
}
}
#endif // SERIAL_FEEDBACK
/*
* UART User Error Callback
* - According to the STM documentation, when a DMA transfer error occurs during a DMA read or a write access,
* the faulty channel is automatically disabled through a hardware clear of its EN bit
* - For hoverboard applications, the UART communication can be unrealiable, disablind the DMA transfer
* - therefore the DMA needs to be re-started
*/
void HAL_UART_ErrorCallback(UART_HandleTypeDef *uartHandle) {
#if defined(SERIAL_DEBUG) || defined(SERIAL_FEEDBACK)
if(uartHandle->Instance == USART2) {
HAL_UART_Receive_DMA (uartHandle, (uint8_t *)rx_buffer, sizeof(rx_buffer));
}
#endif
}
/* =========================== I2C WRITE Functions =========================== */
/*