BalancingRobot/Software/RTOS/rtos_app/rtos_app.c (659 lines of code) (raw):
#include <stdbool.h>
#include <math.h>
#include <stdint.h>
#include "i2c.h"
#include "mt3620-intercore.h"
#include "intercore_messages.h"
#include "os_hal_gpio.h"
#include "os_hal_uart.h"
#include "os_hal_pwm.h"
#include "os_hal_adc.h"
#include "printf.h"
#include "tx_api.h"
#include "utils.h"
#include "PID_v1.h"
#include "PID.h"
#include "FanOut.h"
#include "VL53L1X.h"
// Show Debug Log messages for Yaw/Pitch/Roll/Roll Delta
// #define SHOW_LOG
#define GLOBAL_KICK 0
#define IO_CTRL 0
#define POLARITY_SET 0
#define DEMO_STACK_SIZE 1024
#define DEMO_BYTE_POOL_SIZE 9120
#define DEMO_BLOCK_POOL_SIZE 100
#define DEMO_QUEUE_SIZE 100
#define PWM_PERIOD 1000
#define PID_RANGE 100
#define PWM_MULTIPLIER 10 // range is -250,250 - need to get to PWM duty cycle of 0-1000.
// amount to add/remove from setpoint when ToF is active
#define TOF_ADJUST 1.5
#define SETPOINT_ADJUST 0.01
#define SETPOINT_ADJUST_ANGLE 30
#define SETPOINT_ADJUST_TRIM 0.02
// distance where obstacles are detected
// we will still get a reading outside of this range
// these 'longer' readings are ignored.
#define TOF_OBSTACLE_DISTANCE_MM 100 // 100mm == ~4 inches from ToF sensor.
// used to turn ToF off when robot is initially balanced
// count for TOF_STABILIZE_PERIOD (value can be adjusted) before enabling ToF.
static unsigned long ToF_Settle_Counter = 0;
#define TOF_STABILIZE_PERIOD 500 // 500 * 5ms tick (2,500ms, [2.5 seconds]).
struct TURN_DETAILS details;
struct DEVICE_STATUS tMsg;
struct IMU_STABLE_RESULT tImuStatusMsg;
struct TURN_ROBOT tNorth;
int turnHeading = -1;
unsigned long ToF_ObstacleCounter = 0;
static bool updating = false; // set to true when update is accepted in the HL app.
// resources for inter core messaging
static uint8_t buf[64];
static uint32_t dataSize;
static BufferHeader* outbound, * inbound;
static uint32_t sharedBufSize = 0;
static const size_t payloadStart = 20;
bool highLevelReady = false;
static bool haveHLApp = false; // used to determine whether we've received a message from the HL app.
static const uint8_t HighLevelAppComponentId[16] = { 0x67, 0xc3, 0x5b, 0x88, 0x59, 0xb1, 0xa5, 0x44, 0x91, 0xfa, 0x3f, 0xeb, 0x53, 0xa8, 0x23, 0x17 };
// Define the ThreadX object control blocks...
TX_THREAD tx_hardware_init_thread;
TX_THREAD tx_timer_test_Thread;
TX_THREAD tx_hardware_Thread;
TX_THREAD tx_ToF_Thread;
TX_THREAD tx_Intercore_Thread;
TX_TIMER msTimer;
TX_EVENT_FLAGS_GROUP hardware_event_flags_0;
TX_EVENT_FLAGS_GROUP ToF_event_flags_0;
TX_EVENT_FLAGS_GROUP Intercore_event_flags_0;
TX_BYTE_POOL byte_pool_0;
TX_BLOCK_POOL block_pool_0;
UCHAR memory_area[DEMO_BYTE_POOL_SIZE];
void timerFn(ULONG input);
// Define thread prototypes.
void hardware_thread(ULONG thread_input);
void ToF_thread(ULONG thread_input);
void Intercore_thread(ULONG thread_input);
void hardware_init_thread(ULONG thread_input);
void EnqueueIntercoreMessage(void* payload, size_t payload_size);
int GetCompassDirection(float compassAngle);
static bool TurnRobotFlag = false;
static float turnStartHeading = 0.0;
//#define MOTOR_MULTIPLIER 1000/PID_RANGE
#define CUT_OFF_ANGLE 45
#define M_PI 3.14159265358979323846
// auto-calibration stuff.
#define SETPOINT_DEFAULT 90.54
static bool AutoCalibrateSetpoint = false;
// 5ms tick, 200 ticks (1 second) for auto-adjust.
static double setpoint = SETPOINT_DEFAULT;
static double CalibratedSetpoint = SETPOINT_DEFAULT;
// setpoint adjust values.
float ToFSetpointAdjust = 0.0;
float SpeedSetpointAdjust = 0.0;
float RemoteFwdBackAdjust = 0.0; // add/subtract to get the robot to move foreward/back.
int RemoteRotateCommand = 0; // 0 = none, 1 == left, 2 == right.
#define PID_KP 2.2f
#define PID_KI 0.5f
#define PID_KD 0.25f
#define PID_TAU 0.05f
#define SAMPLE_TIME_S 0.05f
#define PID_LIM_MIN -1.5f
#define PID_LIM_MAX 1.5f
PIDController pid = { PID_KP, PID_KI, -PID_KD,
PID_TAU,
PID_LIM_MIN, PID_LIM_MAX,
SAMPLE_TIME_S };
// used when turning, or moving in a specific direction
// 'turns off' auto-tuning of balance point.
static bool RotateClockwise = false; // used to determine the robot rotation direction (shortest rotation).
static bool ObstacleDetected = false;
static bool Tof_Active = false; // false if the robot is leaning more than CUT_OFF_ANGLE - don't read ToF lasers
static bool GetRotationDirection(float current, float desired);
// used to tweak the balance point.
#define SETPOINT_TWEAK_MAX 0.1
#define SETPOINT_TWEAK_MIN 0.005
bool applyAutoAdjust = true;
float setpointTweak = SETPOINT_TWEAK_MAX;
static float rollHistory[200];
static int rollHistoryPointer = 0;
static unsigned long loopCounter = 0;
static bool imuStable = false;
float oldOutput = 0.0;
// globals used in intercore-msgs.
float g_pitch, g_yaw, g_roll = 0.0;
float g_heading = 0.0;
double input, output = 0.0;
// offset to add to/subtract from motors to allow spin.
#define SPIN_OFFSET 150
double Kp = 16;
double Ki = 120;
double Kd = .4;
#define ACCELEROMETER_SENSITIVITY 8192.0
#define GYROSCOPE_SENSITIVITY 65.536
// from accel calibration.
float base_x_accel;
float base_y_accel;
float base_z_accel;
float base_x_gyro;
float base_y_gyro;
float base_z_gyro;
// Use the following global variables and access functions to help store the overall
// rotation angle of the sensor
unsigned long last_read_time;
float last_x_angle; // These are the filtered angles
float last_y_angle;
float last_z_angle;
float last_gyro_x_angle; // Store the gyro angles to compare drift
float last_gyro_y_angle;
float last_gyro_z_angle;
void calibrate_sensors();
void set_last_read_angle_data(unsigned long time, float x, float y, float z, float x_gyro, float y_gyro, float z_gyro);
static inline unsigned long get_last_time() { return last_read_time; }
static inline float get_last_x_angle() { return last_x_angle; }
static inline float get_last_y_angle() { return last_y_angle; }
static inline float get_last_z_angle() { return last_z_angle; }
static inline float get_last_gyro_x_angle() { return last_gyro_x_angle; }
static inline float get_last_gyro_y_angle() { return last_gyro_y_angle; }
static inline float get_last_gyro_z_angle() { return last_gyro_z_angle; }
static volatile bool hardwareInitOK = false;
bool InitHardware(void);
unsigned long loop(void);
unsigned long loop(void)
{
unsigned long startPeriod = millis();
// TODO: Read IMU, calculate Yaw, Pitch, Roll
// ICM-20948 oritentation gives Roll as lean angle.
float _Roll = 0.0; //
float g_heading = 0.0; //
if (g_heading < 0)
g_heading += 360;
loopCounter++; // counter to ensure we have 200 items in the rollHistory array.
// roll history is used to determine how stable the IMU readings are before unlocking the motors/telemetry
rollHistory[rollHistoryPointer] = _Roll;
rollHistoryPointer = (rollHistoryPointer + 1) % 200;
float min = 360.0;
float max = 0;
int rollHistoryCount = 0;
for (int x = 0; x < 200; x++)
{
if (rollHistory[x] != -1)
{
rollHistoryCount++;
if (rollHistory[x] < min)
min = rollHistory[x];
if (rollHistory[x] > max)
max = rollHistory[x];
}
}
// determine whether the IMU is stable.
if (!imuStable && rollHistoryCount == 200 && fabs(max - min) < 0.02)
{
imuStable = true;
}
// if the IMU is not stable, or the system is updating then return (motor control code after this point)
if (!imuStable || updating)
{
return 0;
}
// see if we're close to pointing north...
if (TurnRobotFlag)
{
// get a value from 0 to 15 (0 == north)
int OrientPos=GetCompassDirection(g_heading);
int TurnPos = GetCompassDirection(turnHeading);
if (OrientPos == TurnPos)
{
details.id = MSG_TURN_DETAILS;
details.startHeading = turnStartHeading;
details.endHeading = g_heading;
EnqueueIntercoreMessage(&details, sizeof(details));
TurnRobotFlag = false;
}
}
// set the PID input
input = _Roll;
float delta = fabs(_Roll - CalibratedSetpoint);
static unsigned long calibrationCounter = 0;
float setpointMotorAdjust = 0.0;
calibrationCounter++;
if (calibrationCounter == 10)
{
calibrationCounter = 0;
if (!AutoCalibrateSetpoint && !ObstacleDetected && _Roll > 80)
{
setpointMotorAdjust = PIDController_Update(&pid, 0, output/100.0);
SpeedSetpointAdjust = setpointMotorAdjust * -1;
}
}
setpoint = CalibratedSetpoint + ToFSetpointAdjust + SpeedSetpointAdjust + RemoteFwdBackAdjust;
Compute();
if (!TurnRobotFlag && !ObstacleDetected && _Roll > 80 && RemoteFwdBackAdjust == 0 && RemoteRotateCommand == 0)
{
if (output > 0)
{
CalibratedSetpoint += 0.005;
}
if (output < 0)
{
CalibratedSetpoint -= 0.005;
}
}
#ifdef SHOW_LOG
printf("Calibrated Setpoint %3.2f | Setpoint %3.2f (Tweak %3.2f) [AutoCalib %c | calibStarted %c | Tick %d] | Delta %3.2f | Output %3.2f\n", CalibratedSetpoint, setpoint, setpointTweak, AutoCalibrateSetpoint == true ? 'Y' : 'N', autoCalibrateStarted == true ? 'Y' : 'N', AutoCalibrationTickCounter, delta, output);
#endif
if (output == 0 || delta > CUT_OFF_ANGLE)
{
Tof_Active = false;
// turn off 'GPIO6 LED'
mtk_os_hal_pwm_config_freq_duty_normal(OS_HAL_PWM_GROUP1, PWM_CHANNEL2, PWM_PERIOD, 0);
ToF_Settle_Counter = 0;
// turn off motors.
mtk_os_hal_gpio_set_output(OS_HAL_GPIO_0, 0);
mtk_os_hal_gpio_set_output(OS_HAL_GPIO_1, 0);
mtk_os_hal_gpio_set_output(OS_HAL_GPIO_2, 0);
mtk_os_hal_gpio_set_output(OS_HAL_GPIO_12, 0);
mtk_os_hal_gpio_set_output(OS_HAL_GPIO_13, 0);
mtk_os_hal_pwm_config_freq_duty_normal(OS_HAL_PWM_GROUP1, PWM_CHANNEL0, PWM_PERIOD, 0);
mtk_os_hal_pwm_config_freq_duty_normal(OS_HAL_PWM_GROUP1, PWM_CHANNEL1, PWM_PERIOD, 0);
}
else
{
if (Tof_Active == false)
{
ToF_Settle_Counter++;
if (ToF_Settle_Counter == TOF_STABILIZE_PERIOD)
{
ToF_Settle_Counter = 0;
Tof_Active = true;
// turn on 'GPIO6 LED'
// mtk_os_hal_pwm_config_freq_duty_normal(OS_HAL_PWM_GROUP1, PWM_CHANNEL2, PWM_PERIOD, 1000);
}
}
unsigned long duty = abs(output)*PWM_MULTIPLIER;
unsigned long dutyLeft = duty;
unsigned long dutyRight = duty;
static bool Tick = false;
// add turning value.
if ((TurnRobotFlag || RemoteRotateCommand != 0) && Tick && !ObstacleDetected)
{
if (TurnRobotFlag || RemoteRotateCommand)
{
if (output > 0)
{
if (!RotateClockwise)
{
dutyLeft = (duty + SPIN_OFFSET) % 1000;
}
else
{
dutyRight = (duty + SPIN_OFFSET) % 1000;
}
}
if (output < 0)
{
if (!RotateClockwise)
{
dutyRight = (duty + SPIN_OFFSET) % 1000;
}
else
{
dutyLeft = (duty + SPIN_OFFSET) % 1000;
}
}
}
}
if (output > 0)
{
mtk_os_hal_gpio_set_output(OS_HAL_GPIO_0, 0);
mtk_os_hal_gpio_set_output(OS_HAL_GPIO_1, 1);
mtk_os_hal_gpio_set_output(OS_HAL_GPIO_2, 1);
mtk_os_hal_gpio_set_output(OS_HAL_GPIO_12, 0);
}
else
{
mtk_os_hal_gpio_set_output(OS_HAL_GPIO_0, 1);
mtk_os_hal_gpio_set_output(OS_HAL_GPIO_1, 0);
mtk_os_hal_gpio_set_output(OS_HAL_GPIO_2, 0);
mtk_os_hal_gpio_set_output(OS_HAL_GPIO_12, 1);
}
Tick = !Tick;
mtk_os_hal_gpio_set_output(OS_HAL_GPIO_13, 1);
mtk_os_hal_pwm_config_freq_duty_normal(OS_HAL_PWM_GROUP1, PWM_CHANNEL0, PWM_PERIOD, dutyLeft);
mtk_os_hal_pwm_config_freq_duty_normal(OS_HAL_PWM_GROUP1, PWM_CHANNEL1, PWM_PERIOD, dutyRight);
}
unsigned long timePeriod = millis() - startPeriod;
return timePeriod;
}
bool InitHardware(void)
{
// Initialize I2C (IMU)
mtk_os_hal_i2c_ctrl_init(OS_HAL_I2C_ISU0);
mtk_os_hal_i2c_speed_init(OS_HAL_I2C_ISU0, I2C_SCL_400kHz);
// Initialize I2C (ToF Sensors)
mtk_os_hal_i2c_ctrl_init(OS_HAL_I2C_ISU1);
mtk_os_hal_i2c_speed_init(OS_HAL_I2C_ISU1, I2C_SCL_400kHz);
// Motor control.
mtk_os_hal_gpio_set_direction(OS_HAL_GPIO_0, OS_HAL_GPIO_DIR_OUTPUT);
mtk_os_hal_gpio_set_direction(OS_HAL_GPIO_1, OS_HAL_GPIO_DIR_OUTPUT);
mtk_os_hal_gpio_set_direction(OS_HAL_GPIO_2, OS_HAL_GPIO_DIR_OUTPUT);
mtk_os_hal_gpio_set_direction(OS_HAL_GPIO_12, OS_HAL_GPIO_DIR_OUTPUT);
// ToF Fanout chip
mtk_os_hal_gpio_set_direction(OS_HAL_GPIO_16, OS_HAL_GPIO_DIR_OUTPUT);
// Motor Driver Enable.
mtk_os_hal_gpio_set_direction(OS_HAL_GPIO_13, OS_HAL_GPIO_DIR_OUTPUT);
// set initial state - all low.
mtk_os_hal_gpio_set_output(OS_HAL_GPIO_0, 0);
mtk_os_hal_gpio_set_output(OS_HAL_GPIO_1, 0);
mtk_os_hal_gpio_set_output(OS_HAL_GPIO_2, 0);
mtk_os_hal_gpio_set_output(OS_HAL_GPIO_12, 0);
// Motor Controller (initially off)
mtk_os_hal_gpio_set_output(OS_HAL_GPIO_13, 0);
// ToF Fanout - turned on.
mtk_os_hal_gpio_set_output(OS_HAL_GPIO_16, 1);
// configure PWM
mtk_os_hal_pwm_ctlr_init(OS_HAL_PWM_GROUP1, PWM_CHANNEL0 | PWM_CHANNEL1 | PWM_CHANNEL2);
mtk_os_hal_pwm_feature_enable(OS_HAL_PWM_GROUP1,PWM_CHANNEL0,GLOBAL_KICK,IO_CTRL,POLARITY_SET);
mtk_os_hal_pwm_feature_enable(OS_HAL_PWM_GROUP1, PWM_CHANNEL1, GLOBAL_KICK, IO_CTRL, POLARITY_SET);
mtk_os_hal_pwm_feature_enable(OS_HAL_PWM_GROUP1, PWM_CHANNEL2, GLOBAL_KICK, IO_CTRL, POLARITY_SET);
mtk_os_hal_pwm_config_freq_duty_normal(OS_HAL_PWM_GROUP1,PWM_CHANNEL0, PWM_PERIOD,0);
mtk_os_hal_pwm_config_freq_duty_normal(OS_HAL_PWM_GROUP1, PWM_CHANNEL1, PWM_PERIOD, 0);
mtk_os_hal_pwm_config_freq_duty_normal(OS_HAL_PWM_GROUP1, PWM_CHANNEL2, PWM_PERIOD, 0);
mtk_os_hal_pwm_start_normal(OS_HAL_PWM_GROUP1,PWM_CHANNEL0);
mtk_os_hal_pwm_start_normal(OS_HAL_PWM_GROUP1, PWM_CHANNEL1);
mtk_os_hal_pwm_start_normal(OS_HAL_PWM_GROUP1, PWM_CHANNEL2);
// TODO: Initialize the IMU here
PID(&input, &output, &setpoint, Kp, Ki, Kd, DIRECT);
SetMode(AUTOMATIC);
SetSampleTime(5);
SetOutputLimits(-PID_RANGE, PID_RANGE); // (-100,100);
return true;
}
void ToF_thread(ULONG thread_input)
{
printf("Initialize ToF\r\n");
SelectFanoutChannel(1);
VL53L1X_setActiveLaser(1); // used to track which has been configued.
bool initOk = VL53L1X_init(true);
if (!initOk)
{
printf("ToF Channel 1 failed\r\n");
return;
}
VL53L1X_setDistanceMode(Long);
VL53L1X_setMeasurementTimingBudget(50000);
// Start continuous readings at a rate of one measurement every 50 ms (the
// inter-measurement period). This period should be at least as long as the
// timing budget.
VL53L1X_startContinuous(50);
delay(2);
SelectFanoutChannel(2);
VL53L1X_setActiveLaser(1); // used to track which has been configued.
initOk = VL53L1X_init(true);
if (!initOk)
{
printf("ToF Channel 2 failed\r\n");
return;
}
VL53L1X_setDistanceMode(Long);
VL53L1X_setMeasurementTimingBudget(50000);
// Start continuous readings at a rate of one measurement every 50 ms (the
// inter-measurement period). This period should be at least as long as the
// timing budget.
VL53L1X_startContinuous(50);
SelectFanoutChannel(1);
bool useFrontToF = true; // TODO: tick/tock on front/rear sensors.
ULONG actual_flags = 0;
uint16_t distances[2]; // front and rear lasers.
// setup last distances to be 'far'.
uint16_t lastDistances[2] = { 2400,2400 };
bool obstacles[2] = { false, false };
while (true)
{
// wait on timer tick.
tx_event_flags_get(&ToF_event_flags_0, 0x1, TX_OR_CLEAR, &actual_flags, TX_WAIT_FOREVER);
if (Tof_Active)
{
int index = useFrontToF == true ? 0 : 1;
distances[index] = VL53L1X_read(true);
if (distances[index] != 0)
{
// Log_Debug("ToF Distance %d\r\n", distance);
if (distances[index] < TOF_OBSTACLE_DISTANCE_MM && distances[index] > 0 && !obstacles[index])
{
mtk_os_hal_pwm_config_freq_duty_normal(OS_HAL_PWM_GROUP1, PWM_CHANNEL2, PWM_PERIOD, 1000);
// Log_Debug("Obstacle Found - adjusting setpoint\n");
obstacles[index] = true;
ObstacleDetected = true;
ToF_ObstacleCounter++;
if (useFrontToF)
{
ToFSetpointAdjust = -1.5;
}
else
{
ToFSetpointAdjust = 1.5;
}
}
// see if we're moving away, and if yes, cancel the movement.
if (obstacles[index] && lastDistances[index] < distances[index] && distances[index] > 0) //distances[index] > 100 && obstacles[index])
{
mtk_os_hal_pwm_config_freq_duty_normal(OS_HAL_PWM_GROUP1, PWM_CHANNEL2, PWM_PERIOD, 0);
obstacles[index] = false;
ObstacleDetected = false;
ToFSetpointAdjust = 0.0;
}
}
lastDistances[index] = distances[index];
useFrontToF = !useFrontToF;
SelectFanoutChannel(useFrontToF == true ? 1 : 2);
}
}
printf("ToF Thread exit\r\n");
}
void Intercore_thread(ULONG thread_input)
{
ULONG actual_flags = 0;
printf("Intercore Thread Starting\r\n");
struct TURN_ROBOT* pTurn;
struct SETPOINT* pSetpoint;
struct REMOTE_CMD* pRemote;
struct UPDATE_ACTIVE* pUpdate;
while (true)
{
// wait on timer tick.
tx_event_flags_get(&Intercore_event_flags_0, 0x1, TX_OR_CLEAR, &actual_flags, TX_WAIT_FOREVER);
dataSize = sizeof(buf);
int r = DequeueData(outbound, inbound, sharedBufSize, buf, &dataSize);
if (r == 0 && dataSize > payloadStart) {
size_t payloadBytes = dataSize - payloadStart;
if (payloadBytes > 0) {
switch (buf[payloadStart]) {
case MSG_UPDATE_ACTIVE:
pUpdate = (struct UPDATE_ACTIVE*)&buf[payloadStart];
if (pUpdate->updateActive)
{
updating = true;
}
else
{
updating = false;
}
break;
case MSG_REMOTE_CMD:
pRemote=(struct REMOTE_CMD*)&buf[payloadStart];
switch (pRemote->cmd)
{
case 4: // stop.
RemoteFwdBackAdjust = 0.0;
RemoteRotateCommand = 0;
break;
case 1: // foreward
RemoteFwdBackAdjust = 1.5;
RemoteRotateCommand = 0;
break;
case 3: // back
RemoteFwdBackAdjust = -1.5;
RemoteRotateCommand = 0;
break;
case 2: // right
RemoteFwdBackAdjust = 0.0;
RotateClockwise = false;
RemoteRotateCommand = 2; // right
break;
case 0: // left
RemoteFwdBackAdjust = 0.0;
RotateClockwise = true;
RemoteRotateCommand = 1; // left.
break;
default:
break;
};
break;
case MSG_IMU_STABLE_REQUEST:
tImuStatusMsg.id = MSG_IMU_STABLE_RESULT;
tImuStatusMsg.imuStable = imuStable;
EnqueueIntercoreMessage(&tImuStatusMsg, sizeof(tImuStatusMsg));
break;
case MSG_TELEMETRY_REQUEST:
haveHLApp = true; // have received at least one message from the HL App, can now start sending IMU telemetry.
tMsg.id = MSG_DEVICE_STATUS;
tMsg.timestamp = millis();
tMsg.numObstaclesDetected = ToF_ObstacleCounter;
tMsg.setpoint = CalibratedSetpoint;
tMsg.pitch = g_pitch;
tMsg.yaw = g_heading;
tMsg.roll = g_roll;
tMsg.turnNorth = TurnRobotFlag;
tMsg.avoidActive = ObstacleDetected;
EnqueueIntercoreMessage(&tMsg, sizeof(tMsg));
break;
case MSG_SETPOINT:
pSetpoint = (struct SETPOINT*)&buf[payloadStart];
if (pSetpoint->setpoint > 80 && pSetpoint->setpoint < 100)
{
CalibratedSetpoint = pSetpoint->setpoint;
}
break;
case MSG_TURN_ROBOT:
pTurn = (struct TURN_ROBOT*)&buf[payloadStart];
// accept the turn if we're stood up.
if (g_roll > 80)
{
if (pTurn->enabled)
{
RotateClockwise = GetRotationDirection(g_heading, pTurn->heading);
}
turnHeading = pTurn->heading;
TurnRobotFlag = pTurn->enabled;
if (TurnRobotFlag)
{
// store current heading (used in telemetry).
turnStartHeading = g_heading;
}
}
break;
};
}
}
}
printf("Intercore Thread exit\r\n");
}
static bool GetRotationDirection(float current, float desired)
{
bool increment = false;
if (current < desired)
{
if (fabs(current - desired) < 180)
increment = true;
else
increment = false;
}
else
{
if (fabs(current - desired) < 180)
increment = false;
else
increment = true;
}
return increment;
}
void EnqueueIntercoreMessage(void* payload, size_t payload_size)
{
uint8_t sendbuf[64];
if ((payloadStart + payload_size) > sizeof(sendbuf)) {
printf("EnqueueIntercoreMessage insufficient buffer\n");
return;
}
memset(sendbuf, 0x00, 64);
memcpy(sendbuf, HighLevelAppComponentId, sizeof(HighLevelAppComponentId));
memcpy(&sendbuf[payloadStart], payload, payload_size);
int result = EnqueueData(inbound, outbound, sharedBufSize, sendbuf, payloadStart + payload_size);
if (result != 0)
{
printf("Unable to queue intercore data\r\n");
}
}
int GetCompassDirection(float compassAngle)
{
int pos = (int)((compassAngle / 22.5) + .5);
pos = pos % 16;
return pos;
}
void hardware_thread(ULONG thread_input)
{
ULONG actual_flags = 0;
#ifdef SHOW_DEBUG_MSGS
unsigned long last = 0;
unsigned long now = 0;
unsigned long delta = 0;
unsigned long loopTime = 0;
#endif
printf("hardware thread starting...\r\n");
while (true)
{
tx_event_flags_get(&hardware_event_flags_0, 0x1, TX_OR_CLEAR, &actual_flags, TX_WAIT_FOREVER);
#ifdef SHOW_DEBUG_MSGS
loopTime = loop();
now = millis();
delta = now - last;
last = now;
printf("%u | %u | %s - Loop: %u\r\n",now, delta, delta > 5 ? "***" : " ",loopTime);
#else
loop();
#endif
}
}
void timerFn(ULONG input)
{
static unsigned long IntercoreTickCounter = 0;
static unsigned long ToFTickCounter = 0;
ULONG status = TX_SUCCESS;
if (hardwareInitOK == true)
{
status = tx_event_flags_set(&hardware_event_flags_0, 0x1, TX_OR);
if (status != TX_SUCCESS)
{
printf("failed to set hardware event flags\r\n");
}
ToFTickCounter++;
if (ToFTickCounter == 20) // 100ms
{
ToFTickCounter=0;
status = tx_event_flags_set(&ToF_event_flags_0, 0x1, TX_OR);
if (status != TX_SUCCESS)
{
printf("failed to set ToF event flags\r\n");
}
}
// Azure HL App 'Device Twin' check is every 5 seconds
// Request for Telemetry is every 20 seconds.
IntercoreTickCounter++;
if (IntercoreTickCounter == 100) // 0.5 seconds.
{
IntercoreTickCounter = 0;
status = tx_event_flags_set(&Intercore_event_flags_0, 0x1, TX_OR);
if (status != TX_SUCCESS)
{
printf("failed to set Intercore event flags\r\n");
}
}
}
}
// only purpose in life is to initialize the hardware.
void hardware_init_thread(ULONG thread_input)
{
printf("Hardware Init Thread - start: %u\r\n", millis());
UINT status = TX_SUCCESS;
bool hwInitOk = InitHardware();
if (hwInitOk)
{
hardwareInitOK = true;
status = tx_timer_create(&msTimer, "5ms Timer", timerFn, 0, 5, 5, TX_AUTO_ACTIVATE);
if (status != TX_SUCCESS)
{
printf("failed to create timer\r\n");
}
else
{
printf("timer created ok\r\n");
}
}
printf("Hardware Init - %s\r\n", hardwareInitOK ? "OK" : "FAIL");
}
int main() {
tx_kernel_enter(); // Enter the Azure RTOS kernel.
}
// Define what the initial system looks like.
void tx_application_define(void* first_unused_memory) {
CHAR* pointer;
UINT status = TX_SUCCESS;
//volatile bool f = false;
//while (!f) {
// // empty.
//}
printf("%c[2J%c[0;0HAzure Sphere Robot App\r\n", 0x1b, 0x1b);
if (GetIntercoreBuffers(&outbound, &inbound, &sharedBufSize) == -1) { // Initialize Inter-Core Communications
return; // don't let the intercore thread run.
}
/* Create a byte memory pool from which to allocate the thread stacks. */
tx_byte_pool_create(&byte_pool_0, "byte pool 0", memory_area, DEMO_BYTE_POOL_SIZE);
// create event flags
status = tx_event_flags_create(&hardware_event_flags_0, "Hardware Event"); // Hardware events fire every 5 ms
if (status != TX_SUCCESS)
{
printf("failed to create hardware_event_flags\r\n");
}
status = tx_event_flags_create(&ToF_event_flags_0, "ToF Event"); // ToF events fire every 100 ms
if (status != TX_SUCCESS)
{
printf("failed to create ToF_event_flags\r\n");
}
status = tx_event_flags_create(&Intercore_event_flags_0, "Intercore Event"); // Intercore events fire every 10 ms
if (status != TX_SUCCESS)
{
printf("failed to create Intercore_event_flags\r\n");
}
/* Allocate the stack for thread 0. */
tx_byte_allocate(&byte_pool_0, (VOID**)&pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
/* Create the main thread. */
tx_thread_create(&tx_hardware_Thread, "hardware thread", hardware_thread, 0,
pointer, DEMO_STACK_SIZE, 1, 1, TX_NO_TIME_SLICE, TX_AUTO_START);
tx_byte_allocate(&byte_pool_0, (VOID**)&pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
/* Create the Time of Flight thread. */
tx_thread_create(&tx_ToF_Thread, "ToF Thread", ToF_thread, 0,
pointer, DEMO_STACK_SIZE, 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START);
tx_byte_allocate(&byte_pool_0, (VOID**)&pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
/* Create the intercore msg thread. */
tx_thread_create(&tx_Intercore_Thread, "Intercore Thread", Intercore_thread, 0,
pointer, DEMO_STACK_SIZE, 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START);
tx_byte_allocate(&byte_pool_0, (VOID**)&pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
// Create a hardware init thread.
tx_thread_create(&tx_hardware_init_thread, "hardware init thread", hardware_init_thread, 0,
pointer, DEMO_STACK_SIZE, 1, 1, TX_NO_TIME_SLICE, TX_AUTO_START);
}