1390 lines
48 KiB
C
1390 lines
48 KiB
C
/* RTOS_Lab4.c
|
||
* Jonathan Valvano
|
||
* December 30, 2025
|
||
* Remove 3.3V J101 jumper to run RTOS sensor board or motor board
|
||
* A two-pin female header is required on the LaunchPad TP10(XDS_VCC) and TP9(!RSTN)
|
||
*/
|
||
|
||
|
||
// Overtake Parameters
|
||
#define FRONT_DETECT 1200
|
||
#define D2_MIN_PASS 250
|
||
#define LD2_MIN_PASS 250
|
||
#define V_MAX_MMPS 1140 // TBD via calibration
|
||
#define V_ALPHA_Q8 77
|
||
#define R_ACC_ALPHA_Q8 205
|
||
#define R_THRESH_BASE 150
|
||
#define R_YAW_GAIN_Q8 77
|
||
#define R_CAP 2000
|
||
#define OFFSET_CMD 180
|
||
#define OFFSET_RATE 20
|
||
#define GZ_RAMP_GATE 500
|
||
#define PASS_CLEAR 900
|
||
#define T_CLEAR_MS 500
|
||
#define T_PASS_MIN 400
|
||
#define T_SETTLE 300
|
||
#define ABORT_SIDE 150
|
||
#define ABORT_GZ 1200
|
||
#define ABORT_DF 300 // guessed value
|
||
|
||
#define CALIB_THROTTLE 9500
|
||
|
||
// PD weights
|
||
#define kp_d 1
|
||
#define kd_d 2
|
||
#define kp_a 5
|
||
#define kd_a 2
|
||
|
||
#include <ti/devices/msp/msp.h>
|
||
#include "../inc/LaunchPad.h"
|
||
#include "../RTOS_Labs_common/ADC.h"
|
||
#include "../inc/Clock.h"
|
||
#include "../RTOS_Labs_common/ST7735_SDC.h"
|
||
#include "../RTOS_Labs_common/RTOS_UART.h"
|
||
#include "../RTOS_Labs_common/Interpreter.h"
|
||
#include "../RTOS_Labs_common/IRDistance.h"
|
||
#include "../RTOS_Labs_common/LPF.h"
|
||
#include "../RTOS_Labs_common/DFT16.h"
|
||
#include "../RTOS_Labs_common/TFLuna1.h"
|
||
#include "../RTOS_Labs_common/TFLuna2.h"
|
||
#include "../RTOS_Labs_common/TFLuna3.h"
|
||
#include "../RTOS_Labs_common/OS.h"
|
||
#include "../RTOS_Labs_common/eDisk.h"
|
||
#include "../RTOS_Labs_common/eFile.h"
|
||
#include "../RTOS_Labs_common/CAN.h"
|
||
#include "Model.h"
|
||
#include "IMU.h"
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
|
||
|
||
#define USE_MEDIAN_FILTER 0 // 1 = median (Median5 per sample, 5-sample pace), 0 = mean (8-sample average)
|
||
|
||
#define angle_ref 5 // degrees "0"
|
||
|
||
int32_t dist_ref_cur = 0; // mm
|
||
|
||
// PA10 is UART0 Tx index 20 in IOMUX PINCM table
|
||
// PA11 is UART0 Rx index 21 in IOMUX PINCM table
|
||
// Insert jumper J25: Connects PA10 to XDS_UART
|
||
// Insert jumper J26: Connects PA11 to XDS_UART
|
||
// PA0 is red LED1, index 0 in IOMUX PINCM table, negative logic
|
||
// PB22 is BLUE LED2, index 49 in IOMUX PINCM table
|
||
// PB26 is RED LED2, index 56 in IOMUX PINCM table
|
||
// PB27 is GREEN LED2, index 57 in IOMUX PINCM table
|
||
// PA18 is S1 positive logic switch, conflict with TFLuna1, so S1 will not be used
|
||
// PB21 is S2 negative logic switch, used for aperiodic task
|
||
// IR analog distance sensors
|
||
// 30 cm GP2Y0A41SK0F or 80 cm long range GP2Y0A21YK0F
|
||
// PA26 Right ADC0_1
|
||
// PA24 Center ADC0_3, used in Labs 1,2,3,4
|
||
// PA22 Left ADC0_7
|
||
// PA27 Extra ADC0_0
|
||
|
||
// RTOS sensor board supported three TF-Luna sensors
|
||
// Serial TxD: PA17 is UART1 Tx (MSPM0 to TFLuna1)
|
||
// Serial RxD: PA18 is UART1 Rx (TFLuna1 to MSPM0), conflict with LaunchPad S1
|
||
// Serial TxD: PB17 is UART2 Tx (MSPM0 to TFLuna2), used in Labs 1,2,3,4
|
||
// Serial RxD: PB18 is UART2 Rx (TFLuna2 to MSPM0), used in Labs 1,2,3,4
|
||
// Serial TxD: PB12 is UART3 Tx (MSPM0 to TFLuna3),
|
||
// Serial RxD: PB13 is UART3 Rx (TFLuna3 to MSPM0), shared with LD19 Lidar
|
||
//UART3 is shared between LD19 and TFLuna3 (can have either but not both)
|
||
|
||
// **** OS must run disk_timerproc(); at 1000Hz, every 1ms *****
|
||
long StartCritical(void);
|
||
void EndCritical(long sr);
|
||
|
||
uint32_t Running; // true while robot is running
|
||
uint32_t NumCreated; // number of foreground threads created
|
||
|
||
//---------------------User debugging-----------------------
|
||
|
||
// Unused sensor board pins, made outputs for debugging
|
||
// Jumper J14 select PB23
|
||
// Jumper J15 select PA16
|
||
void Logic_Init(void){
|
||
IOMUX->SECCFG.PINCM[PA8INDEX] = (uint32_t) 0x00000081;
|
||
IOMUX->SECCFG.PINCM[PB23INDEX] = (uint32_t) 0x00000081; //****CHANGE from PA9****
|
||
IOMUX->SECCFG.PINCM[PA16INDEX] = (uint32_t) 0x00000081;
|
||
IOMUX->SECCFG.PINCM[PB4INDEX] = (uint32_t) 0x00000081;
|
||
IOMUX->SECCFG.PINCM[PB1INDEX] = (uint32_t) 0x00000081;
|
||
IOMUX->SECCFG.PINCM[PB20INDEX] = (uint32_t) 0x00000081;
|
||
GPIOA->DOE31_0 |= (1<<8)|(1<<16); //****CHANGE removing PA9****
|
||
GPIOB->DOE31_0 |= (1<<4)|(1<<1)|(1<<20)|(1<<23);//****CHANGE adding PB23****
|
||
}
|
||
#define TogglePA8() (GPIOA->DOUTTGL31_0 = (1<<8))
|
||
#define SetPA8() (GPIOA->DOUTSET31_0 = (1<<8))
|
||
#define ClrPA8() (GPIOA->DOUTCLR31_0 = (1<<8))
|
||
#define TogglePB23() (GPIOB->DOUTTGL31_0 = (1<<23))
|
||
#define SetPB23() (GPIOB->DOUTSET31_0 = (1<<23))
|
||
#define ClrPB23() (GPIOB->DOUTCLR31_0 = (1<<23))
|
||
#define TogglePA16() (GPIOA->DOUTTGL31_0 = (1<<16))
|
||
#define TogglePB4() (GPIOB->DOUTTGL31_0 = (1<<4))
|
||
#define SetPB4() (GPIOB->DOUTSET31_0 = (1<<4))
|
||
#define ClrPB4() (GPIOB->DOUTCLR31_0 = (1<<4))
|
||
#define TogglePB1() (GPIOB->DOUTTGL31_0 = (1<<1))
|
||
#define TogglePB20() (GPIOB->DOUTTGL31_0 = (1<<20))
|
||
|
||
//--------------Arctan Lookup Table-----------------------
|
||
// TanTable[i] = tan(i degrees) * 1000, for i = 0 to 89
|
||
const uint32_t TanTable[90] = {
|
||
0, 17, 35, 52, 70, 87, 105, 123, 141, 158, // 0-9
|
||
176, 194, 213, 231, 249, 268, 287, 306, 325, 344, // 10-19
|
||
364, 384, 404, 424, 445, 466, 488, 510, 532, 554, // 20-29
|
||
577, 601, 625, 649, 675, 700, 727, 754, 781, 810, // 30-39
|
||
839, 869, 900, 933, 966, 1000, 1036, 1072, 1111, 1150, // 40-49
|
||
1192, 1235, 1280, 1327, 1376, 1428, 1483, 1540, 1600, 1664, // 50-59
|
||
1732, 1804, 1881, 1963, 2050, 2145, 2246, 2356, 2475, 2605, // 60-69
|
||
2747, 2904, 3078, 3271, 3487, 3732, 4011, 4331, 4705, 5145, // 70-79
|
||
5671, 6314, 7115, 8144, 9514,11430,14300,19081,28636,57290 // 80-89
|
||
};
|
||
// arctan: inverse tangent using table lookup and binary search
|
||
// Input: ratio * 1000 (e.g., 1000 for ratio=1.0, -500 for ratio=-0.5)
|
||
// Output: angle in degrees (-89 to 89)
|
||
// Usage: angle = arctan((opposite * 1000) / adjacent);
|
||
int32_t arctan(int32_t ratio_x1000) {
|
||
uint32_t abs_ratio = (ratio_x1000 < 0) ? (uint32_t)(-ratio_x1000) : (uint32_t)ratio_x1000;
|
||
uint32_t lo = 0, hi = 88;
|
||
if (abs_ratio >= TanTable[89]) {
|
||
return (ratio_x1000 < 0) ? -89 : 89;
|
||
}
|
||
while (lo + 1 < hi) {
|
||
uint32_t mid = (lo + hi) / 2;
|
||
if (TanTable[mid] <= abs_ratio) lo = mid;
|
||
else hi = mid;
|
||
}
|
||
return (ratio_x1000 < 0) ? -(int32_t)lo : (int32_t)lo;
|
||
}
|
||
// tangent: tangent of angle using TanTable (same table as arctan)
|
||
// Input: angle in degrees
|
||
// Output: tan(angle) * 1000; returns 57290 (max) for angles near 90/270 (undefined)
|
||
// tan has period 180: sign positive in [0,89] and [180,269], negative in [91,179] and [271,359]
|
||
int32_t tangent(int32_t angle_deg) {
|
||
while (angle_deg < 0) angle_deg += 360;
|
||
while (angle_deg >= 360) angle_deg -= 360;
|
||
uint32_t mod = (uint32_t)angle_deg % 180;
|
||
if (mod <= 89) return (int32_t)TanTable[mod];
|
||
if (mod == 90) return 57290; // clamp — approaches +∞ before, -∞ after
|
||
return -(int32_t)TanTable[180 - mod]; // mod 91–179: negative, use symmetry
|
||
}
|
||
//-----------end of Arctan Lookup Table--------------------
|
||
|
||
//--------------Cosine Lookup Table-----------------------
|
||
// CosTable[i] = cos(i degrees) * 1000, for i = 0 to 90
|
||
const uint32_t CosTable[91] = {
|
||
1000, 999, 999, 998, 997, 996, 994, 992, 990, 987, // 0-9
|
||
984, 981, 978, 974, 970, 965, 961, 956, 951, 945, // 10-19
|
||
939, 933, 927, 920, 913, 906, 898, 891, 882, 874, // 20-29
|
||
866, 857, 848, 838, 829, 819, 809, 798, 788, 777, // 30-39
|
||
766, 754, 743, 731, 719, 707, 694, 681, 669, 656, // 40-49
|
||
642, 629, 615, 601, 587, 573, 559, 544, 529, 515, // 50-59
|
||
500, 484, 469, 453, 438, 422, 406, 390, 374, 358, // 60-69
|
||
342, 325, 309, 292, 276, 258, 242, 224, 208, 190, // 70-79
|
||
173, 156, 139, 121, 104, 87, 69, 52, 35, 17, // 80-89
|
||
0 // 90
|
||
};
|
||
// cosine: cosine of angle using table lookup
|
||
// Input: angle in degrees (-180 to 180)
|
||
// Output: cos(angle) * 1000 (e.g., 1000 for 0 deg, 0 for 90 deg, -1000 for 180 deg)
|
||
int32_t cosine(int32_t angle_deg) {
|
||
// Normalize to [0, 360)
|
||
while (angle_deg < 0) angle_deg += 360;
|
||
while (angle_deg >= 360) angle_deg -= 360;
|
||
// Use symmetry: cos is even and periodic
|
||
if (angle_deg <= 90) return (int32_t)CosTable[angle_deg];
|
||
if (angle_deg <= 180) return -(int32_t)CosTable[180 - angle_deg];
|
||
if (angle_deg <= 270) return -(int32_t)CosTable[angle_deg - 180];
|
||
return (int32_t)CosTable[360 - angle_deg];
|
||
}
|
||
//-----------end of Cosine Lookup Table--------------------
|
||
|
||
uint32_t Checks; // number of times virus checking has run
|
||
uint32_t ChecksWork; // number of checks in 10 second
|
||
//------------------Task 1--------------------------------
|
||
// real-time sampling ADC0 channel 3, using software start trigger
|
||
// 60-Hz notch high-Q, IIR filter, assuming fs=1000 Hz
|
||
// y(n) = (256x(n) -476x(n-1) + 256x(n-2) + 471y(n-1)-251y(n-2))/256 (1k sampling)
|
||
#define PERIOD TIME_1MS // DAS 1kHz sampling period in system time units
|
||
#define FS 1000 // DAS sampling
|
||
#define RUNLENGTH (10000) // display results and quit when FilterWork==RUNLENGTH
|
||
|
||
uint32_t FilterOutput, Distance, DistanceRaw;
|
||
uint32_t L_FilterOutput, L_Distance, L_DistanceRaw;
|
||
uint32_t L_Distance2;
|
||
uint32_t FrontDist = 1000; // mm, from TFLuna1 (front-facing); default far
|
||
Sema4_t LCDFree; // SDC and LCD sharing
|
||
|
||
uint32_t FilterWork;
|
||
uint32_t MaxJitter3;
|
||
#define JITTERSIZE3 512
|
||
uint32_t const JitterSize3=JITTERSIZE3;
|
||
uint32_t JitterHistogram3[JITTERSIZE3]={0,};
|
||
void Jitter3_Init(void){
|
||
for(int i=0;i<JitterSize3;i++){
|
||
JitterHistogram3[i] = 0;
|
||
}
|
||
MaxJitter3 = 0;
|
||
}
|
||
//******** DAS ***************
|
||
// background thread, calculates 60Hz notch filter
|
||
// runs 1000 times/sec
|
||
// samples PA24 Center ADC0_3, calculates Distance
|
||
// inputs: none
|
||
// outputs: none
|
||
void DAS(void){
|
||
uint32_t input, L_input;
|
||
static uint32_t LastTime; // time at previous ADC sample, 12.5 ns
|
||
uint32_t thisTime; // time at current ADC sample, 12.5 ns
|
||
uint32_t jitter; // time between measured and expected, 12.5 ns
|
||
TogglePA8(); // toggle PA8
|
||
ADC_InDual(ADC0, &input, &L_input); // ch1=right IR (PA26), ch7=left IR (PA22)
|
||
TogglePA8(); // toggle PA8
|
||
thisTime = OS_Time(); // current time, 12.5 ns
|
||
FilterOutput = Filter(input);
|
||
DistanceRaw = FilterOutput;
|
||
Distance = IRDistance_Right(FilterOutput); // in mm
|
||
L_FilterOutput = Filter2(L_input);
|
||
L_DistanceRaw = L_FilterOutput;
|
||
L_Distance = IRDistance_Left(L_FilterOutput); // in mm
|
||
if(Running){ // finite time run
|
||
FilterWork++; // calculation finished
|
||
if(FilterWork>2){ // ignore timing of first interrupt
|
||
uint32_t diff = OS_TimeDifference(LastTime,thisTime);
|
||
if(diff>PERIOD){
|
||
jitter = (diff-PERIOD); // in 12.5 ns
|
||
}else{
|
||
jitter = (PERIOD-diff); // in 12.5 ns
|
||
}
|
||
if(jitter > MaxJitter3){
|
||
MaxJitter3 = jitter; // in 12.5 ns
|
||
} // jitter should be 0
|
||
if (jitter > JITTERSIZE3-1) jitter = JITTERSIZE3-1; // clamp
|
||
JitterHistogram3[jitter]++;
|
||
}
|
||
ChecksWork = Checks;
|
||
LastTime = thisTime;
|
||
}
|
||
TogglePA8(); // toggle PA8
|
||
}
|
||
//--------------end of Task 1-----------------------------
|
||
|
||
//------------------Task 2--------------------------------
|
||
// background thread executes with PA28 button
|
||
// PA28 negative logic switch
|
||
// one foreground task created with each button push
|
||
// foreground tread outputs a message and dies
|
||
uint32_t DataLost; // data sent by Producer, but not received by Consumer
|
||
|
||
// ***********PA28Push*************
|
||
int ArmCrash=1;
|
||
void HandleCrash(void){
|
||
TogglePB23();
|
||
TogglePB23();
|
||
uint32_t myId = OS_Id();
|
||
ST7735_Message(1,0,"myID =",myId);
|
||
ST7735_Message(1,1,"*Crash*, t= ",OS_MsTime());
|
||
ArmCrash=1;
|
||
TogglePB23();
|
||
OS_Kill();
|
||
}
|
||
void PA28Push(void){ // real time task
|
||
if(ArmCrash){
|
||
ArmCrash = 0; // debounce
|
||
NumCreated += OS_AddThread(&HandleCrash,128,1); // test robot crash
|
||
}
|
||
}
|
||
|
||
//------------------Task 3--------------------------------
|
||
// hardware-triggered TFLuna distance sampling at 100Hz
|
||
// Producer runs as part of UART2 ISR
|
||
// Producer uses fifo to transmit 100 distance samples/sec to Consumer
|
||
// every 64 samples, Consumer calculates FFT
|
||
// every 2.5ms*64 = 160 ms (6.25 Hz), consumer sends data to Display via mailbox
|
||
// Display thread updates LCD with measurement
|
||
uint32_t DataLost; // data sent by Producer, but not received by Consumer
|
||
uint32_t Distance2; // mm
|
||
int32_t x[16],ReX[16],ImX[16]; // input and output arrays for FFT
|
||
|
||
Sema4_t TFLuna3Ready; // signaled by Producer (right, TFLuna3) after global update
|
||
Sema4_t TFLuna2Ready; // signaled by Producer2 (left, TFLuna2) after global update
|
||
|
||
//******** Producer ***************
|
||
// The Producer in this lab will be called from the UART2 ISR
|
||
// The TFLuna2 samples distance at about 100 Hz
|
||
// sends data to the consumer, runs periodically at 100Hz
|
||
void Producer(uint32_t data){
|
||
if(Running){ // finite time run
|
||
TogglePA16(); // toggle PA16
|
||
#if USE_MEDIAN_FILTER
|
||
Distance2 = (uint32_t)Median5((int32_t)data);
|
||
#else
|
||
Distance2 = data;
|
||
#endif
|
||
OS_bSignal(&TFLuna3Ready);
|
||
TogglePA16(); // toggle PA16
|
||
}
|
||
}
|
||
|
||
void Producer2(uint32_t data){
|
||
if(Running){
|
||
#if USE_MEDIAN_FILTER
|
||
L_Distance2 = (uint32_t)Median5_2((int32_t)data);
|
||
#else
|
||
L_Distance2 = data;
|
||
#endif
|
||
OS_bSignal(&TFLuna2Ready);
|
||
}
|
||
}
|
||
|
||
uint32_t FrontCount = 0; // debug: increments each time TFLuna1 ISR delivers data
|
||
void Producer3(uint32_t data){
|
||
#if USE_MEDIAN_FILTER
|
||
FrontDist = (uint32_t)Median((int32_t)data);
|
||
#else
|
||
FrontDist = data;
|
||
#endif
|
||
FrontCount++;
|
||
}
|
||
|
||
void Display(void);
|
||
|
||
// Describe the error with text on the LCD and then stall.
|
||
// If you are getting disk errors, rerun Testmain1 Testmain2 Testmain3
|
||
void diskError(char *errtype, int32_t code){
|
||
OS_bSignal(&LCDFree);
|
||
ST7735_DrawString(0, 1, "Err: ", ST7735_RED);
|
||
ST7735_DrawString(5, 1, errtype, ST7735_RED);
|
||
ST7735_DrawString(0, 2, "Code: ", ST7735_RED);
|
||
ST7735_SetCursor(6, 2);
|
||
ST7735_SetTextColor(ST7735_RED);
|
||
ST7735_OutUDec(code);
|
||
Running = 0;
|
||
OS_Kill();
|
||
}
|
||
void StartFileDump(char *pt){
|
||
OS_bWait(&LCDFree);
|
||
eFile_Create(pt); // ignore error if file already exists
|
||
if(eFile_WOpen(pt)) diskError("eFile_WOpen",0);
|
||
if(eFile_WriteString("time_ms,elapsed,tf_r,tf_l,tf_front,sp,ot_state\n")) diskError("eFile_WriteString",0);
|
||
OS_bSignal(&LCDFree);
|
||
}
|
||
void EndFileDump(){
|
||
OS_bWait(&LCDFree);
|
||
if(eFile_WClose()) // diskError("eFile_WClose",0);
|
||
OS_bSignal(&LCDFree);
|
||
}
|
||
|
||
void FileDump(uint32_t data, uint32_t data2){
|
||
SetPB4();
|
||
OS_bWait(&LCDFree);
|
||
eFile_WriteUFix2(OS_MsTime()/10); eFile_Write('\t');
|
||
eFile_WriteUDec(data); eFile_Write('\t');
|
||
eFile_WriteUDec(data2); eFile_WriteString("\n\r");
|
||
OS_bSignal(&LCDFree);
|
||
ClrPB4();
|
||
}
|
||
|
||
#define NUMCOLS 7
|
||
// Dump row into csv
|
||
int FileDumpRow(int32_t* row){
|
||
OS_bWait(&LCDFree);
|
||
|
||
for (int i = 0; i < NUMCOLS; i++){
|
||
if (eFile_WriteSDec(row[i])){
|
||
OS_bSignal(&LCDFree);
|
||
return 1;
|
||
}
|
||
if (i < NUMCOLS - 1){
|
||
if(eFile_Write(',')){
|
||
OS_bSignal(&LCDFree);
|
||
return 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (eFile_Write('\n')){
|
||
OS_bSignal(&LCDFree);
|
||
return 1;
|
||
}
|
||
|
||
OS_bSignal(&LCDFree);
|
||
return 0;
|
||
}
|
||
|
||
typedef enum { S_FOLLOW, S_DETECT, S_COMMIT, S_PASS, S_REJOIN } overtake_state_t;
|
||
overtake_state_t ot_state = S_FOLLOW;
|
||
int32_t ot_side = 0; // -1=right, 1=left
|
||
uint32_t ot_detect_cnt = 0;
|
||
uint32_t ot_state_entry_ms = 0, ot_clear_since_ms = 0;
|
||
int32_t front_prev_filt = 0;
|
||
int32_t dist_ref_target = 0;
|
||
int32_t v_hat = 0, r_acc = 0;
|
||
int32_t prev_throttle_avg = 0;
|
||
int32_t front_m1 = 0, front_m2 = 0;
|
||
|
||
int32_t median3_i32(int32_t a, int32_t b, int32_t c) {
|
||
if ((a <= b && b <= c) || (c <= b && b <= a)) return b;
|
||
if ((b <= a && a <= c) || (c <= a && a <= b)) return a;
|
||
return c;
|
||
}
|
||
|
||
int32_t ramp_toward(int32_t cur, int32_t tgt, int32_t step) {
|
||
if (cur < tgt) { cur += step; if (cur > tgt) cur = tgt; }
|
||
else if (cur > tgt) { cur -= step; if (cur < tgt) cur = tgt; }
|
||
return cur;
|
||
}
|
||
|
||
int32_t throttle_to_v_mmps(int32_t throttle) {
|
||
if (throttle < 0) throttle = 0;
|
||
return (int32_t)((long long)throttle * V_MAX_MMPS / 9999); // 9999 = max throttle cmd
|
||
}
|
||
|
||
overtake_state_t overtake_step(int32_t front_filt, int32_t r_acc_in, int32_t r_thresh, int32_t d2, int32_t ld2, int32_t gz_ddps, uint32_t now_ms) {
|
||
overtake_state_t next_state = ot_state;
|
||
|
||
// bail out if something wrong
|
||
if (ot_state != S_FOLLOW && ot_state != S_DETECT) {
|
||
int bail = 0;
|
||
if (front_filt < 200) bail = 1; // wall coming
|
||
if (abs(gz_ddps) > ABORT_GZ) bail = 1;
|
||
if (ot_state == S_COMMIT || ot_state == S_PASS) {
|
||
if (ot_side == -1 && d2 < ABORT_SIDE) bail = 1;
|
||
if (ot_side == 1 && ld2 < ABORT_SIDE) bail = 1;
|
||
}
|
||
int32_t df_dt = front_filt - front_prev_filt;
|
||
if (ot_state == S_COMMIT && df_dt > ABORT_DF) bail = 1; // car swerved back
|
||
if (bail) {
|
||
dist_ref_target = 0;
|
||
return S_FOLLOW;
|
||
}
|
||
}
|
||
|
||
switch(ot_state) {
|
||
case S_FOLLOW:
|
||
dist_ref_target = 0;
|
||
if (front_filt < FRONT_DETECT && r_acc_in > r_thresh && (d2 > D2_MIN_PASS || ld2 > LD2_MIN_PASS)) {
|
||
next_state = S_DETECT;
|
||
ot_detect_cnt = 0;
|
||
}
|
||
break;
|
||
|
||
case S_DETECT:
|
||
dist_ref_target = 0;
|
||
if (front_filt < FRONT_DETECT && r_acc_in > r_thresh) {
|
||
if (d2 >= D2_MIN_PASS && d2 >= ld2) { // go right
|
||
ot_side = -1;
|
||
dist_ref_target = -OFFSET_CMD;
|
||
next_state = S_COMMIT;
|
||
ot_state_entry_ms = now_ms;
|
||
// ST7735_Message(1, 4, "COMMIT R", d2);
|
||
} else if (ld2 >= LD2_MIN_PASS) { // go left
|
||
ot_side = 1;
|
||
dist_ref_target = OFFSET_CMD;
|
||
next_state = S_COMMIT;
|
||
ot_state_entry_ms = now_ms;
|
||
} else {
|
||
if (front_filt < 400) next_state = S_FOLLOW; // too close, can't pass?
|
||
}
|
||
} else {
|
||
next_state = S_FOLLOW;
|
||
}
|
||
break;
|
||
|
||
case S_COMMIT:
|
||
if (abs(dist_ref_cur - dist_ref_target) < 10) {
|
||
next_state = S_PASS;
|
||
ot_state_entry_ms = now_ms;
|
||
ot_clear_since_ms = 0;
|
||
}
|
||
break;
|
||
|
||
case S_PASS:
|
||
if (front_filt > PASS_CLEAR) {
|
||
if (ot_clear_since_ms == 0) ot_clear_since_ms = now_ms;
|
||
if ((now_ms - ot_clear_since_ms) > T_CLEAR_MS && (now_ms - ot_state_entry_ms) > T_PASS_MIN) {
|
||
next_state = S_REJOIN;
|
||
dist_ref_target = 0;
|
||
ot_state_entry_ms = now_ms;
|
||
}
|
||
} else {
|
||
ot_clear_since_ms = 0;
|
||
}
|
||
break;
|
||
|
||
case S_REJOIN:
|
||
// obstacle came back, restart passing
|
||
if (ot_side == -1 && d2 < ABORT_SIDE) {
|
||
dist_ref_target = -OFFSET_CMD;
|
||
next_state = S_PASS;
|
||
}
|
||
if (ot_side == 1 && ld2 < ABORT_SIDE) {
|
||
dist_ref_target = OFFSET_CMD;
|
||
next_state = S_PASS;
|
||
}
|
||
if (abs(dist_ref_cur) < 10 && (now_ms - ot_state_entry_ms) > T_SETTLE) next_state = S_FOLLOW;
|
||
break;
|
||
}
|
||
|
||
return next_state;
|
||
}
|
||
|
||
//******** Robot ***************
|
||
// foreground Consumer thread, accepts data from producer
|
||
// inputs: none
|
||
// outputs: none
|
||
char FileName[8]="robot0";
|
||
|
||
uint32_t elapsed = 0;
|
||
int32_t prevError = 0;
|
||
int32_t prevE_A = 0;
|
||
uint32_t prevTime = 0;
|
||
uint32_t startTime = 0;
|
||
int16_t gyroZ_bias = 0;
|
||
void Robot(void){
|
||
DataLost = 0; // new run with no lost data
|
||
FilterWork = 0;
|
||
Running = 1;
|
||
Jitter3_Init();
|
||
Model_Inputs[steering_prev] = 32768; // Initialize to 0 degrees
|
||
OS_ClearMsTime();
|
||
OS_Fifo_Init(256);
|
||
NumCreated += OS_AddThread(&Display,128,0);
|
||
UART_OutString("Robot running...");
|
||
StartFileDump(FileName);
|
||
OS_ClearMsTime();
|
||
while (LaunchPad_InS2() == 0); // REMOVE AFTER DATA COLLECTION
|
||
|
||
// Gyro-Z bias estimation. Robot must remain stationary.
|
||
UART_OutString("Calibrating IMU...");
|
||
OS_Sleep(1000);
|
||
int32_t gzSum = 0;
|
||
const uint32_t BIAS_SAMPLES = 64; // ~1.6 s at 25 ms spacing
|
||
for (uint32_t i = 0; i < BIAS_SAMPLES; i++) {
|
||
long csr = StartCritical();
|
||
int16_t g = IMU_GyroZ;
|
||
EndCritical(csr);
|
||
gzSum += g;
|
||
OS_Sleep(25); // wait for IMU update
|
||
}
|
||
gyroZ_bias = (int16_t)(gzSum / (int32_t)BIAS_SAMPLES);
|
||
UART_OutString(" gz_bias=");
|
||
UART_OutSDec(gyroZ_bias);
|
||
UART_OutString("\n\r");
|
||
|
||
startTime = OS_MsTime();
|
||
while(1) {
|
||
elapsed = OS_MsTime() - prevTime;
|
||
prevTime = OS_MsTime();
|
||
#if USE_MEDIAN_FILTER
|
||
for (uint8_t i = 0; i < 5; i++) {
|
||
OS_bWait(&TFLuna3Ready);
|
||
OS_bWait(&TFLuna2Ready);
|
||
}
|
||
__disable_irq();
|
||
uint32_t d2 = Distance2;
|
||
uint32_t ld2 = L_Distance2;
|
||
__enable_irq();
|
||
#else
|
||
uint32_t d2 = 0;
|
||
uint32_t ld2 = 0;
|
||
for (uint8_t i = 0; i < 8; i++) {
|
||
OS_bWait(&TFLuna3Ready); // block until right-side TFLuna has fresh data
|
||
OS_bWait(&TFLuna2Ready); // block until left-side TFLuna has fresh data
|
||
__disable_irq();
|
||
d2 += Distance2;
|
||
ld2 += L_Distance2;
|
||
__enable_irq();
|
||
}
|
||
d2 >>= 3;
|
||
ld2 >>= 3;
|
||
#endif
|
||
|
||
__disable_irq();
|
||
uint32_t d_ir = Distance;
|
||
uint32_t ld_ir = L_Distance;
|
||
__enable_irq();
|
||
|
||
// FOR CALIBRATION
|
||
// ST7735_Message(1, 0, "L_DistanceRaw: ", L_DistanceRaw);
|
||
// ST7735_Message(1, 1, "DistanceRaw: ", DistanceRaw);
|
||
// IR correction: if TFLuna sees open space (>600mm) and reads significantly
|
||
// more than the IR, the IR is past its calibrated range — clamp to 305mm.
|
||
if (d2 > 600 && d2 > d_ir + 150) d_ir = 305;
|
||
if (ld2 > 600 && ld2 > ld_ir + 150) ld_ir = 305;
|
||
|
||
// Read IMU to use for controller refinement
|
||
long sr = StartCritical();
|
||
int16_t gz_raw = IMU_GyroZ;
|
||
int16_t ax = IMU_AccelX;
|
||
int16_t ay = IMU_AccelY;
|
||
EndCritical(sr);
|
||
|
||
int16_t gz = (int16_t)(gz_raw - gyroZ_bias); // bias-corrected for heuristic path
|
||
int32_t ax_mg = ((int32_t)ax * 1000) / ACCEL_SCALE;
|
||
int32_t gz_ddps = ((int32_t)gz * 10) / GYRO_SCALE;
|
||
|
||
__disable_irq();
|
||
int32_t front = (int32_t)FrontDist;
|
||
__enable_irq();
|
||
|
||
int32_t front_filt = median3_i32(front, front_m1, front_m2);
|
||
int32_t dt_ms = elapsed ? elapsed : 20;
|
||
int32_t v_meas = throttle_to_v_mmps(prev_throttle_avg);
|
||
v_hat = v_hat + (((v_meas - v_hat) * V_ALPHA_Q8) >> 8);
|
||
int32_t front_pred = front_prev_filt - (v_hat * dt_ms) / 1000;
|
||
int32_t r = front_filt - front_pred;
|
||
r_acc = ((r_acc * R_ACC_ALPHA_Q8) >> 8) + r;
|
||
if (r_acc > R_CAP) r_acc = R_CAP;
|
||
if (r_acc < -R_CAP) r_acc = -R_CAP;
|
||
int32_t r_thresh = R_THRESH_BASE + ((R_YAW_GAIN_Q8 * abs(gz_ddps)) >> 8);
|
||
ot_state = overtake_step(front_filt, r_acc, r_thresh, d2, ld2, gz_ddps, OS_MsTime());
|
||
int32_t ramp = (abs(gz_ddps) > GZ_RAMP_GATE) ? OFFSET_RATE/2 : OFFSET_RATE;
|
||
dist_ref_cur = ramp_toward(dist_ref_cur, dist_ref_target, ramp);
|
||
front_m2 = front_m1; front_m1 = front_filt; front_prev_filt = front_filt;
|
||
|
||
int32_t angle = arctan(((int32_t)(d_ir*1414) - (int32_t)(d2*1000)) /(int32_t)(224+d2)) - angle_ref;
|
||
int32_t L_angle = arctan(((int32_t)(ld_ir*1414) - (int32_t)(ld2*1000))/(int32_t)(224+ld2));
|
||
|
||
int32_t realDist = (d_ir * cosine(angle)) / 1000;
|
||
int32_t L_realDist = (ld_ir * cosine(L_angle)) / 1000;
|
||
int32_t e_d = realDist - L_realDist - dist_ref_cur;
|
||
|
||
int32_t intend_angle = ((kp_d * e_d) / 10) + ((kd_d * (e_d - prevError)) / 10);
|
||
prevError = e_d;
|
||
|
||
int32_t e_a = intend_angle - angle;
|
||
int32_t steeringAngle = ((e_a * kp_a) / 10) + (((e_a - prevE_A) * kd_a) / 10);
|
||
prevE_A = e_a;
|
||
|
||
uint16_t throttle = 9990; // max throttle
|
||
// Follow the gap kicks in when turn comes up
|
||
if(ot_state != S_COMMIT && ot_state != S_PASS && front < 800){
|
||
if (front < 600) throttle -= 1000;
|
||
if (front < 400) throttle -= 1000;
|
||
if (front < 200) throttle -= 1000;
|
||
int32_t urgency = (800-front) >> 2; // steer even harder for race day track
|
||
steeringAngle = (ld2 > d2) ? -urgency : urgency; // turn left if more room on left
|
||
}
|
||
|
||
// Clamp steering angle
|
||
if (steeringAngle < -35) steeringAngle = -35;
|
||
else if (steeringAngle > 35) steeringAngle = 35;
|
||
|
||
if (front < 120){ // too close, tell motors we crashed
|
||
// ST7735_Message(1, 1, "AHHH", 0);
|
||
CAN_TellCrashed(steeringAngle);
|
||
continue;
|
||
}
|
||
|
||
// Differential steering and IMU-based Traction Control / Braking
|
||
int32_t t_l = throttle;
|
||
int32_t t_r = throttle;
|
||
|
||
// If we're pulling too many lateral G's, we need to slow down
|
||
if (ax_mg > 500 || ax_mg < -500) {
|
||
t_l -= 2500;
|
||
t_r -= 2500;
|
||
}
|
||
|
||
// If we're supposed to be turning but we're not, differential steering to force us to rotate
|
||
if (steeringAngle > 15 && gz_ddps > -500) {
|
||
t_l -= 3500;
|
||
}
|
||
else if (steeringAngle < -15 && gz_ddps < 500) {
|
||
t_r -= 3500;
|
||
} else {
|
||
// Fallback to basic differential steering (older algorithm before IMU)
|
||
if (steeringAngle <= -15) {
|
||
t_r -= 2000;
|
||
}
|
||
else if (steeringAngle >= 15) {
|
||
t_l -= 2000;
|
||
}
|
||
}
|
||
|
||
// Prevent underflow
|
||
if (t_l < 0) t_l = 0;
|
||
if (t_r < 0) t_r = 0;
|
||
|
||
uint16_t throttle_l = (uint16_t)t_l;
|
||
uint16_t throttle_r = (uint16_t)t_r;
|
||
|
||
// Normalize inputs to model
|
||
// Want to place inputs in range [0, 65536], or [0,1] in fixed point
|
||
// Model_Inputs[ir_right] = Model_Normalize(d_ir, CAP_IR);
|
||
// Model_Inputs[ir_left] = Model_Normalize(ld_ir, CAP_IR);
|
||
// Model_Inputs[tf_left] = Model_Normalize(ld2, CAP_TFLUNA);
|
||
// Model_Inputs[tf_middle] = Model_Normalize(front, CAP_TFLUNA);
|
||
// Model_Inputs[tf_right] = Model_Normalize(d2, CAP_TFLUNA);
|
||
// // Clamp angles
|
||
// if (L_angle < -CAP_ANGLE) L_angle = -CAP_ANGLE;
|
||
// if (L_angle > CAP_ANGLE) L_angle = CAP_ANGLE;
|
||
// if (angle < -CAP_ANGLE) angle = -CAP_ANGLE;
|
||
// if (angle > CAP_ANGLE) angle = CAP_ANGLE;
|
||
// // Normalize angles
|
||
// Model_Inputs[angle_left] = (L_angle + CAP_ANGLE) << (16 - CAP_ANGLE_POW - 1);
|
||
// Model_Inputs[angle_right] = (angle + CAP_ANGLE) << (16 - CAP_ANGLE_POW - 1);
|
||
|
||
// Model_Inputs[yaw_rate] = Model_NormalizeSigned((int32_t)(-gz_raw), CAP_YAW);
|
||
// Model_Inputs[accel_lat] = Model_NormalizeSigned((int32_t)ax, CAP_ACCEL);
|
||
// Model_Inputs[accel_long] = Model_NormalizeSigned((int32_t)ay, CAP_ACCEL);
|
||
|
||
// // Call model inference
|
||
// Model_Inference();
|
||
|
||
// // Apply model output to PD controller
|
||
// if (ot_state == S_FOLLOW) {
|
||
// Model_ApplyResidual(&throttle_l, &throttle_r, &steeringAngle);
|
||
// }
|
||
|
||
// // Wait till after residuals are applied, so input to next is an accurate reflection
|
||
// Model_Inputs[throttle_left_prev] = Model_Normalize(throttle_l, CAP_THROTTLE);
|
||
// Model_Inputs[throttle_right_prev] = Modesoftwarel_Normalize(throttle_r, CAP_THROTTLE);
|
||
// Model_Inputs[steering_prev] = Model_NormalizeSigned(steeringAngle, CAP_STEERING);
|
||
|
||
ST7735_Message(1, 2, "throt r", throttle_r);
|
||
ST7735_Message(1, 3, "throt L", throttle_l);
|
||
CAN_SetMotors(throttle_l, throttle_r, steeringAngle);
|
||
prev_throttle_avg = ((int32_t)throttle_l + throttle_r) / 2;
|
||
|
||
// uint32_t sp;
|
||
// __asm volatile ("mov %0, sp" : "=r" (sp));
|
||
|
||
// Data collection
|
||
// int32_t row[NUMCOLS] = {OS_MsTime(), elapsed, d2, ld2, front, (int32_t)sp, ot_state};
|
||
// if (FileDumpRow(row)){
|
||
// EndFileDump();
|
||
// char* name;
|
||
// unsigned long size;
|
||
// eFile_DirNext(&name, &size);
|
||
// ST7735_Message(0, 2, "File dump complete ", 0);
|
||
// ST7735_Message(0, 3, "File size: ", size);
|
||
// while (1){ // Stop robot when we can no longer log
|
||
// CAN_SetMotors(0, 0, 0);
|
||
// }
|
||
// }
|
||
}
|
||
EndFileDump();
|
||
UART_OutString("done.\n\r>");
|
||
FileName[5] = (FileName[5]+1)&0xF7; // 0 to 7
|
||
Running = 0; // robot no longer running
|
||
OS_Kill();
|
||
}
|
||
|
||
//************S2Push*************
|
||
// Called when S2 Button pushed, fall of PB21
|
||
// Adds another Robot foreground task
|
||
// background threads execute once and return
|
||
void S2Push(void){
|
||
if(Running==0){
|
||
Running = 1; // prevents you from starting two test threads
|
||
NumCreated += OS_AddThread(&Robot,128,1); // test eDisk
|
||
}
|
||
}
|
||
//--------------end of Task 2-----------------------------
|
||
|
||
//******** Display ***************
|
||
// foreground thread, accepts data from consumer
|
||
// displays results on the LCD
|
||
// inputs: none
|
||
// outputs: none
|
||
void Display(void){
|
||
uint32_t distance;
|
||
uint32_t myId = OS_Id();
|
||
ST7735_Message(0,1,"myId = ",myId); // top half used for Display
|
||
ST7735_Message(0,2,"Run length = ",(RUNLENGTH)/FS); // top half used for Display
|
||
while(Running) {
|
||
TogglePB1(); // toggle PB1
|
||
distance = OS_MailBox_Recv();
|
||
// you will calibrate this in Lab 6
|
||
TogglePB1(); // toggle PB1
|
||
ST7735_Message(0,3,"Time(ms) =",OS_MsTime());
|
||
ST7735_Message(0,4,"work =",FilterWork);
|
||
ST7735_Message(0,5,"d(mm) =",distance);
|
||
ST7735_Message(0,6,"d IR Raw=", DistanceRaw);
|
||
ST7735_Message(0,7,"d IR =", Distance);
|
||
if (Distance2 > 2000) Distance2 = 2000;
|
||
ST7735_Message(1,0,"wall_angle =", arctan(((int32_t)(Distance*1414) - (int32_t)(Distance2*1000))/(int32_t)(224+Distance2)));
|
||
TogglePB1(); // toggle PB1
|
||
}
|
||
OS_Kill(); // done
|
||
}
|
||
|
||
//--------------end of Task 3-----------------------------
|
||
|
||
//------------------Task 4--------------------------------
|
||
// foreground thread that runs without waiting or sleeping
|
||
// it executes a virus detector
|
||
uint32_t Check(uint32_t start, uint32_t end){
|
||
uint32_t sum=0;
|
||
uint32_t *pt; pt = (uint32_t *)start;
|
||
while((uint32_t)pt < end){
|
||
sum += *pt++;
|
||
}
|
||
return sum;
|
||
}
|
||
//******** Virus Detector ***************
|
||
// foreground thread, performs a checksum of all ROM
|
||
// never blocks, never sleeps, never dies
|
||
// inputs: none
|
||
// outputs: none
|
||
uint32_t Checksum; // sum of data stored in ROM
|
||
uint32_t ChecksumOriginal; // sum of data stored in ROM
|
||
uint32_t ChecksumErrors;
|
||
void VirusDetector(void){
|
||
Checks = ChecksumErrors = 0;
|
||
ChecksumOriginal = Check(0,0x20000);
|
||
while(1) {
|
||
TogglePB20(); // toggle PB20
|
||
Checksum = Check(0,0x20000);
|
||
Checks++;
|
||
if(Checksum != ChecksumOriginal){
|
||
ChecksumErrors++;
|
||
}
|
||
}
|
||
}
|
||
|
||
//--------------end of Task 4-----------------------------
|
||
|
||
//------------------Task 5--------------------------------
|
||
// UART0 background ISR performs serial input/output
|
||
// Two software fifos are used to pass I/O data to foreground
|
||
// The interpreter runs as a foreground thread
|
||
// The UART0 driver should call OS_Wait(&RxDataAvailable) when foreground tries to receive
|
||
// The UART0 ISR should call OS_Signal(&RxDataAvailable) when it receives data from Rx
|
||
// Similarly, the transmit channel waits on a semaphore in the foreground
|
||
// and the UART0 ISR signals this semaphore (TxRoomLeft) when getting data from fifo
|
||
|
||
//******** Interpreter ***************
|
||
// Modify your intepreter from Lab 1, adding commands to help debug
|
||
// Interpreter is a foreground thread, accepts input from serial port, outputs to serial port
|
||
// inputs: none
|
||
// outputs: none
|
||
void Interpreter(void); // just a prototype, link to your interpreter
|
||
// add the following commands, leave other commands, if they make sense
|
||
// 1) print performance measures
|
||
// time-jitter, number of data points lost, number of calculations performed
|
||
// i.e., NumCreated, MaxJitter, DataLost, FilterWork, Checks
|
||
|
||
// 2) print debugging parameters
|
||
// i.e., Checks, ChecksumErrors
|
||
|
||
// Call these from your interpreter
|
||
void Lab4(void){int i;
|
||
UART_OutString("\r\nLab 4 performance data");
|
||
UART_OutString("\r\nFilterWork = "); UART_OutUDec(FilterWork);
|
||
UART_OutString("\r\nNumCreated = "); UART_OutUDec(NumCreated);
|
||
UART_OutString("\r\nChecksWork = "); UART_OutUDec(ChecksWork);
|
||
UART_OutString("\r\nDataLost = "); UART_OutUDec(DataLost);
|
||
UART_OutString("\r\nReal-time sampling jitter (cyc)");
|
||
UART_OutString("\r\nTime, Frequencies");
|
||
for(i=0; i<JitterSize3; i++){
|
||
if(JitterHistogram3[i]){ // skip blanks
|
||
UART_OutString("\r\n ");
|
||
UART_OutUDec5(i);
|
||
UART_OutUDec5(JitterHistogram3[i]);
|
||
}
|
||
}
|
||
UART_OutString("\r\nMaxJitter3(cyc) = "); UART_OutUDec(MaxJitter3);
|
||
}
|
||
|
||
void DFT(void){ int i; int32_t real,imag,mag;
|
||
UART_OutString("\r\nLab 2/3 DFT data");
|
||
UART_OutString("\r\nInput, Output Real, Output Imaginary, Magnitude");
|
||
for(i=0; i<8; i++){
|
||
real = ReX[i];
|
||
imag = ImX[i];
|
||
mag = sqrt2(real*real+imag*imag);
|
||
UART_OutString("\r\n"); UART_OutUDec(x[i]); UART_OutChar(' '); UART_OutSDec(real); UART_OutChar(' '); UART_OutSDec(imag);
|
||
UART_OutChar(' '); UART_OutSDec(mag);
|
||
}
|
||
}
|
||
|
||
//--------------end of Task 5-----------------------------
|
||
|
||
// IMU Task
|
||
void IMU_Task(void){
|
||
while (1){
|
||
IMU_Read(); // Update globals
|
||
// Debugging
|
||
// ST7735_Message(1, 1, "AccelX: ", IMU_AccelX);
|
||
// ST7735_Message(1, 2, "AccelY: ", IMU_AccelY);
|
||
// ST7735_Message(1, 3, "GyroZ: ", IMU_GyroZ);
|
||
|
||
OS_Sleep(20); // ~50 Hz
|
||
}
|
||
}
|
||
|
||
|
||
//*******************final user main DEMONTRATE THIS TO TA**********
|
||
int realmain(void){ // realmain
|
||
OS_Init(); // initialize, disable interrupts
|
||
|
||
Logic_Init();
|
||
DataLost = 0; // lost data between producer and consumer
|
||
FilterWork = 0;
|
||
Jitter3_Init();
|
||
// initialize communication channels
|
||
OS_MailBox_Init();
|
||
OS_Fifo_Init(256); // ***note*** 4 is not big enough*****
|
||
|
||
// hardware init
|
||
ADC_InitDual(ADC0, 1, 7, ADCVREF_VDDA); // ch1=right IR (PA26), ch7=left IR (PA22)
|
||
OS_InitSemaphore(&LCDFree, 1);
|
||
OS_InitSemaphore(&TFLuna3Ready, 0); // Robot blocks until ISR produces first sample
|
||
OS_InitSemaphore(&TFLuna2Ready, 0);
|
||
|
||
// CAN init for sending motor commands to motor board
|
||
CAN_Init();
|
||
CAN_EnableInterrupts(1);
|
||
|
||
Clock_Delay1ms(20); // Delay for IMU to work
|
||
if (IMU_Init() != 0){
|
||
ST7735_Message(1, 7, "IMU error", 0);
|
||
}
|
||
|
||
//Initialize LCD
|
||
ST7735_InitR(INITR_REDTAB); // Motor board uses SSD1306, not ST7735
|
||
|
||
// attach background tasks
|
||
OS_AddS2Task(&S2Push,1); // fall of PB21
|
||
OS_AddPA28Task(&PA28Push,1); // fall of PA28
|
||
OS_AddPeriodicThread(&DAS,PERIOD/80000,0); // 1 kHz real time sampling of ADC0_3
|
||
OS_AddPeriodicThread(&disk_timerproc,1,0); // time out routines for disk
|
||
|
||
// create initial foreground threads
|
||
NumCreated = 0;
|
||
NumCreated += OS_AddThread(&Interpreter,128,3);
|
||
NumCreated += OS_AddThread(&Robot,128,1); // CAN motor commands (waits for S2)
|
||
NumCreated += OS_AddThread(&IMU_Task, 128, 1); // Gets IMU data
|
||
NumCreated += OS_AddThread(&VirusDetector,128,7);
|
||
|
||
|
||
LPF_Init7(500,7);
|
||
TFLuna1_Init(&Producer3);
|
||
TFLuna1_Format_Standard_mm();
|
||
TFLuna1_Frame_Rate();
|
||
TFLuna1_SaveSettings();
|
||
TFLuna1_System_Reset();
|
||
|
||
TFLuna2_Init(&Producer2);
|
||
TFLuna2_Format_Standard_mm();
|
||
TFLuna2_Frame_Rate();
|
||
TFLuna2_SaveSettings();
|
||
TFLuna2_System_Reset();
|
||
|
||
TFLuna3_Init(&Producer);
|
||
TFLuna3_Format_Standard_mm(); // format in mm
|
||
TFLuna3_Frame_Rate(); // 100 samples/sec
|
||
TFLuna3_SaveSettings(); // save format and rate
|
||
TFLuna3_System_Reset(); // start measurements
|
||
|
||
if(eFile_Init()) diskError("eFile_Init",0);
|
||
if(eFile_Format()) diskError("eFile_Format",0);
|
||
if(eFile_Mount()) diskError("eFile_Mount",0);
|
||
OS_Launch(TIME_2MS); // doesn't return, interrupts enabled in here
|
||
return 0; // this never executes
|
||
}
|
||
//+++++++++++++++++++++++++DEBUGGING CODE++++++++++++++++++++++++
|
||
// ONCE YOUR RTOS WORKS YOU CAN COMMENT OUT THE REMAINING CODE
|
||
//
|
||
|
||
uint32_t M=1;
|
||
uint32_t Random32(void){
|
||
M = 1664525*M+1013904223;
|
||
return M;
|
||
}
|
||
// 0 to 31
|
||
uint32_t Random5(void){
|
||
return (Random32()>>27);
|
||
}
|
||
// 0 to 127
|
||
uint32_t Random7(void){
|
||
return (Random32()>>25);
|
||
}
|
||
// 0 to 255
|
||
uint8_t Random8(void){
|
||
return (Random32()>>24);
|
||
}
|
||
|
||
//*****************Test project 1*************************
|
||
// This test should run. If ST7735R works, but this fails:
|
||
// - there may be bad solder joints on Sensor board
|
||
// - the MSPM0 might have bad pins (PB0 or PB8)
|
||
// - SDC not seated correctly or damaged
|
||
// Write and read test of random access disk blocks
|
||
// Warning: this overwrites whatever is on the disk
|
||
unsigned char buffer[512]; // don't put on stack
|
||
#define MAXBLOCKS 100
|
||
void TestDisk(void){ DSTATUS result; uint32_t block; int i; uint8_t n;
|
||
// simple test of eDisk
|
||
ST7735_DrawString(0, 1, "eDisk test ", ST7735_WHITE);
|
||
UART_OutString("\n\rECE445M, Lab 4 eDisk test\n\rTestmain1\n\r");
|
||
result = eDisk_Init(0); // initialize disk
|
||
if(result) diskError("eDisk_Init",result);
|
||
UART_OutString("Writing blocks\n\r");
|
||
M = 1; // seed
|
||
for(block = 0; block < MAXBLOCKS; block++){
|
||
for(i=0;i<512;i++){
|
||
buffer[i] = Random8();
|
||
}
|
||
SetPA8(); // PA8 high for 100 block writes
|
||
if(eDisk_WriteBlock(buffer,block))diskError("eDisk_WriteBlock",block); // save to disk
|
||
ClrPA8();
|
||
}
|
||
UART_OutString("Reading blocks\n\r");
|
||
M = 1; // reseed, start over to get the same sequence
|
||
for(block = 0; block < MAXBLOCKS; block++){
|
||
SetPB23(); // PB23 high for one block read
|
||
if(eDisk_ReadBlock(buffer,block))diskError("eDisk_ReadBlock",block); // read from disk
|
||
ClrPB23();
|
||
for(i=0;i<512;i++){
|
||
n = Random8(); // same pseudo random sequence
|
||
if(buffer[i] != (0xFF&n)){
|
||
UART_OutString("Read data not correct, block="); UART_OutUDec(block);
|
||
UART_OutString(", i="); UART_OutUDec(i);
|
||
UART_OutString(", expected "); UART_OutUDec(0xFF&n);
|
||
UART_OutString(", read "); UART_OutUDec(buffer[i]);
|
||
UART_OutString("\n\r");
|
||
OS_Kill();
|
||
}
|
||
}
|
||
}
|
||
UART_OutString("Successful test of 100 blocks\n\r");
|
||
ST7735_DrawString(0, 1, "eDisk successful", ST7735_YELLOW);
|
||
Running = 0; // allow launch again
|
||
OS_Kill();
|
||
}
|
||
|
||
void StartTestDisk(void){
|
||
if(Running==0){
|
||
Running = 1; // prevents you from starting two test threads
|
||
NumCreated += OS_AddThread(&TestDisk,128,1); // test eDisk
|
||
}
|
||
}
|
||
|
||
int Testmain1(void){ // Testmain1
|
||
OS_Init(); // initialize, disable interrupts
|
||
// OS_AddDevices(1,1,0); // attach printf to UART0, allow ST7735, not eFile
|
||
Logic_Init();
|
||
Running = 1;
|
||
|
||
// attach background tasks
|
||
OS_AddPeriodicThread(&disk_timerproc,1,0); // time out routines for disk
|
||
OS_AddS2Task(&StartTestDisk,1);
|
||
OS_AddPA28Task(&StartTestDisk,1);
|
||
|
||
// create initial foreground threads
|
||
NumCreated = 0 ;
|
||
NumCreated += OS_AddThread(&TestDisk,128,1);
|
||
NumCreated += OS_AddThread(&VirusDetector,128,3);
|
||
|
||
OS_Launch(TIME_2MS); // doesn't return, interrupts enabled in here
|
||
return 0; // this never executes
|
||
}
|
||
|
||
|
||
//*******************Measurement of context switch time**********
|
||
// Run this to measure the time it takes to perform a task switch
|
||
// UART0 not needed
|
||
// SYSTICK interrupts, period established by OS_Launch
|
||
// first timer not needed
|
||
// second timer not needed
|
||
// S1 not needed,
|
||
// S2 not needed
|
||
// logic analyzer on PB22 for systick interrupt (in your OS)
|
||
// on PA8 to measure context switch time
|
||
void ThreadCS(void){ // only thread running
|
||
while(1){
|
||
TogglePA8(); // toggle PA8
|
||
}
|
||
}
|
||
int TestmainCS(void){ // TestmainCS
|
||
Logic_Init();
|
||
OS_Init(); // initialize, disable interrupts
|
||
NumCreated = 0 ;
|
||
NumCreated += OS_AddThread(&ThreadCS,128,0);
|
||
OS_Launch(TIME_1MS/10); // 100us, doesn't return, interrupts enabled in here
|
||
return 0; // this never executes
|
||
}
|
||
|
||
|
||
//*****************Test project 2*************************
|
||
// Filesystem test.
|
||
// Warning: this reformats the disk, all existing data will be lost
|
||
void PrintDirectory(void){ char *name; unsigned long size;
|
||
unsigned int num;
|
||
unsigned long total;
|
||
num = 0;
|
||
total = 0;
|
||
UART_OutString("\n\r");
|
||
if(eFile_DOpen("")) diskError("eFile_DOpen",0);
|
||
while(!eFile_DirNext(&name, &size)){
|
||
UART_OutString("Filename = "); UART_OutString(name); UART_OutString(" ");
|
||
UART_OutString("Size (bytes)= "); UART_OutUDec(size); UART_OutString("\n\r");
|
||
total = total+size;
|
||
num++;
|
||
}
|
||
UART_OutString("Number of Files = "); UART_OutUDec(num); UART_OutString("\n\r");
|
||
UART_OutString("Number of Bytes = "); UART_OutUDec(total); UART_OutString("\n\r");
|
||
if(eFile_DClose()) diskError("eFile_DClose",0);
|
||
}
|
||
void TestFile(void){ int i; char data; int status;
|
||
UART_OutString("\n\rECE445M Lab 4 eFile test 2\n\r");
|
||
ST7735_DrawString(0, 1, "eFile test 2 ", ST7735_WHITE);
|
||
// simple test of eFile
|
||
if(eFile_Init()) diskError("eFile_Init",0);
|
||
if(eFile_Format()) diskError("eFile_Format",0);
|
||
if(eFile_Mount()) diskError("eFile_Mount",0);
|
||
PrintDirectory();
|
||
if(eFile_Create("file1")) diskError("eFile_Create",0);
|
||
if(eFile_WOpen("file1")) diskError("eFile_WOpen",0);
|
||
for(i=5; i<=15; i++){
|
||
eFile_WriteString("Testmain2\tabcdefghijklmnopqrstuvwxyz\t");
|
||
eFile_WriteUFix2(OS_MsTime()/10); eFile_Write('\t');
|
||
eFile_WriteUDec(i); eFile_Write('\t');
|
||
eFile_WriteSFix2(i-10); eFile_Write('\t');
|
||
eFile_WriteSDec(i-10); eFile_WriteString("\n\r");
|
||
OS_Sleep(10);
|
||
}
|
||
if(eFile_WClose()) diskError("eFile_WClose",0);
|
||
PrintDirectory();
|
||
|
||
if(eFile_ROpen("file1")) diskError("eFile_ROpen",0);
|
||
do{
|
||
status = eFile_ReadNext(&data);
|
||
if(status == 0) UART_OutChar(data);
|
||
}while(status==0);
|
||
if(eFile_RClose()) diskError("eFile_RClose",0);
|
||
if(eFile_Delete("file1")) diskError("eFile_Delete",0);
|
||
PrintDirectory();
|
||
if(eFile_Unmount()) diskError("eFile_Unmount",0);
|
||
UART_OutString("Successful test\n\r");
|
||
ST7735_DrawString(0, 1, "eFile successful", ST7735_YELLOW);
|
||
Running=0; // launch again
|
||
OS_Kill();
|
||
}
|
||
|
||
void StartFileTest(void){
|
||
if(Running==0){
|
||
Running = 1; // prevents you from starting two test threads
|
||
NumCreated += OS_AddThread(&TestFile,128,1); // test eFile
|
||
}
|
||
}
|
||
|
||
int Testmain2(void){ // Testmain2
|
||
OS_Init(); // initialize, disable interrupts
|
||
Logic_Init();
|
||
Running = 1;
|
||
|
||
// attach background tasks
|
||
OS_AddPeriodicThread(&disk_timerproc,1,0); // time out routines for disk
|
||
OS_AddS2Task(&StartFileTest,1);
|
||
OS_AddPA28Task(&StartFileTest,1);
|
||
|
||
// create initial foreground threads
|
||
NumCreated = 0 ;
|
||
NumCreated += OS_AddThread(&TestFile,128,1);
|
||
NumCreated += OS_AddThread(&VirusDetector,128,3);
|
||
|
||
OS_Launch(TIME_2MS); // doesn't return, interrupts enabled in here
|
||
return 0; // this never executes
|
||
}
|
||
|
||
void PrintFile3(char *pt){int status; char data;
|
||
OS_bWait(&LCDFree);
|
||
eFile_ROpen(pt);
|
||
do{
|
||
status = eFile_ReadNext(&data);
|
||
if(status == 0) UART_OutChar(data);
|
||
}while(status==0);
|
||
eFile_RClose();
|
||
OS_bSignal(&LCDFree);
|
||
}
|
||
void Dump3(uint32_t run,int32_t data){
|
||
SetPA8();
|
||
OS_bWait(&LCDFree);
|
||
eFile_WriteString("Testmain3\tabcdefghijklmnopqrstuvwxyz\t");
|
||
eFile_WriteUFix2(OS_MsTime()/10); eFile_Write('\t');
|
||
eFile_WriteUDec(run); eFile_Write('\t');
|
||
eFile_WriteSFix2(data); eFile_Write('\t');
|
||
eFile_WriteSDec(data); eFile_WriteString("\n\r");
|
||
OS_bSignal(&LCDFree);
|
||
ClrPA8();
|
||
}
|
||
//*****************Test project 3*************************
|
||
// Filesystem stream test.
|
||
// Warning: this reformats the disk, all existing data will be lost
|
||
uint32_t Run3=0;
|
||
void TestFile3(void){ int i; char data;
|
||
UART_OutString("\n\rECE445M Lab 4 eFile test 3\n\r");
|
||
ST7735_Message(0,1,"eFile test 3", Run3);
|
||
// test of eFile
|
||
|
||
PrintDirectory();
|
||
OS_bWait(&LCDFree);
|
||
eFile_Create(FileName);
|
||
eFile_WOpen(FileName);
|
||
OS_bSignal(&LCDFree);
|
||
for(i=-5; i<=5; i++){
|
||
Dump3(Run3,i);
|
||
Run3++;
|
||
OS_Sleep(10);
|
||
}
|
||
OS_bWait(&LCDFree);
|
||
eFile_WClose();
|
||
OS_bSignal(&LCDFree);
|
||
PrintDirectory();
|
||
PrintFile3(FileName);
|
||
UART_OutString("Successful test 3, Run3="); UART_OutUDec(Run3);
|
||
UART_OutString("\n\r");
|
||
ST7735_Message(0,2,"Run3 =",Run3);
|
||
|
||
Running = 0; // allowed to launch again
|
||
FileName[5] = (FileName[5]+1)&0xF7; // 0 to 7
|
||
|
||
OS_Kill();
|
||
}
|
||
void Chaos3(void){
|
||
ST7735_Message(1,0,"Chaos",3);
|
||
while(1){
|
||
for(int l=1; l<5; l++){
|
||
ST7735_Message(1,l,"n =",Random8());
|
||
}
|
||
OS_Sleep(100);
|
||
}
|
||
}
|
||
void StartFileTest3(void){
|
||
if(Running==0){
|
||
Running = 1; // prevents you from starting two test threads
|
||
NumCreated += OS_AddThread(&TestFile3,128,1); // test eFile
|
||
}
|
||
}
|
||
|
||
int Testmain3(void){ // Testmain3
|
||
OS_Init(); // initialize, disable interrupts
|
||
Logic_Init();
|
||
Running = 1;
|
||
OS_InitSemaphore(&LCDFree, 1);
|
||
|
||
// attach background tasks
|
||
OS_AddPeriodicThread(&disk_timerproc,1,0); // time out routines for disk
|
||
OS_AddS2Task(&StartFileTest3,1);
|
||
OS_AddPA28Task(&StartFileTest3,1);
|
||
|
||
// create initial foreground threads
|
||
NumCreated = 0 ;
|
||
NumCreated += OS_AddThread(&TestFile3,128,1);
|
||
NumCreated += OS_AddThread(&Chaos3,128,1);
|
||
NumCreated += OS_AddThread(&VirusDetector,128,3);
|
||
|
||
if(eFile_Init()) diskError("eFile_Init",0);
|
||
if(eFile_Format()) diskError("eFile_Format",0);
|
||
if(eFile_Mount()) diskError("eFile_Mount",0);
|
||
|
||
OS_Launch(TIME_2MS); // doesn't return, interrupts enabled in here
|
||
return 0; // this never executes
|
||
}
|
||
|
||
|
||
//*****************Dump robot0 over UART*************************
|
||
// Press S2 to stream the contents of "robot0" out UART0.
|
||
// Capture on host with log_serial.sh (at repo root).
|
||
// Does NOT format the disk — preserves logged data.
|
||
void DumpRobotFile(void){
|
||
PrintFile3("robot0");
|
||
Running = 0; // allow re-trigger via S2
|
||
OS_Kill();
|
||
}
|
||
|
||
void StartDumpRobotFile(void){
|
||
if(Running == 0){
|
||
Running = 1;
|
||
NumCreated += OS_AddThread(&DumpRobotFile, 128, 1);
|
||
}
|
||
}
|
||
|
||
int DumpRobotFileMain(void){
|
||
OS_Init();
|
||
Logic_Init();
|
||
Running = 0;
|
||
OS_InitSemaphore(&LCDFree, 1);
|
||
|
||
OS_AddPeriodicThread(&disk_timerproc, 1, 0);
|
||
OS_AddS2Task(&StartDumpRobotFile, 1);
|
||
OS_AddPA28Task(&StartDumpRobotFile, 1);
|
||
|
||
NumCreated = 0;
|
||
|
||
NumCreated += OS_AddThread(&VirusDetector, 128, 7);
|
||
NumCreated += OS_AddThread(&Interpreter, 128, 1);
|
||
|
||
if(eFile_Init()) diskError("eFile_Init", 0);
|
||
if(eFile_Mount()) diskError("eFile_Mount", 0);
|
||
|
||
UART_OutString("\n\rReady. Press S2 (PB21) to dump robot0.\n\r");
|
||
OS_Launch(TIME_2MS);
|
||
return 0;
|
||
}
|
||
|
||
|
||
//*****************Test project IMU*************************
|
||
// Read IMU, scale to physical units, display on LCD.
|
||
// Accel in milli-g (1000 mg = 1 g).
|
||
// Gyro in deci-dps (10 ddps = 1 deg/s).
|
||
// Temp in centi-C (100 cC = 1 C).
|
||
void TestIMU(void){
|
||
while(1){
|
||
IMU_Read();
|
||
|
||
int32_t ax_mg = ((int32_t)IMU_AccelX * 1000) / 16384;
|
||
int32_t ay_mg = ((int32_t)IMU_AccelY * 1000) / 16384;
|
||
int32_t az_mg = ((int32_t)IMU_AccelZ * 1000) / 16384;
|
||
int32_t gx_ddps = ((int32_t)IMU_GyroX * 10) / 131;
|
||
int32_t gy_ddps = ((int32_t)IMU_GyroY * 10) / 131;
|
||
int32_t gz_ddps = ((int32_t)IMU_GyroZ * 10) / 131;
|
||
int32_t t_cC = ((int32_t)IMU_Temp * 100) / 340 + 3653;
|
||
|
||
ST7735_Message(0, 0, "ax (mg) =", ax_mg);
|
||
ST7735_Message(0, 1, "ay (mg) =", ay_mg);
|
||
ST7735_Message(0, 2, "az (mg) =", az_mg);
|
||
ST7735_Message(0, 3, "gx(ddps) =", gx_ddps);
|
||
ST7735_Message(0, 4, "gy(ddps) =", gy_ddps);
|
||
ST7735_Message(0, 5, "gz(ddps) =", gz_ddps);
|
||
ST7735_Message(0, 6, "T (cC) =", t_cC);
|
||
|
||
OS_Sleep(100); // 10 Hz display update
|
||
}
|
||
}
|
||
|
||
int TestmainIMU(void){
|
||
OS_Init();
|
||
Logic_Init();
|
||
|
||
ST7735_InitR(INITR_REDTAB);
|
||
|
||
int err = IMU_Init();
|
||
if(err != 0){
|
||
ST7735_Message(0, 0, "IMU_Init err =", err);
|
||
while(1){} // halt
|
||
}
|
||
|
||
NumCreated = 0;
|
||
NumCreated += OS_AddThread(&TestIMU, 128, 1);
|
||
NumCreated += OS_AddThread(&VirusDetector, 128, 2);
|
||
|
||
OS_Launch(TIME_2MS);
|
||
return 0;
|
||
}
|
||
|
||
|
||
//*******************Trampoline for selecting which main to execute**********
|
||
int main(void) { // main
|
||
__disable_irq();
|
||
Clock_Init80MHz(0); // no clock out to pin
|
||
LaunchPad_Init(); // LaunchPad_Init must be called once and before other I/O initializations
|
||
realmain();
|
||
}
|
||
|
||
|