This commit is contained in:
2026-06-12 02:55:04 -07:00
commit 30406f4f49
2040 changed files with 571534 additions and 0 deletions

460
RTOS_Labs_common/ADC.c Normal file
View File

@@ -0,0 +1,460 @@
/* ADC.c
* Jonathan Valvano
* December 20, 2022
* Derived from adc12_single_conversion_vref_internal_LP_MSPM0G3507_nortos_ticlang
* adc12_single_conversion_LP_MSPM0G3507_nortos_ticlang
* PB24_ADC0.5 thermistor input, V_temperature
* connect J27.1 to J27.2
*/
// ****note to students****
// the data sheet says the ADC does not work when clock is 80 MHz
// however, the ADC seems to work on my boards at 80 MHz
// I suggest you try 80MHz, but if it doesn't work, switch to 40MHz
#include <ti/devices/msp/msp.h>
#include <stdint.h>
#include "../inc/ADC.h"
#include "../inc/Clock.h"
// PA27 ADC0 channel 0 J1.8 also MKII light interrupt
// PA26 ADC0 channel 1 J1.5 Joystick select button
// PA25 ADC0 channel 2 J1.2 MKII Joystick X
// PA24 ADC0 channel 3 J3.27 ***free***
// PB25 ADC0 channel 4 J19.7 (insert 0ohm R74, no U3 OPA2365)
// PB24 ADC0 channel 5 J1.6 MKII microphone in
// PB20 ADC0 channel 6 J4.36 ***free***
// PA22 ADC0 channel 7 J24 MKII Accelerometer Y
//
// PA15 ADC1 channel 0 J3.30 (also DACout)
// PA16 ADC1 channel 1 J3.29 ***free***
// PA17 ADC1 channel 2 J3.28 ***free***
// PA18 ADC1 channel 3 J3.26 MKII Joystick Y
// PB17 ADC1 channel 4 J2.18 ***free***
// PB18 ADC1 channel 5 J3.25 MKII Accelerometer Z
// PB19 ADC1 channel 6 J3.23 MKII Accelerometer X
// PA21 ADC1 channel 7 J17.8 (insert R20, remove R3)
// Assumes 40 MHz bus
void ADC0_Init(uint32_t channel, uint32_t reference){
// Reset ADC and VREF
// RSTCLR
// bits 31-24 unlock key 0xB1
// bit 1 is Clear reset sticky bit
// bit 0 is reset ADC
ADC0->ULLMEM.GPRCM.RSTCTL = 0xB1000003;
if(reference == ADCVREF_INT){
VREF->GPRCM.RSTCTL = 0xB1000003;
}
// Enable power ADC and VREF
// PWREN
// bits 31-24 unlock key 0x26
// bit 0 is Enable Power
ADC0->ULLMEM.GPRCM.PWREN = 0x26000001;
if(reference == ADCVREF_INT){
VREF->GPRCM.PWREN = 0x26000001;
}
Clock_Delay(24); // time for ADC and VREF to power up
ADC0->ULLMEM.GPRCM.CLKCFG = 0xA9000000; // ULPCLK
// bits 31-24 key=0xA9
// bit 5 CCONSTOP= 0 not continuous clock in stop mode
// bit 4 CCORUN= 0 not continuous clock in run mode
// bit 1-0 0=ULPCLK,1=SYSOSC,2=HFCLK
ADC0->ULLMEM.CLKFREQ = 7; // 40 to 48 MHz
ADC0->ULLMEM.CTL0 = 0x03010000;
// bits 26-24 = 011 divide by 8
// bit 16 PWRDN=1 for manual, =0 power down on completion, if no pending trigger
// bit 0 ENC=0 disable (1 to 0 will end conversion)
ADC0->ULLMEM.CTL1 = 0x00000000;
// bits 30-28 =0 no shift
// bits 26-24 =0 no averaging
// bit 20 SAMPMODE=1 software triggers
// bits 17-16 CONSEQ=0 ADC at start will be sampled once, 10 for repeated sampling
// bit 8 SC=0 for stop, =1 to software start
// bit 0 TRIGSRC=0 software trigger
ADC0->ULLMEM.CTL2 = 0x00000000;
// bits 28-24 ENDADD (which MEMCTL to end)
// bits 20-16 STARTADD (which MEMCTL to start)
// bits 15-11 SAMPCNT (for DMA)
// bit 10 FIFOEN=0 disable FIFO
// bit 8 DMAEN=0 disable DMA
// bits 2-1 RES=0 for 12 bit (=1 for 10bit,=2for 8-bit)
// bit 0 DF=0 unsigned formant (1 for signed, left aligned)
ADC0->ULLMEM.MEMCTL[0] = reference+channel;
// bit 28 WINCOMP=0 disable window comparator
// bit 24 TRIG trigger policy, =0 for auto next, =1 for next requires trigger
// bit 20 BCSEN=0 disable burn out current
// bit 16 = AVGEN =0 for no averaging
// bit 12 = STIME=0 for SCOMP0
// bits 9-8 VRSEL = 10 for internal VREF,(00 for VDDA)
// bits 4-0 channel = 0 to 7 available
ADC0->ULLMEM.SCOMP0 = 0; // 8 sample clocks
// ADC0->ULLMEM.GEN_EVENT.ICLR |= 0x0100; // clear flag MEMCTL[0]
// ADC0->ULLMEM.GEN_EVENT.IMASK = 0; // no interrupt
if(reference == ADCVREF_INT){
VREF->CLKSEL = 0x00000008; // bus clock
VREF->CLKDIV = 0; // divide by 1
VREF->CTL0 = 0x0001;
// bit 8 SHMODE = off
// bit 7 BUFCONFIG=0 for 2.4 (=1 for 1.4)
// bit 0 is enable
VREF->CTL2 = 0;
// bits 31-16 HCYCLE=0
// bits 15-0 SHCYCLE=0
while((VREF->CTL1&0x01)==0){}; // wait for VREF to be ready
}
}
// sample 12-bit ADC
uint32_t ADC0_In(void){
ADC0->ULLMEM.CTL0 |= 0x00000001; // enable conversions
ADC0->ULLMEM.CTL1 |= 0x00000100; // start ADC
uint32_t volatile delay=ADC0->ULLMEM.STATUS; // time to let ADC start
while((ADC0->ULLMEM.STATUS&0x01)==0x01){} // wait for completion
return ADC0->ULLMEM.MEMRES[0];
}
// Assumes 40 MHz bus
void ADC1_Init(uint32_t channel, uint32_t reference){
// Reset ADC and VREF
// RSTCLR
// bits 31-24 unlock key 0xB1
// bit 1 is Clear reset sticky bit
// bit 0 is reset ADC
ADC1->ULLMEM.GPRCM.RSTCTL = (uint32_t)0xB1000003;
if(reference == ADCVREF_INT){
VREF->GPRCM.RSTCTL = (uint32_t)0xB1000003;
}
// Enable power ADC and VREF
// PWREN
// bits 31-24 unlock key 0x26
// bit 0 is Enable Power
ADC1->ULLMEM.GPRCM.PWREN = (uint32_t)0x26000001;
if(reference == ADCVREF_INT){
VREF->GPRCM.PWREN = (uint32_t)0x26000001;
}
Clock_Delay(24); // time for ADC and VREF to power up
ADC1->ULLMEM.GPRCM.CLKCFG = 0xA9000000; // ULPCLK
// bits 31-24 key=0xA9
// bit 5 CCONSTOP= 0 not continuous clock in stop mode
// bit 4 CCORUN= 0 not continuous clock in run mode
// bit 1-0 0=ULPCLK,1=SYSOSC,2=HFCLK
ADC1->ULLMEM.CLKFREQ = 7; // 40 to 48 MHz
ADC1->ULLMEM.CTL0 = 0x03010000;
// bits 26-24 = 011 divide by 8
// bit 16 PWRDN=1 for manual, =0 power down on completion, if no pending trigger
// bit 0 ENC=0 disable (1 to 0 will end conversion)
ADC1->ULLMEM.CTL1 = 0x00000000;
// bits 30-28 =0 no shift
// bits 26-24 =0 no averaging
// bit 20 SAMPMODE=0 high phase
// bits 17-16 CONSEQ=0 ADC at start will be sampled once, 10 for repeated sampling
// bit 8 SC=0 for stop, =1 to software start
// bit 0 TRIGSRC=0 software trigger
ADC1->ULLMEM.CTL2 = 0x00000000;
// bits 28-24 ENDADD (which MEMCTL to end)
// bits 20-16 STARTADD (which MEMCTL to start)
// bits 15-11 SAMPCNT (for DMA)
// bit 10 FIFOEN=0 disable FIFO
// bit 8 DMAEN=0 disable DMA
// bits 2-1 RES=0 for 12 bit (=1 for 10bit,=2for 8-bit)
// bit 0 DF=0 unsigned formant (1 for signed, left aligned)
ADC1->ULLMEM.MEMCTL[0] = reference+channel;
// bit 28 WINCOMP=0 disable window comparator
// bit 24 TRIG trigger policy, =0 for auto next, =1 for next requires trigger
// bit 20 BCSEN=0 disable burn out current
// bit 16 = AVGEN =0 for no averaging
// bit 12 = STIME=0 for SCOMP0
// bits 9-8 VRSEL = 10 for internal VREF,(00 for VDDA)
// bits 4-0 channel = 0 to 7 available
ADC1->ULLMEM.SCOMP0 = 0; // 8 sample clocks
// ADC1->ULLMEM.GEN_EVENT.ICLR |= 0x0100; // clear flag MEMCTL[0]
ADC1->ULLMEM.GEN_EVENT.IMASK = 0; // no interrupt
if(reference == ADCVREF_INT){
VREF->CLKSEL = 0x00000008; // bus clock
VREF->CLKDIV = 0; // divide by 1
VREF->CTL0 = 0x0001;
// bit 8 SHMODE = off
// bit 7 BUFCONFIG=0 for 2.4 (=1 for 1.4)
// bit 0 is enable
VREF->CTL2 = 0;
// bits 31-16 HCYCLE=0
// bits 15-0 SHCYCLE=0
while((VREF->CTL1&0x01)==0){}; // wait for VREF to be ready
}
}
// sample 12-bit ADC
uint32_t ADC1_In(void){
ADC1->ULLMEM.CTL0 |= 0x00000001; // enable conversions
ADC1->ULLMEM.CTL1 |= 0x00000100; // start ADC
uint32_t volatile delay=ADC1->ULLMEM.STATUS; // time to let ADC start
while((ADC1->ULLMEM.STATUS&0x01)==0x01){}; // wait for completion
return ADC1->ULLMEM.MEMRES[0];
}
// Assumes 40 MHz bus
void ADC_Init(ADC12_Regs *adc12,uint32_t channel, uint32_t reference){
// Reset ADC and VREF
// RSTCLR
// bits 31-24 unlock key 0xB1
// bit 1 is Clear reset sticky bit
// bit 0 is reset ADC
adc12->ULLMEM.GPRCM.RSTCTL = (uint32_t)0xB1000003;
if(reference == ADCVREF_INT){
VREF->GPRCM.RSTCTL = (uint32_t)0xB1000003;
}
// Enable power ADC and VREF
// PWREN
// bits 31-24 unlock key 0x26
// bit 0 is Enable Power
adc12->ULLMEM.GPRCM.PWREN = (uint32_t)0x26000001;
if(reference == ADCVREF_INT){
VREF->GPRCM.PWREN = (uint32_t)0x26000001;
}
Clock_Delay(24); // time for ADC and VREF to power up
adc12->ULLMEM.GPRCM.CLKCFG = 0xA9000000; // ULPCLK
// bits 31-24 key=0xA9
// bit 5 CCONSTOP= 0 not continuous clock in stop mode
// bit 4 CCORUN= 0 not continuous clock in run mode
// bit 1-0 0=ULPCLK,1=SYSOSC,2=HFCLK
adc12->ULLMEM.CLKFREQ = 7; // 40 to 48 MHz
adc12->ULLMEM.CTL0 = 0x03010000;
// bits 26-24 = 011 divide by 8
// bit 16 PWRDN=1 for manual, =0 power down on completion, if no pending trigger
// bit 0 ENC=0 disable (1 to 0 will end conversion)
adc12->ULLMEM.CTL1 = 0x00000000;
// bits 30-28 =0 no shift
// bits 26-24 =0 no averaging
// bit 20 SAMPMODE=0 high phase
// bits 17-16 CONSEQ=0 ADC at start will be sampled once, 10 for repeated sampling
// bit 8 SC=0 for stop, =1 to software start
// bit 0 TRIGSRC=0 software trigger
adc12->ULLMEM.CTL2 = 0x00000000;
// bits 28-24 ENDADD (which MEMCTL to end)
// bits 20-16 STARTADD (which MEMCTL to start)
// bits 15-11 SAMPCNT (for DMA)
// bit 10 FIFOEN=0 disable FIFO
// bit 8 DMAEN=0 disable DMA
// bits 2-1 RES=0 for 12 bit (=1 for 10bit,=2for 8-bit)
// bit 0 DF=0 unsigned formant (1 for signed, left aligned)
adc12->ULLMEM.MEMCTL[0] = reference|channel;
// bit 28 WINCOMP=0 disable window comparator
// bit 24 TRIG trigger policy, =0 for auto next, =1 for next requires trigger
// bit 20 BCSEN=0 disable burn out current
// bit 16 = AVGEN =0 for no averaging
// bit 12 = STIME=0 for SCOMP0
// bits 9-8 VRSEL = 10 for internal VREF,(00 for VDDA)
// bits 4-0 channel = 0 to 7 available
adc12->ULLMEM.SCOMP0 = 0; // 8 sample clocks
// adc12->ULLMEM.INT_EVENT0.ICLR |= 0x0100; // clear flag MEMCTL[0]
adc12->ULLMEM.GEN_EVENT.IMASK = 0; // no interrupt
if(reference == ADCVREF_INT){
VREF->CLKSEL = 0x00000008; // bus clock
VREF->CLKDIV = 0; // divide by 1
VREF->CTL0 = 0x0001;
// bit 8 SHMODE = off
// bit 7 BUFCONFIG=0 for 2.4 (=1 for 1.4)
// bit 0 is enable
VREF->CTL2 = 0;
// bits 31-16 HCYCLE=0
// bits 15-0 SHCYCLE=0
while((VREF->CTL1&0x01)==0){}; // wait for VREF to be ready
}
}
// sample 12-bit ADC
uint32_t ADC_In(ADC12_Regs *adc12){
adc12->ULLMEM.CTL0 |= 0x00000001; // enable conversions
adc12->ULLMEM.CTL1 |= 0x00000100; // start ADC
uint32_t volatile delay=adc12->ULLMEM.STATUS; // time to let ADC start
while((adc12->ULLMEM.STATUS&0x01)==0x01){}; // wait for completion
return adc12->ULLMEM.MEMRES[0];
}
// Assumes 40 MHz bus
void ADC_InitDual(ADC12_Regs *adc12,uint32_t channel1,uint32_t channel2, uint32_t reference){
// Reset ADC and VREF
// RSTCLR
// bits 31-24 unlock key 0xB1
// bit 1 is Clear reset sticky bit
// bit 0 is reset ADC
adc12->ULLMEM.GPRCM.RSTCTL = (uint32_t)0xB1000003;
if(reference == ADCVREF_INT){
VREF->GPRCM.RSTCTL = (uint32_t)0xB1000003;
}
// Enable power ADC and VREF
// PWREN
// bits 31-24 unlock key 0x26
// bit 0 is Enable Power
adc12->ULLMEM.GPRCM.PWREN = (uint32_t)0x26000001;
if(reference == ADCVREF_INT){
VREF->GPRCM.PWREN = (uint32_t)0x26000001;
}
Clock_Delay(24); // time for ADC and VREF to power up
adc12->ULLMEM.GPRCM.CLKCFG = 0xA9000000; // ULPCLK
// bits 31-24 key=0xA9
// bit 5 CCONSTOP= 0 not continuous clock in stop mode
// bit 4 CCORUN= 0 not continuous clock in run mode
// bit 1-0 0=ULPCLK,1=SYSOSC,2=HFCLK
adc12->ULLMEM.CLKFREQ = 7; // 40 to 48 MHz
adc12->ULLMEM.CTL0 = 0x03010000;
// bits 26-24 = 011 divide by 8
// bit 16 PWRDN=1 for manual, =0 power down on completion, if no pending trigger
// bit 0 ENC=0 disable (1 to 0 will end conversion)
adc12->ULLMEM.CTL1 = 0x00010000;
// bits 30-28 =0 no shift
// bits 26-24 =0 no averaging
// bit 20 SAMPMODE=0 high phase
// bits 17-16 CONSEQ=01 ADC at start to end
// bit 8 SC=0 for stop, =1 to software start
// bit 0 TRIGSRC=0 software trigger
adc12->ULLMEM.CTL2 = 0x02010000;
// bits 28-24 ENDADD=2 (which MEMCTL to end)
// bits 20-16 STARTADD=1 (which MEMCTL to start)
// bits 15-11 SAMPCNT (for DMA)
// bit 10 FIFOEN=0 disable FIFO
// bit 8 DMAEN=0 disable DMA
// bits 2-1 RES=0 for 12 bit (=1 for 10bit,=2for 8-bit)
// bit 0 DF=0 unsigned formant (1 for signed, left aligned)
adc12->ULLMEM.MEMCTL[1] = reference+channel1;
adc12->ULLMEM.MEMCTL[2] = reference+channel2;
// bit 28 WINCOMP=0 disable window comparator
// bit 24 TRIG trigger policy, =0 for auto next, =1 for next requires trigger
// bit 20 BCSEN=0 disable burn out current
// bit 16 = AVGEN =0 for no averaging
// bit 12 = STIME=0 for SCOMP0
// bits 9-8 VRSEL = 10 for internal VREF,(00 for VDDA)
// bits 4-0 channel = 0 to 7 available
adc12->ULLMEM.SCOMP0 = 0; // 8 sample clocks
// adc12->ULLMEM.GEN_EVENT.ICLR |= 0x0100; // clear flag MEMCTL[1] ??
adc12->ULLMEM.GEN_EVENT.IMASK = 0; // no interrupt
if(reference == ADCVREF_INT){
VREF->CLKSEL = 0x00000008; // bus clock
VREF->CLKDIV = 0; // divide by 1
VREF->CTL0 = 0x0001;
// bit 8 SHMODE = off
// bit 7 BUFCONFIG=0 for 2.4 (=1 for 1.4)
// bit 0 is enable
VREF->CTL2 = 0;
// bits 31-16 HCYCLE=0
// bits 15-0 SHCYCLE=0
while((VREF->CTL1&0x01)==0){}; // wait for VREF to be ready
}
}
// sample 12-bit ADC
void ADC_InDual(ADC12_Regs *adc12,uint32_t *d1, uint32_t *d2){
adc12->ULLMEM.CTL0 |= 0x00000001; // enable conversions
adc12->ULLMEM.CTL1 |= 0x00000100; // start ADC
uint32_t volatile delay=adc12->ULLMEM.STATUS; // time to let ADC start
while((adc12->ULLMEM.STATUS&0x01)==0x01){}; // wait for completion
*d1 = adc12->ULLMEM.MEMRES[1];
*d2 = adc12->ULLMEM.MEMRES[2];
}
// Assumes 40 MHz bus
void ADC_InitTriple(ADC12_Regs *adc12,uint32_t channel1,uint32_t channel2,uint32_t channel3,uint32_t reference){
// Reset ADC and VREF
// RSTCLR
// bits 31-24 unlock key 0xB1
// bit 1 is Clear reset sticky bit
// bit 0 is reset ADC
adc12->ULLMEM.GPRCM.RSTCTL = (uint32_t)0xB1000003;
if(reference == ADCVREF_INT){
VREF->GPRCM.RSTCTL = (uint32_t)0xB1000003;
}
// Enable power ADC and VREF
// PWREN
// bits 31-24 unlock key 0x26
// bit 0 is Enable Power
adc12->ULLMEM.GPRCM.PWREN = (uint32_t)0x26000001;
if(reference == ADCVREF_INT){
VREF->GPRCM.PWREN = (uint32_t)0x26000001;
}
Clock_Delay(24); // time for ADC and VREF to power up
adc12->ULLMEM.GPRCM.CLKCFG = 0xA9000000; // ULPCLK
// bits 31-24 key=0xA9
// bit 5 CCONSTOP= 0 not continuous clock in stop mode
// bit 4 CCORUN= 0 not continuous clock in run mode
// bit 1-0 0=ULPCLK,1=SYSOSC,2=HFCLK
adc12->ULLMEM.CLKFREQ = 7; // 40 to 48 MHz
adc12->ULLMEM.CTL0 = 0x03010000;
// bits 26-24 = 011 divide by 8
// bit 16 PWRDN=1 for manual, =0 power down on completion, if no pending trigger
// bit 0 ENC=0 disable (1 to 0 will end conversion)
adc12->ULLMEM.CTL1 = 0x00010000;
// bits 30-28 =0 no shift
// bits 26-24 =0 no averaging
// bit 20 SAMPMODE=0 high phase
// bits 17-16 CONSEQ=01 ADC at start to end
// bit 8 SC=0 for stop, =1 to software start
// bit 0 TRIGSRC=0 software trigger
adc12->ULLMEM.CTL2 = 0x03010000;
// bits 28-24 ENDADD=3 (which MEMCTL to end)
// bits 20-16 STARTADD=1 (which MEMCTL to start)
// bits 15-11 SAMPCNT (for DMA)
// bit 10 FIFOEN=0 disable FIFO
// bit 8 DMAEN=0 disable DMA
// bits 2-1 RES=0 for 12 bit (=1 for 10bit,=2for 8-bit)
// bit 0 DF=0 unsigned formant (1 for signed, left aligned)
adc12->ULLMEM.MEMCTL[1] = reference+channel1;
adc12->ULLMEM.MEMCTL[2] = reference+channel2;
adc12->ULLMEM.MEMCTL[3] = reference+channel3;
// bit 28 WINCOMP=0 disable window comparator
// bit 24 TRIG trigger policy, =0 for auto next, =1 for next requires trigger
// bit 20 BCSEN=0 disable burn out current
// bit 16 = AVGEN =0 for no averaging
// bit 12 = STIME=0 for SCOMP0
// bits 9-8 VRSEL = 10 for internal VREF,(00 for VDDA)
// bits 4-0 channel = 0 to 7 available
adc12->ULLMEM.SCOMP0 = 0; // 8 sample clocks
// adc12->ULLMEM.GEN_EVENT.ICLR |= 0x0100; // clear flag MEMCTL[1] ??
adc12->ULLMEM.GEN_EVENT.IMASK = 0; // no interrupt
if(reference == ADCVREF_INT){
VREF->CLKSEL = 0x00000008; // bus clock
VREF->CLKDIV = 0; // divide by 1
VREF->CTL0 = 0x0001;
// bit 8 SHMODE = off
// bit 7 BUFCONFIG=0 for 2.4 (=1 for 1.4)
// bit 0 is enable
VREF->CTL2 = 0;
// bits 31-16 HCYCLE=0
// bits 15-0 SHCYCLE=0
while((VREF->CTL1&0x01)==0){}; // wait for VREF to be ready
}
}
// sample 12-bit ADC
void ADC_InTriple(ADC12_Regs *adc12,uint32_t *d1, uint32_t *d2, uint32_t *d3){
adc12->ULLMEM.CTL0 |= 0x00000001; // enable conversions
adc12->ULLMEM.CTL1 |= 0x00000100; // start ADC
uint32_t volatile delay=adc12->ULLMEM.STATUS; // time to let ADC start
while((adc12->ULLMEM.STATUS&0x01)==0x01){}; // wait for completion
*d1 = adc12->ULLMEM.MEMRES[1];
*d2 = adc12->ULLMEM.MEMRES[2];
*d3 = adc12->ULLMEM.MEMRES[3];
}
// n means 2^n samples in average, n=0 to 7
void ADC0_InitAve(uint32_t channel, uint32_t n){
ADC0->ULLMEM.GPRCM.RSTCTL = 0xB1000003; // 1) reset
ADC0->ULLMEM.GPRCM.PWREN = 0x26000001; // 2) activate
Clock_Delay(24); // 3) wait
ADC0->ULLMEM.GPRCM.CLKCFG = 0xA9000000; // 4) ULPCLK
ADC0->ULLMEM.CLKFREQ = 7; // 5) 40-48 MHz
ADC0->ULLMEM.CTL0 = 0x03010000; // 6) divide by 8
ADC0->ULLMEM.CTL1 = 0x00000000|(n<<28)|(n<<24);// 7) mode
// AVGD = AVEN = n
ADC0->ULLMEM.CTL2 = 0x00000000; // 8) MEMRES
if(n){
ADC0->ULLMEM.MEMCTL[0] = (1<<16)|channel; // 9) channel
}else{
ADC0->ULLMEM.MEMCTL[0] = channel; // 9) channel
}
ADC0->ULLMEM.SCOMP0 = 0; // 10) 8 sample clocks
ADC0->ULLMEM.GEN_EVENT.IMASK = 0; // 11) no interrupt
}

235
RTOS_Labs_common/ADC.h Normal file
View File

@@ -0,0 +1,235 @@
/*!
* @defgroup ADC
* @brief Analog to digital conversion
<table>
<caption id="ADCpins">ADC pins on the MSPM0G3507</caption>
<tr><th>Pin <th>ADC channel<th>Sensor
<tr><td>PA27 <td>ADC0 channel 0 <td>J1.8 also MKII light interrupt
<tr><td>PA26 <td>ADC0 channel 1 <td>J1.6 MKII microphone in
<tr><td>PA25 <td>ADC0 channel 2 <td>J1.2 MKII Joystick X
<tr><td>PA24 <td>ADC0 channel 3 <td>J3.27 ***free***
<tr><td>PB25 <td>ADC0 channel 4 <td>J19.7 (insert 0ohm R74, no U3 OPA2365)
<tr><td>PB24 <td>ADC0 channel 5 <td>J1.5 also MKII joystick select button
<tr><td>PB20 <td>ADC0 channel 6 <td>J4.36 ***free***
<tr><td>PA22 <td>ADC0 channel 7 <td>J24 MKII Accelerometer Y
<tr><td>PA15 <td>ADC1 channel 0 <td>J3.30 (also DACout)
<tr><td>PA16 <td>ADC1 channel 1 <td>J3.29 ***free***
<tr><td>PA17 <td>ADC1 channel 2 <td>J3.28 ***free***
<tr><td>PA18 <td>ADC1 channel 3 <td>J3.26 MKII Joystick Y
<tr><td>PB17 <td>ADC1 channel 4 <td>J2.18 ***free***
<tr><td>PB18 <td>ADC1 channel 5 <td>J3.25 MKII Accelerometer Z
<tr><td>PB19 <td>ADC1 channel 6 <td>J3.23 MKII Accelerometer X
<tr><td>PA21 <td>ADC1 channel 7 <td>J17.8 (insert R20, remove R3)
</table>
* @{*/
/**
* @file ADC.h
* @brief Initialize 12-bit ADC0 and/or ADC1
* @details ADC input, software trigger, 12-bit conversion<br>
* The ADC allows two possible references: 2.5V or 3.3V.<br>
* The internal 2.5V reference is lower noise, but limits the range to 0 to 2.5V.<br>
* The other possibility is to use the analog voltage supply at 3.3V,
* making the ADC range 0 to 3.3V. In this driver, the 3.3V range is selected.
* There are several configurations (each with initialization and a
* read ADC with software trigger, busy-wait function)<br>
* @version ECE319K v1.0
* @author Daniel Valvano and Jonathan Valvano
* @copyright Copyright 2023 by Jonathan W. Valvano, valvano@mail.utexas.edu,
* @warning AS-IS
* @note For more information see http://users.ece.utexas.edu/~valvano/
* @date August 13, 2023
<table>
<caption id="ADCpins2">ADC pins on the MSPM0G3507</caption>
<tr><th>Pin <th>ADC channel<th>Sensor
<tr><td>PA27 <td>ADC0 channel 0 <td>J1.8 also MKII light interrupt
<tr><td>PA26 <td>ADC0 channel 1 <td>J1.6 MKII microphone in
<tr><td>PA25 <td>ADC0 channel 2 <td>J1.2 MKII Joystick X
<tr><td>PA24 <td>ADC0 channel 3 <td>J3.27 ***free***
<tr><td>PB25 <td>ADC0 channel 4 <td>J19.7 (insert 0ohm R74, no U3 OPA2365)
<tr><td>PB24 <td>ADC0 channel 5 <td>J1.5 also MKII joystick select button
<tr><td>PB20 <td>ADC0 channel 6 <td>J4.36 ***free***
<tr><td>PA22 <td>ADC0 channel 7 <td>J24 MKII Accelerometer Y
<tr><td>PA15 <td>ADC1 channel 0 <td>J3.30 (also DACout)
<tr><td>PA16 <td>ADC1 channel 1 <td>J3.29 ***free***
<tr><td>PA17 <td>ADC1 channel 2 <td>J3.28 ***free***
<tr><td>PA18 <td>ADC1 channel 3 <td>J3.26 MKII Joystick Y
<tr><td>PB17 <td>ADC1 channel 4 <td>J2.18 ***free***
<tr><td>PB18 <td>ADC1 channel 5 <td>J3.25 MKII Accelerometer Z
<tr><td>PB19 <td>ADC1 channel 6 <td>J3.23 MKII Accelerometer X
<tr><td>PA21 <td>ADC1 channel 7 <td>J17.8 (insert R20, remove R3)
</table>
****note to students****<br>
the data sheet says the ADC does not work when clock is 80 MHz
however, the ADC seems to work on my boards at 80 MHz
I suggest you try 80MHz, but if it doesn't work, switch to 40MHz
******************************************************************************/
//#ifndef __ADC_H__
//#define __ADC_H__
#include <ti/devices/msp/msp.h>
#include <stdint.h>
/**
* \brief using ADCVREF_INT means choose internal 2.5V reference for accuracy
*/
#define ADCVREF_INT 0x200
/**
* \brief using ADCVREF_EXT means choose external reference not tested
*/
#define ADCVREF_EXT 0x100
/**
* \brief using ADCVREF_VDDA means choose power line 3.3V reference for 0 to 3.3V range
*/
#define ADCVREF_VDDA 0x000
/**
* Initialize 12-bit ADC0 in software-triggered mode to take
* measurements when the associated function is called.
* One channel is to be measured
* @param channel is the 0 to 7
* @param reference is ADCVREF_INT, ADCVREF_EXT, ADCVREF_VDDA
* @return none
* @brief Initialize 12-bit ADC0
*/
void ADC0_Init(uint32_t channel, uint32_t reference);
/**
* Trigger a single ADC0 measurement,
* wait for it to complete, and return the 12-bit result
* as 0 to 4095.
* The ADC input voltage range is 0 to 3.3V.
* Busy-wait synchronization used.
* @param none
* @return 12-bit result
* @note Assumes ADC0_Init has been called.
* @brief Trigger ADC measurement and wait for result.
*/
uint32_t ADC0_In(void);
/**
* Initialize 12-bit ADC1 in software-triggered mode to take
* measurements when the associated function is called.
* One channel is to be measured
* @param channel is the 0 to 7
* @param reference is ADCVREF_INT, ADCVREF_EXT, ADCVREF_VDDA
* @return none
* @brief Initialize 12-bit ADC0
*/
void ADC1_Init(uint32_t channel, uint32_t reference);
/**
* Trigger a single ADC1 measurement,
* wait for it to complete, and return the 12-bit result
* as 0 to 4095.
* The ADC input voltage range is 0 to 3.3V.
* Busy-wait synchronization used.
* @param none
* @return 12-bit result
* @note Assumes ADC1_Init has been called.
* @brief Trigger ADC measurement and wait for result.
*/
uint32_t ADC1_In(void);
/**
* Initialize 12-bit ADC0 or ADC1 in software-triggered mode to take
* measurements when the associated function is called.
* One channel is to be measured
* @param adc12 is ADC0 or ADC1
* @param channel is the 0 to 7
* @param reference is ADCVREF_INT, ADCVREF_EXT, ADCVREF_VDDA
* @return none
* @brief Initialize 12-bit ADC0
*/
void ADC_Init(ADC12_Regs *adc12, uint32_t channel, uint32_t reference);
/**
* Trigger a single ADC0 or ADC1 measurement,
* wait for it to complete, and return the 12-bit result
* as 0 to 4095.
* The ADC input voltage range is 0 to 3.3V.
* Busy-wait synchronization used.
* @param adc12 is ADC0 or ADC1
* @return 12-bit result
* @note Assumes ADC_Init has been called.
* @brief Trigger ADC measurement and wait for result.
*/
uint32_t ADC_In(ADC12_Regs *adc12);
/**
* Initialize 12-bit ADC0 or ADC1 in software-triggered mode to take
* measurements when the associated function is called.
* Two channels are to be measured
* @param adc12 is ADC0 or ADC1
* @param channel1 is the 0 to 7
* @param channel2 is the 0 to 7
* @param reference is ADCVREF_INT, ADCVREF_EXT, ADCVREF_VDDA
* @return none
* @brief Initialize 12-bit ADC for two channels
*/
void ADC_InitDual(ADC12_Regs *adc12, uint32_t channel1, uint32_t channel2, uint32_t reference);
/**
* Trigger a dual ADC0 or ADC1 measurement,
* wait for it to complete, and return the two 12-bit results
* as 0 to 4095.
* The ADC input voltage range is 0 to 3.3V.
* Busy-wait synchronization used.
* @param adc12 is ADC0 or ADC1
* @param *d1 address into which the first ADC sample will be stored
* @param *d2 address into which the second ADC sample will be stored
* @return none
* @note Assumes ADC_InitDual has been called.
* @brief Trigger two ADC measurements and wait for results.
*/
void ADC_InDual(ADC12_Regs *adc12, uint32_t *d1, uint32_t *d2);
/**
* Initialize 12-bit ADC0 or ADC1 in software-triggered mode to take
* measurements when the associated function is called.
* three channels are to be measured
* @param adc12 is ADC0 or ADC1
* @param channel1 is the 0 to 7
* @param channel2 is the 0 to 7
* @param channel3 is the 0 to 7
* @param reference is ADCVREF_INT, ADCVREF_EXT, ADCVREF_VDDA
* @return none
* @brief Initialize 12-bit ADC for three channels
*/
void ADC_InitTriple(ADC12_Regs *adc12,uint32_t channel1,uint32_t channel2,uint32_t channel3,uint32_t reference);
/**
* Trigger a triple ADC0 or ADC1 measurement,
* wait for it to complete, and return the three 12-bit results
* as 0 to 4095.
* The ADC input voltage range is 0 to 3.3V.
* Busy-wait synchronization used.
* @param adc12 is ADC0 or ADC1
* @param *d1 address into which the first ADC sample will be stored
* @param *d2 address into which the second ADC sample will be stored
* @param *d3 address into which the third ADC sample will be stored
* @return none
* @note Assumes ADC_InitTriple has been called.
* @brief Trigger three ADC measurements and wait for results.
*/
void ADC_InTriple(ADC12_Regs *adc12,uint32_t *d1, uint32_t *d2, uint32_t *d3);
/**
* Initialize 12-bit ADC0 in software-triggered mode to take
* measurements when the associated function is called.
* One channel is to be measured 2^n times in average.
* @param channel is the 0 to 7
* @param n 0 to 7
* @return none
* @brief Initialize 12-bit ADC0 for hardware averaging
* @note uses ADCVREF_VDDA, so range is 0 to 3.3V
*/
void ADC0_InitAve(uint32_t channel, uint32_t n);
//#endif // __ADC_H__
/** @}*/

215
RTOS_Labs_common/ADCTimer.c Normal file
View File

@@ -0,0 +1,215 @@
/* ADCTimer.c
* Jonathan Valvano
* May 19, 2025
* Derived from adc12_single_conversion_vref_internal_LP_MSPM0G3507_nortos_ticlang
* adc12_single_conversion_LP_MSPM0G3507_nortos_ticlang
* adc12_triggered_by_timer_event_LP_MSPM0G3507_nortos_ticlang
*/
// ****note to students****
// the data sheet says the ADC does not work when clock is 80 MHz
// however, the ADC seems to work on my boards at 80 MHz
// I suggest you try 80MHz, but if it doesn't work, switch to 40MHz
#include <ti/devices/msp/msp.h>
#include "../inc/ADCTimer.h"
#include "../inc/Clock.h"
// Assumes 80 MHz bus
// for 80MHz bus clock, Timer G0 clock is ULPCLK 40MHz
// frequency = TimerClock/prescale/period
// e.g., period=5000,prescale=8 is 40MHz/5000/8 = 1kHz
void ADC0_TimerG0_Init(uint32_t channel, uint32_t reference,uint16_t period, uint32_t prescale, uint32_t priority){
// Reset ADC Timer G0 and VREF
// RSTCLR
// bits 31-24 unlock key 0xB1
// bit 1 is Clear reset sticky bit
// bit 0 is reset ADC
ADC0->ULLMEM.GPRCM.RSTCTL = 0xB1000003;
if(reference == ADCVREF_INT){
VREF->GPRCM.RSTCTL = 0xB1000003;
}
TIMG0->GPRCM.RSTCTL = 0xB1000003;
// Enable power ADC G0 and VREF
// PWREN
// bits 31-24 unlock key 0x26
// bit 0 is Enable Power
ADC0->ULLMEM.GPRCM.PWREN = 0x26000001;
if(reference == ADCVREF_INT){
VREF->GPRCM.PWREN = 0x26000001;
}
TIMG0->GPRCM.PWREN = 0x26000001;
Clock_Delay(24); // time for ADC Vref G0 to power up
TIMG0->CLKSEL = 0x08; // bus clock
TIMG0->CLKDIV = 0x00; // divide by 1
TIMG0->COMMONREGS.CPS = prescale-1; // divide by prescale,
TIMG0->COUNTERREGS.LOAD = period-1; // set reload register
TIMG0->FPUB_0 = 1; // publish on channel 1
// bits 29-28 CVAE 00 (set to LOAD value)
TIMG0->COUNTERREGS.CTRCTL = 0x02;
// bits 5-4 CM =0, down
// bits 3-1 REPEAT =001, continue
// bit 0 EN enable (0 for disable, 1 for enable)
TIMG0->GEN_EVENT0.IMASK = 1; // hardware event published on Chan1
TIMG0->COMMONREGS.CCLKCTL = 1;
ADC0->ULLMEM.GPRCM.CLKCFG = 0xA9000000; // ULPCLK
// bits 31-24 key=0xA9
// bit 5 CCONSTOP= 0 not continuous clock in stop mode
// bit 4 CCORUN= 0 not continuous clock in run mode
// bit 1-0 0=ULPCLK,1=SYSOSC,2=HFCLK
ADC0->ULLMEM.CLKFREQ = 7; // 40 to 48 MHz
ADC0->ULLMEM.CTL0 = 0x03010000;
// bits 26-24 = 011 divide by 8
// bit 16 PWRDN=1 for manual, =0 power down on completion, if no pending trigger
// bit 0 ENC=1 enable (1 to 0 will end conversion)
ADC0->ULLMEM.CTL1 = 0x00000001;
// bits 30-28 =0 no shift
// bits 26-24 =0 no averaging
// bit 20 SAMPMODE=0 timer triggers
// bits 17-16 CONSEQ=01 ADC at start will be sampled once, 10 for repeated sampling
// bit 8 SC=0 for stop, =1 to software start
// bit 0 TRIGSRC=1 timer trigger
ADC0->ULLMEM.CTL2 = 0x00000000;
// bits 28-24 ENDADD (which MEMCTL to end)
// bits 20-16 STARTADD (which MEMCTL to start)
// bits 15-11 SAMPCNT (for DMA)
// bit 10 FIFOEN=0 disable FIFO
// bit 8 DMAEN=0 disable DMA
// bits 2-1 RES=0 for 12 bit (=1 for 10bit,=2for 8-bit)
// bit 0 DF=0 unsigned formant (1 for signed, left aligned)
ADC0->ULLMEM.MEMCTL[0] = reference+channel;
// bit 28 WINCOMP=0 disable window comparator
// bit 24 TRIG trigger policy, =0 for auto next, =1 for next requires trigger
// bit 20 BCSEN=0 disable burn out current
// bit 16 = AVGEN =0 for no averaging
// bit 12 = STIME=0 for SCOMP0
// bits 9-8 VRSEL = 10 for internal VREF,(00 for VDDA)
// bits 4-0 channel = 0 to 7 available
ADC0->ULLMEM.SCOMP0 = 0x64; // sample clocks
ADC0->ULLMEM.SCOMP1 = 0x32; // sample clocks
if(reference == ADCVREF_INT){
VREF->CLKSEL = 0x00000008; // bus clock
VREF->CLKDIV = 0; // divide by 1
VREF->CTL0 = 0x0001;
// bit 8 SHMODE = off
// bit 7 BUFCONFIG=0 for 2.4 (=1 for 1.4)
// bit 0 is enable
VREF->CTL2 = 0;
// bits 31-16 HCYCLE=0
// bits 15-0 SHCYCLE=0
while((VREF->CTL1&0x01)==0){}; // wait for VREF to be ready
}
ADC0->ULLMEM.FSUB_0 = 0x00000001; // subscribe to chan 1
ADC0->ULLMEM.CPU_INT.IMASK = 0x00000100; //MEMRESIFG0
ADC0->ULLMEM.CTL0 |= 1; // start
NVIC->ISER[0] = 1 << 4; // ADC0 interrupt
NVIC->IP[1] = (NVIC->IP[1]&(~0x000000FF))|(priority<<6); // set priority (bits 7,6) IRQ 4
TIMG0->COUNTERREGS.CTRCTL |= 0x01;
}
// Assumes 80 MHz bus
// power Domain PD0
// for 80MHz bus clock, Timer G8 clock is ULPCLK 40MHz
// frequency = TimerClock/prescale/period
// e.g., period=5000,prescale=8 is 40MHz/5000/8 = 1kHz
void ADC1_TimerG8_Init(uint32_t channel, uint32_t reference,uint16_t period, uint32_t prescale, uint32_t priority){
// Reset ADC Timer G8 and VREF
// RSTCLR
// bits 31-24 unlock key 0xB1
// bit 1 is Clear reset sticky bit
// bit 0 is reset ADC
ADC1->ULLMEM.GPRCM.RSTCTL = 0xB1000003;
if(reference == ADCVREF_INT){
VREF->GPRCM.RSTCTL = 0xB1000003;
}
TIMG8->GPRCM.RSTCTL = 0xB1000003;
// Enable power ADC G0 and VREF
// PWREN
// bits 31-24 unlock key 0x26
// bit 0 is Enable Power
ADC1->ULLMEM.GPRCM.PWREN = 0x26000001;
if(reference == ADCVREF_INT){
VREF->GPRCM.PWREN = 0x26000001;
}
TIMG8->GPRCM.PWREN = 0x26000001;
Clock_Delay(24); // time for ADC Vref G8 to power up
TIMG8->CLKSEL = 0x08; // bus clock
TIMG8->CLKDIV = 0x00; // divide by 1
TIMG8->COMMONREGS.CPS = prescale-1; // divide by prescale,
TIMG8->COUNTERREGS.LOAD = period-1; // set reload register
TIMG8->FPUB_0 = 2; // publish on channel 2
// bits 29-28 CVAE 00 (set to LOAD value)
TIMG8->COUNTERREGS.CTRCTL = 0x02;
// bits 5-4 CM =0, down
// bits 3-1 REPEAT =001, continue
// bit 0 EN enable (0 for disable, 1 for enable)
TIMG8->GEN_EVENT0.IMASK = 1; // hardware event published on Chan1
TIMG8->COMMONREGS.CCLKCTL = 1;
ADC1->ULLMEM.GPRCM.CLKCFG = 0xA9000000; // ULPCLK
// bits 31-24 key=0xA9
// bit 5 CCONSTOP= 0 not continuous clock in stop mode
// bit 4 CCORUN= 0 not continuous clock in run mode
// bit 1-0 0=ULPCLK,1=SYSOSC,2=HFCLK
ADC1->ULLMEM.CLKFREQ = 7; // 40 to 48 MHz
ADC1->ULLMEM.CTL0 = 0x03010000;
// bits 26-24 = 011 divide by 8
// bit 16 PWRDN=1 for manual, =0 power down on completion, if no pending trigger
// bit 0 ENC=1 enable (1 to 0 will end conversion)
ADC1->ULLMEM.CTL1 = 0x00000001;
// bits 30-28 =0 no shift
// bits 26-24 =0 no averaging
// bit 20 SAMPMODE=0 timer triggers
// bits 17-16 CONSEQ=01 ADC at start will be sampled once, 10 for repeated sampling
// bit 8 SC=0 for stop, =1 to software start
// bit 0 TRIGSRC=1 timer trigger
ADC1->ULLMEM.CTL2 = 0x00000000;
// bits 28-24 ENDADD (which MEMCTL to end)
// bits 20-16 STARTADD (which MEMCTL to start)
// bits 15-11 SAMPCNT (for DMA)
// bit 10 FIFOEN=0 disable FIFO
// bit 8 DMAEN=0 disable DMA
// bits 2-1 RES=0 for 12 bit (=1 for 10bit,=2for 8-bit)
// bit 0 DF=0 unsigned formant (1 for signed, left aligned)
ADC1->ULLMEM.MEMCTL[0] = reference+channel;
// bit 28 WINCOMP=0 disable window comparator
// bit 24 TRIG trigger policy, =0 for auto next, =1 for next requires trigger
// bit 20 BCSEN=0 disable burn out current
// bit 16 = AVGEN =0 for no averaging
// bit 12 = STIME=0 for SCOMP0
// bits 9-8 VRSEL = 10 for internal VREF,(00 for VDDA)
// bits 4-0 channel = 0 to 7 available
ADC1->ULLMEM.SCOMP0 = 0x64; // sample clocks
ADC1->ULLMEM.SCOMP1 = 0x32; // sample clocks
if(reference == ADCVREF_INT){
VREF->CLKSEL = 0x00000008; // bus clock
VREF->CLKDIV = 0; // divide by 1
VREF->CTL0 = 0x0001;
// bit 8 SHMODE = off
// bit 7 BUFCONFIG=0 for 2.4 (=1 for 1.4)
// bit 0 is enable
VREF->CTL2 = 0;
// bits 31-16 HCYCLE=0
// bits 15-0 SHCYCLE=0
while((VREF->CTL1&0x01)==0){}; // wait for VREF to be ready
}
ADC1->ULLMEM.FSUB_0 = 0x00000002; // subscribe to chan 2
ADC1->ULLMEM.CPU_INT.IMASK = 0x00000100; //MEMRESIFG0
ADC1->ULLMEM.CTL0 |= 1; // start
NVIC->ISER[0] = 1 << 5; // ADC1 interrupt
NVIC->IP[1] = (NVIC->IP[1]&(~0x0000FF00))|(priority<<14); // set priority (bits 15,14) IRQ 5
TIMG8->COUNTERREGS.CTRCTL |= 0x01;
}

124
RTOS_Labs_common/ADCTimer.h Normal file
View File

@@ -0,0 +1,124 @@
/*!
* @defgroup ADC
* @brief Analog to digital conversion
<table>
<caption id="ADCpinsT">ADC pins on the MSPM0G3507</caption>
<tr><th>Pin <th>ADC channel<th>Sensor
<tr><td>PA27 <td>ADC0 channel 0 <td>J1.8 also MKII light interrupt
<tr><td>PA26 <td>ADC0 channel 1 <td>J1.6 MKII microphone in
<tr><td>PA25 <td>ADC0 channel 2 <td>J1.2 MKII Joystick X
<tr><td>PA24 <td>ADC0 channel 3 <td>J3.27 ***free***
<tr><td>PB25 <td>ADC0 channel 4 <td>J19.7 (insert 0ohm R74, no U3 OPA2365)
<tr><td>PB24 <td>ADC0 channel 5 <td>J1.5 also MKII joystick select button
<tr><td>PB20 <td>ADC0 channel 6 <td>J4.36 ***free***
<tr><td>PA22 <td>ADC0 channel 7 <td>J24 MKII Accelerometer Y
</table>
* @{*/
/**
* @file ADCTimer.h
* @brief Initialize 12-bit ADC0
* @details ADC input, timer trigger, 12-bit conversion<br>
* The ADC allows two possible references: 2.5V or 3.3V.<br>
* The internal 2.5V reference is lower noise, but limits the range to 0 to 2.5V.<br>
* The other possibility is to use the analog voltage supply at 3.3V,
* making the ADC range 0 to 3.3V. In this driver, the 3.3V range is selected.
* There are several configurations (each with initialization and a
* read ADC with software trigger, busy-wait function)<br>
* @version ECE319K v1.3
* @author Daniel Valvano and Jonathan Valvano
* @copyright Copyright 2025 by Jonathan W. Valvano, valvano@mail.utexas.edu,
* @warning AS-IS
* @note For more information see http://users.ece.utexas.edu/~valvano/
* @date May 23, 2025
<table>
<caption id="ADCpins2T">ADC pins on the MSPM0G3507</caption>
<tr><th>Pin <th>ADC channel<th>Sensor
<tr><td>PA27 <td>ADC0 channel 0 <td>J1.8 also MKII light interrupt
<tr><td>PA26 <td>ADC0 channel 1 <td>J1.6 MKII microphone in
<tr><td>PA25 <td>ADC0 channel 2 <td>J1.2 MKII Joystick X
<tr><td>PA24 <td>ADC0 channel 3 <td>J3.27 ***free***
<tr><td>PB25 <td>ADC0 channel 4 <td>J19.7 (insert 0ohm R74, no U3 OPA2365)
<tr><td>PB24 <td>ADC0 channel 5 <td>J1.5 also MKII joystick select button
<tr><td>PB20 <td>ADC0 channel 6 <td>J4.36 ***free***
<tr><td>PA22 <td>ADC0 channel 7 <td>J24 MKII Accelerometer Y
<tr><td>PA15 <td>ADC1 channel 0 <td>J3.30 (also DACout)
<tr><td>PA16 <td>ADC1 channel 1 <td>J3.29 ***free***
<tr><td>PA17 <td>ADC1 channel 2 <td>J3.28 ***free***
<tr><td>PA18 <td>ADC1 channel 3 <td>J3.26 MKII Joystick Y
<tr><td>PB17 <td>ADC1 channel 4 <td>J2.18 ***free***
<tr><td>PB18 <td>ADC1 channel 5 <td>J3.25 MKII Accelerometer Z
<tr><td>PB19 <td>ADC1 channel 6 <td>J3.23 MKII Accelerometer X
<tr><td>PA21 <td>ADC1 channel 7 <td>J17.8 (insert R20, remove R3)
</table>
****note to students****<br>
the data sheet says the ADC does not work when clock is 80 MHz
however, the ADC seems to work on my boards at 80 MHz
I suggest you try 80MHz, but if it doesn't work, switch to 40MHz
******************************************************************************/
#ifndef __ADCTimer_H__
#define __ADCTimer_H__
#include <ti/devices/msp/msp.h>
/**
* \brief using ADCVREF_INT means choose internal 2.5V reference for accuracy
*/
#define ADCVREF_INT 0x200
/**
* \brief using ADCVREF_EXT means choose external reference not tested
*/
#define ADCVREF_EXT 0x100
/**
* \brief using ADCVREF_VDDA means choose power line 3.3V reference for 0 to 3.3V range. This is the mode we use in ECE319K
*/
#define ADCVREF_VDDA 0x000
/**
* Initialize ADC0 for Timer G0 triggered sampling<br>
* Assuming 80 MHz bus, the sampling is 40MHz/period/prescale<br>
* Pin channel<br>
* PA27 0 <br>
* PA26 1 <br>
* PA25 2 <br>
* PA24 3 <br>
* PB25 4 <br>
* PB24 5 <br>
* PB20 6 <br>
* PA22 7
* @param channel is the 0 to 7
* @param reference is ADCVREF_INT, ADCVREF_EXT, ADCVREF_VDDA
* @param period is 16-bit period in bus cycles on Timer G0
* @param prescale is 8-bit prescale in bus cycles on Timer G0
* @param priority is 0 to 3 for ADC interrupts
* @return none
* @note uses internal pub/sub channel 1
* @brief Initialize 12-bit ADC0
*/
void ADC0_TimerG0_Init(uint32_t channel, uint32_t reference,uint16_t period, uint32_t prescale, uint32_t priority);
/**
* Initialize ADC1 for Timer G8 triggered sampling<br>
* Assuming 80 MHz bus, the sampling is 40MHz/period/prescale<br>
* Pin channel<br>
* PA15 0 <br>
* PA16 1 <br>
* PA17 2 <br>
* PA18 3 <br>
* PB17 4 <br>
* PB18 5 <br>
* PB19 6 <br>
* PA21 7
* @param channel is the 0 to 7
* @param reference is ADCVREF_INT, ADCVREF_EXT, ADCVREF_VDDA
* @param period is 16-bit period in bus cycles on Timer G8
* @param prescale is 8-bit prescale in bus cycles on Timer G8
* @param priority is 0 to 3 for ADC interrupts
* @return none
* @note uses internal pub/sub channel 2
* @brief Initialize 12-bit ADC1
*/
void ADC1_TimerG8_Init(uint32_t channel, uint32_t reference,uint16_t period, uint32_t prescale, uint32_t priority);
#endif // __ADCTimer_H__
/** @}*/

612
RTOS_Labs_common/CAN.c Normal file
View File

@@ -0,0 +1,612 @@
// CAN.c
// Runs on MSPM0G3507
// Use FDCAN0 to communicate on CAN bus PA12 and PA13
// Jonathan Valvano
// December 11, 2025
// derived from can_to_uart_bridge_LP_MSPM0G3507_nortos_ticlang
/*
Copyright 2025 by Jonathan W. Valvano, valvano@mail.utexas.edu
You may use, edit, run or distribute this file
as long as the above copyright notice remains
THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED
OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
VALVANO SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
For more information about my classes, my research, and my books, see
http://users.ece.utexas.edu/~valvano/
*/
// Use TCAN1057AVDRQ1 (not TCAN1057A-Q1, the version with pin 5 nc)
// Pin1 TXD ---- CAN_Tx PA12 FD-CAN module 0 transmit
// Pin2 Vss ---- ground
// Pin3 VCC ---- +5V with 0.1uF cap to ground
// Pin4 RXD ---- CAN_Rx PA13 FD-CAN module 0 receive (0 to 3.3V)
// Pin5 VIO ---- +3.3V (digital interface supply)
// Pin6 CANL ---- to other CANL on network
// Pin7 CANH ---- to other CANH on network
// Pin8 RS ---- ground, Slope-Control Input (maximum slew rate)
// 120 ohm across CANH, CANL on both ends of network
#include <stdint.h>
#include <ti/devices/msp/msp.h>
#include "../inc/Clock.h"
#include "../RTOS_Labs_common/CAN.h"
#include "../RTOS_Labs_common/LaunchPad.h"
#include "../RTOS_Labs_common/OS.h"
uint32_t CAN_PID,CAN_CREL; // version
Sema4_t CanDataAvailable;
__STATIC_INLINE uint32_t READ_REG32_RAW(uint32_t addr){
uint32_t regVal = *(volatile uint32_t *) addr;
return (regVal);
}
#define CAN_DEBUG 0
uint32_t objSize4[8] = {4, 5, 6, 7, 8, 10, 14, 18};
typedef struct {
/*! Rx FIFO number
* One of @ref DL_MCAN_RX_FIFO_NUM
*/
uint32_t num;
/*! Rx FIFO Fill Level */
uint32_t fillLvl;
/*! Rx FIFO Get Index */
uint32_t getIdx;
/*! Rx FIFO Put Index */
uint32_t putIdx;
/*! Rx FIFO Full
* 0 = Rx FIFO not full
* 1 = Rx FIFO full
*/
uint32_t fifoFull;
/*! Rx FIFO Message Lost */
uint32_t msgLost;
} MCAN_RxFIFOStatus_t;
/**
* @brief Structure for MCAN Rx Buffer element.
*/
#if CAN_DEBUG
typedef struct {
// Identifier
uint32_t id;
// Remote Transmission Request
// 0 = Received frame is a data frame
// 1 = Received frame is a remote frame
uint32_t rtr;
// Extended Identifier
// 0 = 11-bit standard identifier
// 1 = 29-bit extended identifier
uint32_t xtd;
// Error State Indicator
// 0 = Transmitting node is error active
// 1 = Transmitting node is error passive
uint32_t esi;
// Rx Timestamp
uint32_t rxts;
//Data Length Code
// 0-8 = CAN + CAN FD: received frame has 0-8 data bytes
uint32_t dlc;
// Bit Rat Switching
// 0 = Frame received without bit rate switching
// 1 = Frame received with bit rate switching
uint32_t brs;
// FD Format
// 0 = Standard frame format
// 1 = CAN FD frame format (new DLC-coding and CRC)
uint32_t fdf;
// Filter Index
uint32_t fidx;
// Accepted Non-matching Frame
// 0 = Received frame matching filter index FIDX
// 1 = Received frame did not match any Rx filter element
uint32_t anmf;
// Data bytes.
// Only first dlc number of bytes are valid.
uint8_t data[8];
} MCAN_RxBufElement_t;
MCAN_RxBufElement_t rxMsg4;
#endif
// CAN message
typedef struct {
uint32_t id; // identification of the message
uint8_t dlc; // Data Length Code
uint8_t data[8]; // Data bytes limited to 8 bytes
} CANmsg_t;
#define CANFIFOSIZE 8
CANmsg_t CANFIFO[CANFIFOSIZE]; // must be a power of two
uint32_t CANGetI = 0;
uint32_t CANPutI = 0;
volatile uint32_t CanInterruptLine1Status=0;
// SYSPLLCLK1 80MHz
// bit rate 1Mbps
void CAN_Init(void){uint32_t regVal;
// assumes Port A has been reset and enabled previously by LaunchPad_Init
CANFD0->MCANSS.RSTCTL = 0xB1000003; // reset CAN
CANFD0->MCANSS.PWREN = 0x26000001; // enable power
Clock_Delay(200); // time for CAN to power up
IOMUX->SECCFG.PINCM[PA12INDEX] = (uint32_t) 0x00000085; // PA12 CAN Tx
IOMUX->SECCFG.PINCM[PA13INDEX] = (uint32_t) 0x00040086; // PA13 CAN Rx
Clock_Delay(1000); // time for CAN to power up
// Enables the MCAN functional module clock
CANFD0->MCANSS.TI_WRAPPER.MSP.MCANSS_CLKEN = 1;
// Clock_Delay(24); // time for CAN to power up
// Clock CAN from HFCLK
// SYSCTL->SOCLOCK.GENCLKCFG = (SYSCTL->SOCLOCK.GENCLKCFG&~0x00000100);
SYSCTL->SOCLOCK.GENCLKCFG |= 0x00000100; // SYSPLLCLK1
// bit 8 0 CANCLK is HFCLK, 1 SYSPLLCLK1
// Clock divide ratio specification. Enables configuring clock divide
// settings for the MCAN functional clock input to the MCAN-SS.
// 0h (R/W) = Divides input clock by 1
// 1h (R/W) = Divides input clock by 2
// 2h (R/W) = Divides input clock by 4
// 3h (R/W) = Divides input clock by 1
CANFD0->MCANSS.TI_WRAPPER.MSP.MCANSS_CLKDIV = 1; // divide by 2
// Clock_Delay(100); // time for CAN to power up
// Get MCANSS Revision ID. (optional FYI only)
CAN_PID = CANFD0->MCANSS.TI_WRAPPER.PROCESSORS.MCANSS_REGS.MCANSS_PID;
// 31-30 SCHEME R 1h PID Register Scheme
// 27-16 MODULE_ID R 8E0h Module Identification Number
// 10-8 MAJOR R 1h Major Revision of the MCAN Subsystem
// 5-0 MINOR R 1h Minor Revision of the MCAN Subsystem
CAN_CREL = CANFD0->MCANSS.MCAN.MCAN_CREL; // core release (optional FYI only)
// 31-28 REL R 3h Core Release. One digit, BCD-coded.
// 27-24 STEP R 2h Step of Core Release. One digit, BCD-coded.
// 23-20 SUBSTEP R 3h Sub-Step of Core Release. One digit, BCD-coded.
// 19-16 YEAR R 8h Time Stamp Year. One digit, BCD-coded.
// 15-8 MON R 6h Time Stamp Month. Two digits, BCD-coded.
// 7-0 DAY R 8h Time Stamp Day. Two digits, BCD-coded.
// Wait for Memory initialization to be completed.
// bit 1 MEM_INIT_DONE R 0h Memory Initialization Done.
// 0 Message RAM initialization is in progress
// 1 Message RAM is initialized for use
while((CANFD0->MCANSS.TI_WRAPPER.PROCESSORS.MCANSS_REGS.MCANSS_STAT&0x02)==0){};
// Put MCAN in SW initialization mode.
// MCAN_CCCR INIT R/W 1h Initialization
// 0 Normal Operation
// 1 Initialization is started
// Note: Due to the synchronization mechanism between the two clock
// domains, there may be a delay until the value written to INIT can
// be read back. Therefore the programmer has to assure that the
// previous value written to INIT has been accepted by reading INIT
// before setting INIT to a new value
CANFD0->MCANSS.MCAN.MCAN_CCCR |= 0x01; // SW initialization mode
while((CANFD0->MCANSS.MCAN.MCAN_CCCR&0x01)==0){};
// Wait while MCAN is not in SW initialization mode
// Initialize MCAN module
// Configure MCAN wakeup and clock stop controls
regVal = CANFD0->MCANSS.TI_WRAPPER.PROCESSORS.MCANSS_REGS.MCANSS_CTRL;
regVal |= (1<<4); // wkupReqEnable
regVal |= (1<<5); // autoWkupEnable
regVal |= (1<<3); // DBGSUSP_FREE emulationEnable
CANFD0->MCANSS.TI_WRAPPER.PROCESSORS.MCANSS_REGS.MCANSS_CTRL = regVal;
CANFD0->MCANSS.MCAN.MCAN_CCCR |= 0x02; //ProtectedRegAccessUnlock
// Configure MCAN mode(FD vs Classic CAN operation) and controls
regVal = CANFD0->MCANSS.MCAN.MCAN_CCCR;
regVal |= (1<<8); // fdMode, Flexible Datarate Operation Enable
regVal |= (1<<9); // brsEnable, Bit Rate Switch Enable
regVal &= ~(1<<14); // txpEnable=false, Transmit Pause
regVal &= ~(1<<13); // efbi=false, Edge Filtering during Bus Integration
regVal &= ~(1<<12); // pxhddisable=0, Protocol Exception Handling Disable
regVal &= ~(1<<6); // darEnable=0, Disable Automatic Retransmission
CANFD0->MCANSS.MCAN.MCAN_CCCR = regVal;
// Configure Transceiver Delay Compensation
CANFD0->MCANSS.MCAN.MCAN_TDCR = (6<<8)+ 10; //tdco=6,tdcf=10
// Configure MSG RAM watchdog counter preload value *
CANFD0->MCANSS.MCAN.MCAN_RWD = 255; // WDC=wdcPreload=255
// Enable/Disable Transceiver Delay Compensation
CANFD0->MCANSS.MCAN.MCAN_DBTP |= (1<<23); // Transmitter Delay Compensation
// Configure Bit timings
CANFD0->MCANSS.MCAN.MCAN_NBTP = // MCAN Nominal Bit Timing and Prescaler Register
(31<<25) // nomSynchJumpWidth Nominal (Re)Synchronization Jump Width
|(0<<16) // nomRatePrescalar Nominal Bit Rate Prescaler
|(126<<8) // nomTimeSeg1Nominal Time Segment Before Sample Point
|(31<<0); // nomTimeSeg2 Nominal Time Segment After Sample Point.
CANFD0->MCANSS.MCAN.MCAN_DBTP = // MCAN Data Bit Timing and Prescaler Register
(1<<23) // TDC =1 delay compensation
|(14<<8) // dataTimeSeg1
|(3<<4) // dataTimeSeg2
|(3<<0); // dataSynchJumpWidth
// Configure Message RAM Sections
// start end num size
// 0 0 0 0 Standard ID Filter List (not used)
// 0 0 0 0 Extended ID Filter List (not used)
// 448 ??? 2 7(64) Tx Buffers (only uses the first buffer)
// 164 172 10 2 Tx EventFIFOs (not used)
// 8 368 5 7(64) RxFifo0 (used)
// 368 448 5 0(8) RxFifo1 (not used)
// 600 672 1 7(64) Rx Buffer (not used)
// Configure Message Filters
CANFD0->MCANSS.MCAN.MCAN_SIDFC = 0; // MCAN Standard ID Filter Configuration
// No standard Message ID filter
// No Filter List Standard Start Address
CANFD0->MCANSS.MCAN.MCAN_XIDFC = 0;
// no Filter List Extended Start Address
// No No extended Message ID filter
// Configure Rx FIFO 0 section
// if (0U != msgRAMConfigParams->rxFIFO0size) {
CANFD0->MCANSS.MCAN.MCAN_RXF0C =
(0<<31) // F0OM 0 FIFO 0 blocking mode
|(0<<24) // F0WM 0 Watermark interrupt disabled
|(5<<16) // F0S Rx FIFO 0 number of elements (means you can queue 5 received messages)
|(2<<2); // F0SA Rx FIFO 0 Start Address
// Configure Rx FIFO0 elements size */
CANFD0->MCANSS.MCAN.MCAN_RXESC = (CANFD0->MCANSS.MCAN.MCAN_RXESC&(~0x07))|7;
// F0DS Rx FIFO0 Data Field Size, 111 64 byte data field
// Configure Rx FIFO 1 section (not used)
CANFD0->MCANSS.MCAN.MCAN_RXF1C =
(0<<31) // F1OM 0 FIFO 0 blocking mode
|(3<<24) // F1WM 3 Level for Rx FIFO 1 watermark interrupt
|(5<<16) // F1S Rx FIFO 1 Size
|(368<<2); // F1SA Rx FIFO 1 Start Address
// Configure Rx FIFO1 elements size (not used)
CANFD0->MCANSS.MCAN.MCAN_RXESC = (CANFD0->MCANSS.MCAN.MCAN_RXESC&(~0x70))|0;
// F1DS Rx FIFO 1 Data Field Size 000 8-byte data field
// Configure Rx Buffer Start Address (not used)
CANFD0->MCANSS.MCAN.MCAN_RXBC = (150<<2); // Rx Buffer Start Address
// Configure Rx Buffer elements size (not used)
CANFD0->MCANSS.MCAN.MCAN_RXESC = (CANFD0->MCANSS.MCAN.MCAN_RXESC&(~0x700))|0x700;
// RBDS Rx Buffer Data Field Size 111 64 byte data field
// Configure Tx Event FIFO section
CANFD0->MCANSS.MCAN.MCAN_TXEFC =
(0<<24) // EFWM Event FIFO Watermark 0 Watermark interrupt disabled
|(2<<16) // EFS Event FIFO Size Number of Tx Event FIFO elements
|(41<<2); // EFSA Event FIFO Start Address.
// TX buffer configuration (uses first buffer at address 448)
CANFD0->MCANSS.MCAN.MCAN_TXBC =
112<<2 // TBSA txStartAddr
| (2<<16) // TDTB txBufNum, 2 buffers (uses first one)
| (10<<24) // TFQS txFIFOSize, 10 fifos
| (0<<30); // TFQM, 0 Tx FIFO operation
CANFD0->MCANSS.MCAN.MCAN_TXESC = 7; // TBDS, 64 byte data field txBufElemSize
// Set Extended ID Mask (does not use extended ID)
CANFD0->MCANSS.MCAN.MCAN_XIDAM = 0x1FFFFFFF;
// set access lock
CANFD0->MCANSS.MCAN.MCAN_CCCR &= ~0x02; // Access lock
// Put MCAN into normal mode mode, stop SW initialization mode
CANFD0->MCANSS.MCAN.MCAN_CCCR &= ~0x01;
while((CANFD0->MCANSS.MCAN.MCAN_CCCR&0x01)==1){}; // wait for normal mode
// Initialize semaphore for read
OS_InitSemaphore(&CanDataAvailable, 0);
}
void CAN_EnableInterrupts(uint32_t priority){
// Enable MCAN module Interrupts
CANFD0->MCANSS.MCAN.MCAN_IE |= 0x3FEF1201;
// MCAN_IE Field Descriptions
// 29 ARAE 1 Access to Reserved Address Enable
// 28 PEDE 1 Protocol Error in Data Phase Enable
// 27 PEAE 1 Protocol Error in Arbitration Phase Enable
// 26 WDIE 1 Watchdog Interrupt Enable
// 25 BOE 1 Bus_Off Status Enable
// 24 EWE 1 Warning Status Enable
// 23 EPE 1 Error Passive Enable
// 22 ELOE 1 Error Logging Overflow Enable
// 21 BEUE 1 Bit Error Uncorrected Enable
// 20 BECE 0 Bit Error Corrected Enable
// 19 DRXE 1 Message Stored to Dedicated Rx Buffer Enable
// 18 TOOE 1 Timeout Occurred Enable
// 17 MRAFE 1 Message RAM Access Failure Enable
// 16 TSWE 1 Timestamp Wraparound Enable
// 15 TEFLE 0 Tx Event FIFO Element Lost Enable
// 14 TEFFE 0 Tx Event FIFO Full Enable
// 13 TEFWE 0 Tx Event FIFO Watermark Reached Enable
// 12 TEFNE 1 Tx Event FIFO New Entry Enable
// 11 TFEE 0 Tx FIFO Empty Enable
// 10 TCFE 0 Transmission Cancellation Finished Enable
// 9 TCE 1 Transmission Completed Enable
// 8 HPME 0 High Priority Message Enable
// 7 RF1LE 0 Rx FIFO 1 Message Lost Enable
// 6 RF1FE 0 Rx FIFO 1 Full Enable
// 5 RF1WE 0 Rx FIFO 1 Watermark Reached Enable
// 4 RF1NE 0 Rx FIFO 1 New Message Enable
// 3 RF0LE 0 Rx FIFO 0 Message Lost Enable
// 2 RF0FE 0 Rx FIFO 0 Full Enable
// 1 RF0WE 0 Rx FIFO 0 Watermark Reached Enable
// 0 RF0NE 1 Rx FIFO 0 New Message Enable (only one used)
CANFD0->MCANSS.MCAN.MCAN_ILS |= 0x3FEFFFFF; // select all Line 1
CANFD0->MCANSS.MCAN.MCAN_ILE = 0x02; // line 1 enable
// Enable MSPM0 MCAN interrupt
CANFD0->MCANSS.TI_WRAPPER.MSP.CPU_INT.ICLR = 0x02; // line 1 clear
CANFD0->MCANSS.TI_WRAPPER.MSP.CPU_INT.IMASK |= 0x02; // line 1 enable
NVIC->ICPR[0] = (1 << 6); // NVIC_ClearPendingIRQ(6)
NVIC->ISER[0] = (1 << 6); // NVIC_EnableIRQ(6)
NVIC->IP[1] = (NVIC->IP[1]& (~0x00FF0000))|(priority<<22); // 23 - 22 CANFD0_IRQHandler
}
__STATIC_INLINE void WRITE_REG32_RAW(uint32_t addr, uint32_t value){
*(volatile uint32_t *) addr = value;
return;
}
// 0 if failure
// 1 if ok
int CAN_Send(uint32_t id, uint32_t dlc, uint8_t *data){
uint16_t count;
uint32_t regVal, loopCnt;
uint32_t elemAddr;
if(id > 2047) return 1;
if(dlc > 8) return 1;
// always uses buffer 0
elemAddr = CANFD0->MCANSS.MCAN.MCAN_TXBC&0xFFFC; // bits 15:2, 112*4= 448
regVal = (((uint32_t)(id << 18)) | // 11-bit ID 28:18,
((uint32_t)(0 << 29 )) | // rtr=0 Transmit data frame
((uint32_t)(0 << 30 )) | // xtd=0 11-bit standard identifier
((uint32_t)(0 << 31 ))); // esi=0 ESI bit in CAN FD format depends only on error passive flag
WRITE_REG32_RAW(((uint32_t)CANFD0 + (uint32_t) elemAddr), regVal); //448,0, 0x40508000=0
elemAddr += 4U;
regVal = (((uint32_t)(dlc << 16 )) | // dlc = data length (0to 8)
((uint32_t)(1 << 20 )) | // brs=1, CAN FD frames transmitted with bit rate switching
((uint32_t)(1 << 21 )) | // fdf=1, Frame transmitted in CAN FD format
((uint32_t)(1 << 23 )) | // efc=1, Store Tx events
((uint32_t)(0xAAU<< 24 ))); // mm = Message Marker
WRITE_REG32_RAW(((uint32_t) CANFD0 + (uint32_t) elemAddr), regVal);
elemAddr += 4U;
loopCnt = 0U;
// Framing words out of the payload bytes and writing it to message RAM
while ((4U <= (dlc - loopCnt)) && (0U != (dlc - loopCnt))){
regVal = ((uint32_t) data[loopCnt] |
((uint32_t) data[(loopCnt + 1U)] << 8U) |
((uint32_t) data[(loopCnt + 2U)] << 16U) |
((uint32_t) data[(loopCnt + 3U)] << 24U));
WRITE_REG32_RAW(((uint32_t) CANFD0 + (uint32_t) elemAddr), regVal);
elemAddr += 4U;
loopCnt += 4U;
}
// Framing a word out of remaining payload bytes and writing it to message RAM
if (0U < (dlc - loopCnt)){
regVal = ((uint32_t) data[loopCnt] |
((uint32_t) data[(loopCnt + 1U)] << 8U) |
((uint32_t) data[(loopCnt + 2U)] << 16U) |
((uint32_t) data[(loopCnt + 3U)] << 24U));
WRITE_REG32_RAW(((uint32_t) CANFD0 + (uint32_t) elemAddr), regVal);
}
regVal = CANFD0->MCANSS.MCAN.MCAN_TXBAR;
regVal |= ((uint32_t) 1U << 0); // buffer 0
/*
* For writing to TXBAR CCE bit should be '0'. This need not be
* reverted because for other qualified writes this is locked state
* and can't be written.
*/
CANFD0->MCANSS.MCAN.MCAN_CCCR &= ~0x02; //Configuration Change Enable
CANFD0->MCANSS.MCAN.MCAN_TXBAR = regVal;
return 1; // success
}
void getCANRxFIFOStatus(MCAN_RxFIFOStatus_t *fifoStatus){
uint32_t regVal;
regVal = CANFD0->MCANSS.MCAN.MCAN_RXF0S;
// 25 RF0L message lost 0
// 24 F0F 1 means full 0
// 21-16 F0PI Rx Putindex 2
// 13-8 F0GI Rx Getindex 1
// 6-0 F0FL Fill level 1
fifoStatus->fillLvl = regVal&0x7F; // HW_GET_FIELD(regVal, MCAN_RXF0S_F0FL);
fifoStatus->getIdx = (regVal>>8)&0x3F; // HW_GET_FIELD(regVal, MCAN_RXF0S_F0GI);
fifoStatus->putIdx = (regVal>>16)&0x3F; // HW_GET_FIELD(regVal, MCAN_RXF0S_F0PI);
fifoStatus->fifoFull = (regVal>>24)&0x01; // HW_GET_FIELD(regVal, MCAN_RXF0S_F0F);
fifoStatus->msgLost = (regVal>>25)&0x01; // HW_GET_FIELD(regVal, MCAN_RXF0S_RF0L);
}
void ReadCANMsgRam(void){
uint32_t startAddr = 0U, elemSize = 0U, elemAddr = 0U;
uint32_t idx = 0U;
uint32_t regVal, loopCnt;
startAddr = CANFD0->MCANSS.MCAN.MCAN_RXF0C&0xFFFC; // F0SA bits 15-2
elemSize = CANFD0->MCANSS.MCAN.MCAN_RXESC&0x07; // F0DS bits 2-0
idx = (CANFD0->MCANSS.MCAN.MCAN_RXF0S>>8)&0x3F; // F0GI, get index is bits 13-0
elemSize = objSize4[elemSize];
elemSize *= 4U;
elemAddr = startAddr + (elemSize * idx);
// ReadCANMsgRamRaw(elemAddr);
regVal = READ_REG32_RAW(((uint32_t) CANFD0 + (uint32_t) elemAddr));
// ID is stored in ID[28:18]
CANFIFO[CANPutI].id = (regVal&0x1FFFFFFF) >> 18;
#if CAN_DEBUG
rxMsg4.id = regVal&0x1FFFFFFF; // 28:0, 29-bit id, 28:18 have standard id
rxMsg4.rtr = (regVal>>29)&0x01; // RTR bit 29 (should be 0)
rxMsg4.xtd = (regVal>>30)&0x01; // XTD bit 30 (should be 0)
rxMsg4.esi = (regVal>>31)&0x01; // ESI bit 31 (should be 0)
#endif
elemAddr += 4U;
regVal = READ_REG32_RAW(((uint32_t) CANFD0 + (uint32_t) elemAddr));
CANFIFO[CANPutI].dlc = (regVal>>16)&0x0F; // DLC data length, bits 19:16
#if CAN_DEBUG
rxMsg4.rxts = (regVal>>0)&0x0FF; // RXTS time stamp, bits 15:0
rxMsg4.dlc = (regVal>>16)&0x0F; // DLC data length, bits 19:16
rxMsg4.brs = (regVal>>20)&0x01; // BRS, baud rate shift, bit 20
rxMsg4.fdf = (regVal>>21)&0x01; // FDF, FD format, bit 21, should be 1, CAN FD frame format
rxMsg4.fidx = (regVal>>24)&0x7F; // FIDX filter index, bits 30:24
rxMsg4.anmf = (regVal>>31)&0x01; // ANMF bit 31, 0 means matching filter FIDX, 1 means no matching
#endif
elemAddr += 4U;
// first four, only DLC bytes are valid
regVal = READ_REG32_RAW(((uint32_t) CANFD0 + (uint32_t) elemAddr));
CANFIFO[CANPutI].data[0] = (uint8_t)(regVal & 0x000000FFU);
CANFIFO[CANPutI].data[1] = (uint8_t)((regVal & 0x0000FF00U) >> 8U);
CANFIFO[CANPutI].data[2] = (uint8_t)((regVal & 0x00FF0000U) >> 16U);
CANFIFO[CANPutI].data[3] = (uint8_t)((regVal & 0xFF000000U) >> 24U);
elemAddr += 4U;
// second four, only DLC bytes are valid
regVal = READ_REG32_RAW(((uint32_t) CANFD0 + (uint32_t) elemAddr));
CANFIFO[CANPutI].data[4] = (uint8_t)(regVal & 0x000000FFU);
CANFIFO[CANPutI].data[5] = (uint8_t)((regVal & 0x0000FF00U) >> 8U);
CANFIFO[CANPutI].data[6] = (uint8_t)((regVal & 0x00FF0000U) >> 16U);
CANFIFO[CANPutI].data[7] = (uint8_t)((regVal & 0xFF000000U) >> 24U);
#if CAN_DEBUG
for(int i=0; i<rxMsg4.dlc; i++){
rxMsg4.data[i] = CANFIFO[CANPutI].data[i];
}
#endif
if(((CANPutI+1)&(CANFIFOSIZE-1)) != CANGetI){
CANPutI = (CANPutI+1)&(CANFIFOSIZE-1);
}
}
bool readCANRxMsg(void){
static MCAN_RxFIFOStatus_t rxFS;
rxFS.fillLvl = 0;
if ((CanInterruptLine1Status & 0x01) == 0x01){ // true
// should have bit 0, RF0N new message in RxFifo0
rxFS.num = 0 ; //DL_MCAN_RX_FIFO_NUM_0; // 0
while ((rxFS.fillLvl) == 0) {
getCANRxFIFOStatus(&rxFS);
}
ReadCANMsgRam();
CANFD0->MCANSS.MCAN.MCAN_RXF0A = rxFS.getIdx; // ack the fifo
CanInterruptLine1Status &= ~0x01; //(MCAN_IR_RF0N_MASK);
return true;
} else{
return false;
}
}
void CANFD0_IRQHandler(void){
bool processFlag;
uint16_t count;
uint32_t dlc;
// IIDX = interrupt index status
// 00h = No interrupt pending.
// 1h = MCAN Interrupt Line 0 interrupt pending.
// 2h = MCAN Interrupt Line 1 interrupt pending.
// 3h = Message RAM SEC (Single Error Correction) interrupt pending.
// 4h = Message RAM DED (Double Error Detection) interrupt pending.
// 5h = External Timestamp Counter Overflow interrupt pending.
// 6h = Clock Stop Wake Up interrupt pending.
switch (CANFD0->MCANSS.TI_WRAPPER.MSP.CPU_INT.IIDX){
case 2: // DL_MCAN_IIDX_LINE1:
// Check MCAN interrupts fired during TX/RX of CAN package
CanInterruptLine1Status |= CANFD0->MCANSS.MCAN.MCAN_IR;
// should have bit 0, RF0N new message in RxFifo0
CANFD0->MCANSS.MCAN.MCAN_IR = CanInterruptLine1Status; //intrmask=1, MCAN_IR
CANFD0->MCANSS.TI_WRAPPER.PROCESSORS.MCANSS_REGS.MCANSS_EOI = 2; // eoi=2, line 1 cleared
processFlag = readCANRxMsg();
if(processFlag == true){
GPIOA->DOUTTGL31_0 = 1;
OS_Signal(&CanDataAvailable);
}
break;
default:
break;
}
}
// Returns true if receive data is available
// false if no receive data ready
int CAN_CheckMail(void){
return (CANGetI != CANPutI);
}
// if receive data is ready, gets the data and returns true
// if no receive data is ready, returns false
int CAN_GetMailNonBlock(uint32_t *id, uint32_t *dlc, uint8_t *data){
if(CANGetI != CANPutI){
*id = CANFIFO[CANGetI].id;
*dlc = CANFIFO[CANGetI].dlc;
for(int i=0; i<*dlc; i++){
data[i] = CANFIFO[CANGetI].data[i];
}
CANGetI = (CANGetI+1)&(CANFIFOSIZE-1);
return 1;
}
return 0;
}
// if receive data is ready, gets the data
// if no receive data is ready, it waits until it is ready
void CAN_GetMail(uint32_t *id, uint32_t *dlc, uint8_t *data){
OS_Wait(&CanDataAvailable); // Use semaphore instead of spinning
//while(CANGetI == CANPutI){};
*id = CANFIFO[CANGetI].id;
*dlc = CANFIFO[CANGetI].dlc;
for(int i=0; i<*dlc; i++){
data[i] = CANFIFO[CANGetI].data[i];
}
CANGetI = (CANGetI+1)&(CANFIFOSIZE-1);
}
// 0 if failure
// 1 if ok
int CAN_SendMessage(uint32_t id, CanMessage_t* command){
int status = CAN_Send(id, 8, (uint8_t*)command); // returns 1 if OK
if (status == 1){
return 1;
}
return 0;
}
void CAN_ReadMessage(CanMessage_t* message){
uint32_t id, dlc;
CAN_GetMail(&id, &dlc, (uint8_t*)message);
}
int CAN_SetMotors(uint16_t Duty_L, uint16_t Duty_R, int16_t SteeringAngle){
CanMessage_t motorCommand;
motorCommand.MessageType = CMD_MOTOR;
motorCommand.Field1 = Duty_L;
motorCommand.Field2 = Duty_R;
motorCommand.Field3 = (uint16_t)SteeringAngle;
return CAN_SendMessage(0, &motorCommand);
}
int CAN_SendOSData(uint16_t jitter){
CanMessage_t OsData;
OsData.MessageType = DATA_STATS;
OsData.Field1 = jitter;
return CAN_SendMessage(0, &OsData);
}
int CAN_TellCrashed(int16_t steeringAngle){
CanMessage_t CrashEvent;
CrashEvent.MessageType = CMD_CRASH;
CrashEvent.Field1 = steeringAngle;
return CAN_SendMessage(0, &CrashEvent);
}

79
RTOS_Labs_common/CAN.h Normal file
View File

@@ -0,0 +1,79 @@
// CAN.h
// Runs on MSPM0G3507
// Use FDCAN0 to communicate on CAN bus PA12 and PA13
//
// Jonathan Valvano
// December 11, 2025
// derived from can_to_uart_bridge_LP_MSPM0G3507_nortos_ticlang
/*
Copyright 2025 by Jonathan W. Valvano, valvano@mail.utexas.edu
You may use, edit, run or distribute this file
as long as the above copyright notice remains
THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED
OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
VALVANO SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
For more information about my classes, my research, and my books, see
http://users.ece.utexas.edu/~valvano/
*/
// Use TCAN1057AVDRQ1 (not TCAN1057A-Q1, the version with pin 5 nc)
// Pin1 TXD ---- CAN_Tx PA12 FD-CAN module 0 transmit
// Pin2 Vss ---- ground
// Pin3 VCC ---- +5V with 0.1uF cap to ground
// Pin4 RXD ---- CAN_Rx PA13 FD-CAN module 0 receive (0 to 3.3V)
// Pin5 VIO ---- +3.3V (digital interface supply)
// Pin6 CANL ---- to other CANL on network
// Pin7 CANH ---- to other CANH on network
// Pin8 RS ---- ground, Slope-Control Input (maximum slew rate)
// 120 ohm across CANH, CANL on both ends of network
#ifndef __CAN_H__
#define __CAN_H__
#include <stdint.h>
typedef uint16_t MessageType_t;
#define CMD_MOTOR 0
#define DATA_STATS 1
#define CMD_CRASH 2
typedef struct{
MessageType_t MessageType;
uint16_t Field1;
uint16_t Field2;
uint16_t Field3;
} CanMessage_t;
void CAN_Init(void);
void CAN_EnableInterrupts(uint32_t priority);
// 0 if failure
// 1 if ok
int CAN_Send(uint32_t id, uint32_t dlc, uint8_t *data);
// Returns 1 if receive data is available
// 0 if no receive data ready
int CAN_CheckMail(void);
// if receive data is ready, gets the data and returns true
// if no receive data is ready, returns false
int CAN_GetMailNonBlock(uint32_t *id, uint32_t *dlc, uint8_t *data);
// if receive data is ready, gets the data
// if no receive data is ready, it waits until it is ready
void CAN_GetMail(uint32_t *id, uint32_t *dlc, uint8_t *data);
int CAN_SendMessage(uint32_t id, CanMessage_t*);
void CAN_ReadMessage(CanMessage_t* message);
int CAN_SetMotors(uint16_t Duty_L, uint16_t Duty_R, int16_t SteeringAngle);
int CAN_SendOSData(uint16_t jitter);
int CAN_TellCrashed(int16_t steeringAngle);
#endif // __CAN_H__

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -0,0 +1,22 @@
.text
.thumb
.align 2
.global StartCritical
.global EndCritical
// *********** StartCritical ************************
//make a copy of previous I bit, disable interrupts
// inputs : none
// outputs : R0=previous I bit
StartCritical:
MRS R0, PRIMASK // save old status
CPSID I // mask all (except faults)
BX LR
// *********** EndCritical ************************
// using the copy of previous I bit, restore I bit to previous value
// inputs : R0=previous I bit
// outputs : none
EndCritical:
MSR PRIMASK, R0
BX LR

23
RTOS_Labs_common/DFT16.c Normal file
View File

@@ -0,0 +1,23 @@
// DFT16.c
// 16-point DFT
// 12-bit binary fixed point
#include <stdint.h>
const int32_t ReW[16]={4096, 3784, 2896, 1567, 0, -1567, -2896, -3784, -4096, -3784, -2896, -1567, 0, 1567, 2896, 3784};
const int32_t ImW[16]={0, -1567, -2896, -3784, -4096, -3784, -2896, -1567, 0, 1567, 2896, 3784, 4096, 3784, 2896, 1567};
// 12-bit data*12-bit fixed*16 element, 12+12+4 = 28bit intermediate values, no overflow possible
// units of ReX and ImX match units of x
void DFT16(int32_t x[16],int32_t ReX[16],int32_t ImX[16]){
ReX[0] = 0;
for(int i=0; i<16; i++) ReX[0] += x[i];
ReX[0] >>= 4; // matches units of x
ImX[0] = 0; // real inputs
for(int k=1; k<16; k++){
ReX[k] = ImX[k] = 0;
for(int n=0; n<16; n++){
ReX[k] += x[n]*ReW[(k*n)&0x0F];
ImX[k] += x[n]*ImW[(k*n)&0x0F];
}
ReX[k] >>= 15; // matches units of x
ImX[k] >>= 15; // matches units of x
}
}

11
RTOS_Labs_common/DFT16.h Normal file
View File

@@ -0,0 +1,11 @@
// DFT16.h
// 16-point DFT
// 12-bit binary fixed point
// fill x with input data
// point ReX and ImX to empty arrays
// call DFT16
// results in ReX and ImX
// units of ReX and ImX match units of x
void DFT16(int32_t x[16],int32_t ReX[16],int32_t ImX[16]);

BIN
RTOS_Labs_common/DFT16.xls Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Binary file not shown.

2206
RTOS_Labs_common/FFT.c Normal file

File diff suppressed because it is too large Load Diff

82
RTOS_Labs_common/FFT.h Normal file
View File

@@ -0,0 +1,82 @@
/**
* @file FFT.h
* @brief Fast Fourier Transform
* @details fft() transforms time domain to frequency domain and
* ifft() transforms frequency to time domain domain
* @version TI-RSLK MAX v1.1
* @author Daniel Valvano and Jonathan Valvano
* @copyright Copyright 2020 by Jonathan W. Valvano, valvano@mail.utexas.edu,
* @warning AS-IS
* @note For more information see http://users.ece.utexas.edu/~valvano/
* @date July 13, 2020
******************************************************************************/
/* Factored discrete Fourier transform, or FFT, and its inverse iFFT */
// Derived from
// http://www.math.wustl.edu/~victor/mfmm/index.html#mfmm-software
// Mathematics for Multimedia, ISBN 978-0-8176-4879-4, was formerly managed by Elsevier/Academic Press.
// MSP432 available RAM limits size to N=2048 points
#include <math.h>
#include <stdlib.h>
/*!
* @defgroup Math
* @brief
* @{*/
/*!
* @brief a complex number has a real and imaginary part
*/
struct complex{
float Real;
float Imag;
};
typedef struct complex complex_t;
/**
* \brief PI is ratio of circumference to diameter of a circle
*/
#ifndef PI
#define PI 3.14159265358979323846264338327950288
#endif
/**
* discrete Fast Fourier Transform,
* Converts time to frequency domain <br>
* Assume the input is real data sampled at fs <br>
* Place n measurements into the real part of array v <br>
* Set the imaginary part of array v to 0 <br>
* Call fft() and the results are returned back in array v <br>
* If the input is real only, then the transform will contain <br>
* Index 0 <= k < n/2 complex components of frequency f=k/fs <br>
* E.g., k=0 represents the DC component of the input <br>
* Index n/2 <= k < n are complex conjugates of the first half <br>
* @param v array of input data in complex form
* @param n size of the two arrays 2, 4, 8, ..., 2048
* @param tmp temporary array of data in complex form
* @return none
* @note n is a power of two from 2 to 2048
* @brief fft
*/
void fft(complex_t *v, int n, complex_t *tmp);
/**
* discrete Inverse Fast Fourier Transform,
* Converts frequency to time domain <br>
* Assume the input is complex data with components k=0 to n-1 <br>
* Index 0 <= k < n-1 complex components of frequency f=k/fs <br>
* E.g., k=0 represents the DC component of the input <br>
* Place n complex values into the array v <br>
* Typically the index n/2 <= k < n are complex conjugates of the first half<br>
* Call ifft() and the results are returned back in array v <br>
* If the input is real only, then the transform will contain <br>
* @param v array of input data in complex form
* @param n size of the two arrays 2, 4, 8, ..., 2048
* @param tmp temporary array of data in complex form
* @return none
* @note n is a power of two from 2 to 2048
* @brief Inverse fft
*/
void ifft(complex_t *v, int n, complex_t *tmp);

110
RTOS_Labs_common/FIFO2.h Normal file
View File

@@ -0,0 +1,110 @@
// FIFO.h
// Runs on any LM3Sxxx
// Provide functions that initialize a FIFO, put data in, get data out,
// and return the current size. The file includes a transmit FIFO
// using index implementation and a receive FIFO using pointer
// implementation. Other index or pointer implementation FIFOs can be
// created using the macros supplied at the end of the file.
// Daniel Valvano
// June 16, 2011
/* This example accompanies the book
"Embedded Systems: Real Time Interfacing to Arm Cortex M Microcontrollers",
ISBN: 978-1463590154, Jonathan Valvano, copyright (c) 2015
Programs 3.7, 3.8., 3.9 and 3.10 in Section 3.7
Copyright 2015 by Jonathan W. Valvano, valvano@mail.utexas.edu
You may use, edit, run or distribute this file
as long as the above copyright notice remains
THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED
OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
VALVANO SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
For more information about my classes, my research, and my books, see
http://users.ece.utexas.edu/~valvano/
*/
#ifndef __FIFO_H__
#define __FIFO_H__
// macro to create an index FIFO
#define AddIndexFifo(NAME,SIZE,TYPE,SUCCESS,FAIL) \
uint32_t volatile NAME ## PutI; \
uint32_t volatile NAME ## GetI; \
TYPE static NAME ## Fifo [SIZE]; \
void NAME ## Fifo_Init(void){ \
NAME ## PutI = NAME ## GetI = 0; \
} \
int NAME ## Fifo_Put (TYPE data){ \
if(( NAME ## PutI - NAME ## GetI ) & ~(SIZE-1)){ \
return(FAIL); \
} \
NAME ## Fifo[ NAME ## PutI &(SIZE-1)] = data; \
NAME ## PutI ## ++; \
return(SUCCESS); \
} \
int NAME ## Fifo_Get (TYPE *datapt){ \
if( NAME ## PutI == NAME ## GetI ){ \
return(FAIL); \
} \
*datapt = NAME ## Fifo[ NAME ## GetI &(SIZE-1)]; \
NAME ## GetI ## ++; \
return(SUCCESS); \
} \
unsigned short NAME ## Fifo_Size (void){ \
return ((unsigned short)( NAME ## PutI - NAME ## GetI )); \
}
// e.g.,
// AddIndexFifo(Tx,32,unsigned char, 1,0)
// SIZE must be a power of two
// creates TxFifo_Init() TxFifo_Get() and TxFifo_Put()
// macro to create a pointer FIFO
#define AddPointerFifo(NAME,SIZE,TYPE,SUCCESS,FAIL) \
TYPE volatile *NAME ## PutPt; \
TYPE volatile *NAME ## GetPt; \
TYPE static NAME ## Fifo [SIZE]; \
void NAME ## Fifo_Init(void){ long sr; \
sr = StartCritical(); \
NAME ## PutPt = NAME ## GetPt = &NAME ## Fifo[0]; \
EndCritical(sr); \
} \
int NAME ## Fifo_Put (TYPE data){ \
TYPE volatile *nextPutPt; \
nextPutPt = NAME ## PutPt + 1; \
if(nextPutPt == &NAME ## Fifo[SIZE]){ \
nextPutPt = &NAME ## Fifo[0]; \
} \
if(nextPutPt == NAME ## GetPt ){ \
return(FAIL); \
} \
else{ \
*( NAME ## PutPt ) = data; \
NAME ## PutPt = nextPutPt; \
return(SUCCESS); \
} \
} \
int NAME ## Fifo_Get (TYPE *datapt){ \
if( NAME ## PutPt == NAME ## GetPt ){ \
return(FAIL); \
} \
*datapt = *( NAME ## GetPt ## ++); \
if( NAME ## GetPt == &NAME ## Fifo[SIZE]){ \
NAME ## GetPt = &NAME ## Fifo[0]; \
} \
return(SUCCESS); \
} \
unsigned short NAME ## Fifo_Size (void){\
if( NAME ## PutPt < NAME ## GetPt ){ \
return ((unsigned short)( NAME ## PutPt - NAME ## GetPt + (SIZE*sizeof(TYPE)))/sizeof(TYPE)); \
} \
return ((unsigned short)( NAME ## PutPt - NAME ## GetPt )/sizeof(TYPE)); \
}
// e.g.,
// AddPointerFifo(Rx,32,unsigned char, 1,0)
// SIZE can be any size
// creates RxFifo_Init() RxFifo_Get() and RxFifo_Put()
#endif // __FIFO_H__

View File

@@ -0,0 +1,104 @@
// IRDistance.c
// Runs on any microcontroller
// Provide mid-level functions that convert raw ADC
// values from the GP2Y0A21YK0F infrared distance sensors to
// distances in mm. STUDENTS WILL NEED TO CALIBRATE THEIR OWN SENSORS
// Jonathan Valvano
// Jan 15, 2020
/* This example accompanies the book
"Embedded Systems: Introduction to Robotics,
Jonathan W. Valvano, ISBN: 9781074544300, copyright (c) 2020
For more information about my classes, my research, and my books, see
http://users.ece.utexas.edu/~valvano/
Simplified BSD License (FreeBSD License)
Copyright (c) 2020, Jonathan Valvano, All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are
those of the authors and should not be interpreted as representing official
policies, either expressed or implied, of the FreeBSD Project.
*/
#include <stdint.h>
/* Calibration data
distance measured from front of the sensor to the wall
d(cm) 1/d bL al aR bR adcSample d (0.01cm) error
10 0.100 2813 2830 2820 2830 2823.25 1006 0.06
15 0.067 1935 1976 1986 1978 1968.75 1482 -0.18
20 0.050 1520 1500 1520 1550 1522.5 1966 -0.34
30 0.033 1040 1096 1028 933 1024.25 3099 0.99
adcSample = 26813/d+159 2681300
d = 26813/(adcSample-159) -159
*/
const int32_t A[4] = { 268130,268130,268130,268130};
const int32_t B[4] = { -159,-159,-159,-159};
const int32_t C[4] = { 0,0,0,0};
const int32_t IRmax[4] = { 494,494,494,494};
// returns distance in mm
int32_t IRDistance_Convert(int32_t adcSample, uint32_t sensor){
if(adcSample < IRmax[sensor]){
return 300;
}
return A[sensor]/(adcSample + B[sensor]) + C[sensor];
}
/* Calibrated constants from IR_Calib.xlsx (3-12 inch range, 4 pts each)
d(mm) = A / (adc + B) + C
Right (PA26, ADC0 ch1): A=52850, B=-1239, C=69, RMSE~18mm
Left (PA22, ADC0 ch7): A=137932, B=-859, C=32, RMSE~10mm
Right RMSE is high because 9in and 12in ADC readings (1529 vs 1476)
are only 53 counts apart, compressing the far-range fit.
Below the minimum calibrated ADC the sensor is beyond ~12in/305mm,
so 305 is returned as the out-of-range sentinel. */
#define IR_RIGHT_A 199877
#define IR_RIGHT_B -666
#define IR_RIGHT_C 4
#define IR_RIGHT_MIN_ADC 1381 // ADC at ~12 inches
#define IR_LEFT_A 186922
#define IR_LEFT_B -691
#define IR_LEFT_C 9
#define IR_LEFT_MIN_ADC 1372 // ADC at ~12 inches
// returns distance in mm for the right IR sensor (PA26, ADC0 ch1)
int32_t IRDistance_Right(int32_t adcSample){
if(adcSample < IR_RIGHT_MIN_ADC){
return 305;
}
return IR_RIGHT_A / (adcSample + IR_RIGHT_B) + IR_RIGHT_C;
}
// returns distance in mm for the left IR sensor (PA22, ADC0 ch7)
int32_t IRDistance_Left(int32_t adcSample){
if(adcSample < IR_LEFT_MIN_ADC){
return 305;
}
return IR_LEFT_A / (adcSample + IR_LEFT_B) + IR_LEFT_C;
}

View File

@@ -0,0 +1,88 @@
/**
* @file IRDistance.h
* @brief Take infrared distance measurements
* @details Provide mid-level functions that convert raw ADC
* values from the GP2Y0A21YK0F infrared distance sensors to
* distances in mm.
* @version derived from TI-RSLK MAX v1.1
* @author Daniel Valvano and Jonathan Valvano
* @copyright Copyright 2020 by Jonathan W. Valvano, valvano@mail.utexas.edu,
* @warning AS-IS
* @note For more information see http://users.ece.utexas.edu/~valvano/
* @date January 15, 2020
******************************************************************************/
/* This example accompanies the book
"Embedded Systems: Introduction to Robotics,
Jonathan W. Valvano, ISBN: 9781074544300, copyright (c) 2020
For more information about my classes, my research, and my books, see
http://users.ece.utexas.edu/~valvano/
Simplified BSD License (FreeBSD License)
Copyright (c) 2020, Jonathan Valvano, All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are
those of the authors and should not be interpreted as representing official
policies, either expressed or implied, of the FreeBSD Project.
*/
// 5V connected to all GP2Y0A21YK0F Vcc's (+5V)
// 5V connected to positive side of 10 uF capacitors physically near the sensors
// ground connected to all GP2Y0A21YK0F grounds and microcontroller ground
// ground connected to negative side of all 10 uF capacitors
#ifndef IRDISTANCE_H_
#define IRDISTANCE_H_
#include <stdint.h>
/**
* Convert ADC sample into distance for the GP2Y0A21YK0F
* infrared distance sensor. Conversion uses a calibration formula<br>
* D = A/(adcSample + B) + C
* @param adcSample is the 12-bit ADC sample 0 to 4095
* @param sensor is sensor number 0 to 3
* @return distance from robot to wall (units mm)
* @brief Convert infrared distance measurement
*/
int32_t IRDistance_Convert(int32_t adcSample, uint32_t sensor);
/**
* Convert ADC sample to distance for the right IR sensor (PA26, ADC0 ch1)<br>
* Calibrated from IR_Calib.xlsx: d(mm) = 76921/(adc - 495) + 20, RMSE~4.6mm
* @param adcSample is the 12-bit ADC sample 0 to 4095
* @return distance in mm, or 305 if beyond ~12-inch calibrated range
*/
int32_t IRDistance_Right(int32_t adcSample);
/**
* Convert ADC sample to distance for the left IR sensor (PA22, ADC0 ch7)<br>
* Calibrated from IR_Calib.xlsx: d(mm) = 83326/(adc - 498) + 18, RMSE~7.5mm
* @param adcSample is the 12-bit ADC sample 0 to 4095
* @return distance in mm, or 305 if beyond ~12-inch calibrated range
*/
int32_t IRDistance_Left(int32_t adcSample);
#endif /* IRDISTANCE_H_ */

View File

@@ -0,0 +1,229 @@
// *************Interpreter.c**************
// Students implement this as part of EE445M Lab 1,2,3,4,5,6
// high level OS user interface
// Solution to labs 1,2,3,4,5,6
// Runs on MSPM0
// Jonathan W. Valvano 12/29/2025, valvano@mail.utexas.edu
#include <stdint.h>
#include <string.h>
#include "../RTOS_Labs_common/OS.h"
#include "../RTOS_Labs_common/ST7735_SDC.h"
#include "../inc/ADC.h"
#include "../RTOS_Labs_common/RTOS_UART.h"
#include "../RTOS_Labs_common/LPF.h"
#include "../RTOS_Labs_common/eDisk.h"
#include "../RTOS_Labs_common/eFile.h"
#include "../RTOS_Labs_common/heap.h"
#include "../RTOS_Labs_common/Interpreter.h"
void Lab4(void); // Lab 4 performance data
void DFT(void); // Discrete Fourier Transform
void Robot(void); // Robot thread
extern Sema4_t LCDFree;
unsigned int currentDirectory = 0;
#define MAX_CMD_LEN 16
// readLine reads a line from UART into buf (up to maxLen-1 chars + null terminator)
// Echoes printable characters, handles backspace, terminates on carriage return
static void readLine(char *buf, int maxLen){
int pos = 0;
while(1){
char c = UART_InChar();
if(c == '\r') break;
if(c == '\b' || c == 127){ // backspace or DEL
if(pos > 0){
pos--;
buf[pos] = 0;
UART_OutString("\b \b");
}
}else if(c >= ' ' && c <= '~'){ // printable ASCII only
if(pos < maxLen - 1){
buf[pos] = c;
pos++;
UART_OutChar(c);
}
}
}
buf[pos] = 0;
UART_OutString("\n");
}
// findArg returns pointer to first character after the first space in line,
// or 0 if no space found. Null-terminates the command portion.
static char* findArg(char *line){
for(int i = 0; line[i]; i++){
if(line[i] == ' '){
line[i] = 0;
return &line[i+1];
}
}
return 0;
}
// displayHelp prints all commands to the serial console
// Input: None
// Output: None
static void displayHelp(void){
UART_OutString("Basic RTOS interpreter commands:\n\n"
"format Formats the SDC (will lose EVERYTHING)\n\n"
"cd [directory ID] selects working directory, options are 0 or 1\n\n"
"ls prints name and contents of working directory\n\n"
"show [filename] prints contents of file in working directory\n\n"
"delete [filename] deletes the file named\n\n"
"Lab4 executes Lab4()\n\n"
"robot launches the Robot() thread\n\n"
"dft displays dft data\n\n"
"clear clears the terminal output and LCD display\n\n"
"? shows this help menu\n\n");
}
// Takes user input through serial console
// Loops infinitely, processing commands
void Interpreter(void){
char line[MAX_CMD_LEN];
char *arg;
while(1){
UART_OutString("\n> ");
readLine(line, MAX_CMD_LEN);
if(line[0] == 0) continue;
arg = findArg(line);
switch(line[0]){
case '?':
displayHelp();
break;
case 'f': // format
UART_OutString("Formatting disk...\n");
OS_bWait(&LCDFree);
if(eFile_Format()){
OS_bSignal(&LCDFree);
UART_OutString("Format failed\n");
}else if(eFile_Mount()){
OS_bSignal(&LCDFree);
UART_OutString("Format complete, mount failed\n");
}else{
OS_bSignal(&LCDFree);
UART_OutString("Format complete\n");
}
break;
case 'c': // cd or clear
if(line[1] == 'd'){ // cd
if(!arg || (arg[0] != '0' && arg[0] != '1') || arg[1] != 0){
UART_OutString("Usage: cd 0 or cd 1\n");
}else{
unsigned char dirID = arg[0] - '0';
OS_bWait(&LCDFree);
if(eFile_SelectDirectory(dirID)){
OS_bSignal(&LCDFree);
UART_OutString("cd failed\n");
}else{
OS_bSignal(&LCDFree);
currentDirectory = dirID;
}
}
}else if(line[1] == 'l'){ // clear
UART_OutString("\033[2J\033[H");
OS_bWait(&LCDFree);
ST7735_FillScreen(0);
OS_bSignal(&LCDFree);
}else{
UART_OutString("Unknown command. Enter ? for help.\n");
}
break;
case 'l': // ls or Lab4
if(line[1] == 's'){ // ls
OS_bWait(&LCDFree);
if(eFile_DOpen("")){
OS_bSignal(&LCDFree);
UART_OutString("ls failed to open directory\n");
}else{
char *name;
unsigned long size;
unsigned int num = 0;
unsigned long total = 0;
UART_OutString("\nContents of Directory ");
UART_OutUDec(currentDirectory);
UART_OutString("\n\n");
while(eFile_DirNext(&name, &size) == 0){
UART_OutString("Filename = "); UART_OutString(name); UART_OutString(" ");
UART_OutString("Size (bytes)= "); UART_OutUDec(size); UART_OutString("\n");
total += size;
num++;
}
UART_OutString("\nNumber of Files = "); UART_OutUDec(num); UART_OutString("\n");
UART_OutString("Number of Bytes = "); UART_OutUDec(total); UART_OutString("\n");
eFile_DClose();
OS_bSignal(&LCDFree);
}
}else{
UART_OutString("Unknown command. Enter ? for help.\n");
}
break;
case 'L': // Lab4
UART_OutString("Lab4 data\n");
Lab4();
break;
case 's': // show
if(!arg || arg[0] == 0){
UART_OutString("Usage: show [filename]\n");
}else{
OS_bWait(&LCDFree);
if(eFile_ROpen(arg)){
OS_bSignal(&LCDFree);
UART_OutString("File not found\n");
}else{
char data; int status;
UART_OutString("\n--- Contents of "); UART_OutString(arg); UART_OutString(" ---\n");
do{
status = eFile_ReadNext(&data);
if(status == 0) UART_OutChar(data);
}while(status == 0);
eFile_RClose();
OS_bSignal(&LCDFree);
UART_OutString("--- End of file ---\n");
}
}
break;
case 'd': // delete or dft
if(line[1] == 'e'){ // delete
if(!arg || arg[0] == 0){
UART_OutString("Usage: delete [filename]\n");
}else{
OS_bWait(&LCDFree);
if(eFile_Delete(arg)){
OS_bSignal(&LCDFree);
UART_OutString("Delete failed\n");
}else{
OS_bSignal(&LCDFree);
UART_OutString("Deleted\n");
}
}
}else if(line[1] == 'f'){ // dft
UART_OutString("DFT data\n");
DFT();
}else{
UART_OutString("Unknown command. Enter ? for help.\n");
}
break;
case 'r': // robot
UART_OutString("Launching Robot\n");
OS_AddThread(&Robot, 128, 1);
break;
default:
UART_OutString("Unknown command. Enter ? for help.\n");
break;
}
}
}

View File

@@ -0,0 +1,39 @@
/**
* @file Interpreter.h
* @brief Real Time Operating System for Labs 1, 2, 3 and 4
* @details ECE445M
* @version V1.1
* @author Valvano
* @copyright Copyright 2025 by Jonathan W. Valvano, valvano@mail.utexas.edu,
* @warning AS-IS
* @note For more information see http://users.ece.utexas.edu/~valvano/
* @date June 10, 2025
******************************************************************************/
#if LAB6
#include <stdint.h>
// IO redirection for interpreter
struct InterpreterIORedir {
uint32_t tid;
void (*InString)(char *, uint16_t);
void (*OutString)(char *);
void (*OutChar)(char);
void (*OutUDec)(uint32_t);
void (*OutSDec)(long);
void (*OutUHex)(uint32_t);
void (*Fix2)(long);
};
extern struct InterpreterIORedir *InterpreterIO;
#endif
/**
* @details Run the user interface.
* @param none
* @return none
* @brief Interpreter
*/
void Interpreter(void);

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

364
RTOS_Labs_common/LD19.c Normal file
View File

@@ -0,0 +1,364 @@
/* LD19.c
* Jonathan Valvano
* Date: Nov 5, 2025
LiDAR Sensor LD19 distance sensor
The LD19 consists mainly of
a laser ranging core,
a wireless power transmission unit,
a wireless communication unit,
an angle measuring unit,
a motor drive unit and
a mechanical housing
Range 20mm to 12m
Sampling rate 10 Hz
Accuracy: 45mm
Standard deviation 10mm
Resolution: 15mm
Angular resolution: 0.8deg (540 measurements per rotation)
*/
#include <ti/devices/msp/msp.h>
#include "../RTOS_Labs_common/LD19.h"
#include "../inc/Clock.h"
#include "../inc/LaunchPad.h"
// There are four possible UART3 Rx pins that could be used
// Sensor board uses PB13 (UART3) and PB12=0 (GPIO output)
// LiDAR Sensor LD19 on RTOS sensor board
// Pin LD19 MSPM0
// 1 Tx PB13 RxD: is UART3 Rx (LD19 to MSPM0) baud=230400 bps
// 2 PWM PB12 GPIO write a 0 to run fastest (MSPM0 to LD19)
// 3 GND ground
// 4 P5V 5V
// UART3 is shared between LD19 and TFLuna3 (can have either but not both)
uint32_t LostDataLD19;
#define LD19_SIZE 64 // usable size is 63
#define LD19_MESSAGESIZE 47 // LD19 packet is 47 bytes, assuming length remains 12
// prototypes for private functions
void LD19Fifo_Init(void);
uint32_t LD19Fifo_Put(uint8_t data);
uint32_t LD19Fifo_Get(uint8_t *datapt);
int LD19Index;
uint8_t LD19LastByte;
uint32_t StartAngle,EndAngle,Speed,Distance[12],Intensity[12],Previous;
// 0 for looking for 54 2C
// 2-47 filling the TFLunaDataMessage with 47-byte message
//int LD19Mode; // 0 for FIFO and 1 for 540 array
uint8_t LD19DataMessage[64]; // 47 should be enough
//uint16_t Starts[256],Stops[256],Times[256],II=0;
// number of angles LD19NUM 540
//uint16_t Distances[540];
uint16_t *Distances;
int BadCnt=0; // index error
int BadDistance=0; // too low
uint32_t BadIntensity;
/*
Scope Measurements Continuous packets
Baud = 230400 bps
Packet Length = 47 bytes, 2.034ms long (470bits/230400bps = 2.039ms)
Packet period = 2.447ms
Packet frequency = 409 packets/sec
Packet payload = 12 measurements
Measurement frequency =12 measurements/2.447ms = 4904measurements/sec
540 measurements per rotation
4904 measurements/sec / 540 measurements/rotation = 9.08 rotations/sec
Overhead:
Interrupts every three frames, 130.4us (30bits/230400 bps)
15 interrupts take 3.55us (overhead 2.7%)
1 interrupt takes 13.4us (overhead 10%)
average overhead = (15*2.7%+10%)/16 = 3.2%
HeaderThe length is 1 byte, and the value is fixed at 0x54,
indicating the beginning of the data packet;
VerLenThe length is 1 byte, the upper three bits indicate the packet type,
which is currently fixed at 1, and the lower five bits indicate the number of
measurement points in a packet, which is currently fixed at 12, so the byte
value is fixed at 0x2C
SpeedThe length is 2 bytes (little endian), the unit is degrees per second, indicating the
speed of the lidar, e.g., 2152 degrees per second;
Start angle: The length is 2 bytes (little endian), and the unit is 0.01 degrees, indicating
the starting angle of the data packet point; e.g., 32427, or 324.27 degrees
DataIndicates measurement data, a measurement data length is 3 bytes, 12 measurements
Distance: 2 bytes (little endian) in mm, e.g., 1234 means 1234mm
Intensity: 1 byte, the typical value of the signal strength value is around 200
End angle: The length is 2 bytes (little endian), and the unit is 0.01 degrees, indicating
the end angle of the data packet point; e.g., 33470, or 334.7 degrees;
TimestampThe length is 2 bytes, the unit is milliseconds, and the maximum is 30000.
When it reaches 30000, it will be counted again
CRC checkThe length is 1 byte, obtained from the verification of all the previous data except itself.
*/
// UART3 is in power Domain PD1
// for 32MHz bus clock, UART3 clock is 32MHz
// for 40MHz bus clock, UART3 clock is MCLK 40MHz
// for 80MHz bus clock, UART3 clock is MCLK 80MHz
//------------LD19_Init------------
// Initialize the UART3 for 230400 baud rate (assuming 80 MHz clock),
// 8 bit word length, no parity bits, one stop bit, FIFOs enabled
// Input: function 0 for debug, 1 for real time
// Output: none
void LD19_Init(uint16_t *d){
// do not reset or activate PortA, already done in LaunchPad_Init
// RSTCLR to GPIOA and UART3 peripherals
// bits 31-24 unlock key 0xB1
// bit 1 is Clear reset sticky bit
// bit 0 is reset gpio port
// GPIOA->GPRCM.RSTCTL = (uint32_t)0xB1000003; // called previously
UART3->GPRCM.RSTCTL = 0xB1000003;
// Enable power to GPIOA and UART3 peripherals
// PWREN
// bits 31-24 unlock key 0x26
// bit 0 is Enable Power
// GPIOA->GPRCM.PWREN = (uint32_t)0x26000001; // called previously
UART3->GPRCM.PWREN = 0x26000001;
Clock_Delay(24); // time for uart to power up
// the following code selects PB13 to use UART3
IOMUX->SECCFG.PINCM[PB13INDEX] = 0x00040082;
//bit 18 INENA input enable
//bit 7 PC connected
//bits 5-0=2 for UART3_Rx
// configure PB12 to be GPIO output
IOMUX->SECCFG.PINCM[PB12INDEX] = 0x00000081;
//bit 7 PC connected
//bits 5-0=1 for GPIO
GPIOB->DOE31_0 |= (1<<12); // enable output
GPIOB->DOUTCLR31_0 = (1<<12); // PWM=0, fastest sampling
UART3->CLKSEL = 0x08; // bus clock
UART3->CLKDIV = 0x00; // no divide
UART3->CTL0 &= ~0x01; // disable UART3
UART3->CTL0 = 0x00020018;
// bit 17 FEN=1 enable FIFO
// bits 16-15 HSE=00 16x oversampling
// bit 14 CTSEN=0 no CTS hardware
// bit 13 RTSEN=0 no RTS hardware
// bit 12 RTS=0 not RTS
// bits 10-8 MODE=000 normal
// bits 6-4 TXE=001 enable TxD
// bit 3 RXE=1 enable TxD
// bit 2 LBE=0 no loop back
// bit 0 ENABLE 0 is disable, 1 to enable
if(Clock_Freq() == 40000000){
// 40000000/16 = 2,500,000 Hz
// Baud = 230400
// 2,500,000/230400 = 10.8506944444
// divider = 10+54/64 = 10.84375
UART3->IBRD = 10;
UART3->FBRD = 54; // baud =2,500,000/10.84375 = 230,547.55
}else if (Clock_Freq() == 32000000){
// 32000000/16 = 2,000,000
// Baud = 230400
// 2,000,000/230400 = 8.6805555
// divider = 8+43/64 = 8.671875
UART3->IBRD = 8;
UART3->FBRD = 43; // 230,630.63
}else if (Clock_Freq() == 80000000){
// 80000000/16 = 5,000,000 Hz
// Baud = 230400
// 5,000,000/230400 = 21.701388
// divider = 21+45/64 = 21.703125
UART3->IBRD = 21;
UART3->FBRD = 45; // baud =5,000,000/21.703125 = 230,381.569
}else return;
//LD19Mode = mode;
Distances = d;
LD19Index = 0; // looking for 0x54 0x2C
LD19LastByte = 0;
LD19DataMessage[0] = 0x54;
LD19DataMessage[1] = 0x2C;
LD19Fifo_Init();
LostDataLD19 = 0;
UART3->LCRH = 0x00000030;
// bits 5-4 WLEN=11 8 bits
// bit 3 STP2=0 1 stop
// bit 2 EPS=0 parity select
// bit 1 PEN=0 no parity
// bit 0 BRK=0 no break
UART3->CPU_INT.IMASK = 0x0401;
// bit 11 TXINT no
// bit 10 RXINT yes
// bit 0 Receive timeout, yes
UART3->IFLS = 0x0232;
// bits 11-8 RXTOSEL receiver timeout select 2 (0xF highest)
// bits 6-4 RXIFLSEL 3 is greater than or equal to 3/4 full (three elements)
// bits 2-0 TXIFLSEL 2 is less than or equal to half
NVIC->ICPR[0] = 1<<3; // UART3 is IRQ 3
NVIC->ISER[0] = 1<<3;
NVIC->IP[0] = (NVIC->IP[0]&(~0xFF000000))|(1<<30); // set priority (bits 31-30) IRQ 3
UART3->CTL0 |= 0x01; // enable UART3
}
// pointer to most recent array of 540 measurements
//uint16_t *LD19_GetDistances(void){
// return Distances;
//}
// copy from hardware RX FIFO to software LD19Fifo
// stop when hardware RX FIFO is empty
// data are lost if software LD19Fifo is full
void static copyHardwareToSoftwareLD19(void){
uint8_t letter;
if(Distances==0){ // raw data mode
while((UART3->STAT&0x04) == 0){// empty RX hardware FIFO
letter = UART3->RXDATA;
if((LD19Fifo_Put(letter))==0){
LostDataLD19++;
}
}
}else{
while((UART3->STAT&0x04)==0){ // empty RX hardware FIFO
letter = UART3->RXDATA;
if(LD19Index == 0){ // looking for sequence 0x54,0x2C
if((letter == 0x2C)&&(LD19LastByte == 0x54)){
LD19Index = 2; // looking for message
}
LD19LastByte = letter;
}else{
LD19DataMessage[LD19Index] = letter;
LD19Index++;
if(LD19Index == LD19_MESSAGESIZE){
// GPIOB->DOUTTGL31_0 = BLUE; // PB22
// Speed = LD19DataMessage[2]+(LD19DataMessage[3]<<8);
StartAngle = LD19DataMessage[4]+(LD19DataMessage[5]<<8);
//EndAngle = LD19DataMessage[42]+(LD19DataMessage[43]<<8);
// StartAngle goes from 0 to 35999
// index goes from 0 to 540-1 (539()
//uint32_t index = (540*StartAngle)/36000; // 0 to 539
uint32_t findex = (983*StartAngle)>>16;
if(findex>=540) {
findex = 540-1;
BadCnt++; // never happens
}
for(int i=0;i<12;i++){
uint16_t d = LD19DataMessage[6+3*i]+(LD19DataMessage[7+3*i]<<8);
if(d < 10){
BadDistance++; // happens a lot, might be low intensity
BadIntensity = LD19DataMessage[8+3*i];
if(findex==0){
d = Distances[539];
if(d > 10){
Distances[findex+i] = d;
}
}else{
d = Distances[findex+i-1];
if(d > 10){
Distances[findex+i] = Distances[findex+i-1];
}
}
}else{
Distances[findex+i] = d;
}
// if(findex > 270){
// GPIOA->DOUTSET31_0 = 1;
// GPIOB->DOUTSET31_0 = BLUE; // PB22
// }
// if(findex < 200){
// GPIOA->DOUTCLR31_0 = 1;
// GPIOB->DOUTCLR31_0 = BLUE; // PB22
// }
// Distance[i] = d;
// Intensity[i] = LD19DataMessage[8+3*i];
}
/*
Starts[II] = StartAngle;
Stops[II] = EndAngle;
Times[II] = LD19DataMessage[44]+(LD19DataMessage[45]<<8);
II = (II+1)&0xFF;
*/
LD19LastByte = 0; // get ready for next message
LD19Index = 0;
}
}
}
}
}
//------------LD19_InChar------------
// Wait for new serial port input
// Input: none
// Output: ASCII code for key typed
uint8_t LD19_InChar(void){uint32_t status;
uint8_t letter;
do{
status = LD19Fifo_Get(&letter);
}while(status==0);
return(letter);
}
void UART3_IRQHandler(void){ uint32_t status;
status = UART3->CPU_INT.IIDX; // reading clears bit in RIS
// GPIOA->DOUTTGL31_0 = 1;
GPIOB->DOUTSET31_0 = BLUE; // PB22
if(status == 0x01){ // 0x01 receive timeout
copyHardwareToSoftwareLD19();
}else if(status == 0x0B){ // 0x0B receive
copyHardwareToSoftwareLD19();
}
GPIOB->DOUTCLR31_0 = BLUE; // PB22
}
// Declare state variables for FiFo
// size, buffer, put and get indexes
int32_t static LD19PutI; // Index to put new
int32_t static LD19GetI; // Index of oldest
uint8_t static LD19Fifo[LD19_SIZE];
// *********** LD19Fifo_Init**********
// Initializes a software LD19Fifo of a
// fixed size and sets up indexes for
// put and get operations
void LD19Fifo_Init(void){
LD19PutI = LD19GetI = 0;
}
// *********** LD19Fifo_Put**********
// Adds an element to the LD19FIFO
// Input: data is character to be inserted
// Output: 1 for success, data properly saved
// 0 for failure, LD19Fifo is full
uint32_t LD19Fifo_Put(uint8_t data){
if(((LD19PutI+1)&(LD19_SIZE-1)) == LD19GetI){
return 0;
}
LD19Fifo[LD19PutI] = data;
LD19PutI = (LD19PutI+1)&(LD19_SIZE-1);
return 1;
}
// *********** LD19Fifo_Get**********
// Gets an element from the LD19FIFO
// Input: pointer to empty 8-bit variable
// Output: If the LD19FIFO is empty return 0
// If the LD19FIFO has data, remove it, and put in *datapt, return 1
uint32_t LD19Fifo_Get(uint8_t *datapt){
if(LD19GetI == LD19PutI){
return 0;
}
*datapt = LD19Fifo[LD19GetI];
LD19GetI = (LD19GetI+1)&(LD19_SIZE-1);
return 1;
}
//------------LD19_InStatus------------
// Returns how much data available for reading from LD19 FIFO
// Input: none
// Output: number of elements in receive FIFO
uint32_t LD19_InStatus(void){
return ((LD19PutI - LD19GetI)&(LD19_SIZE-1));
}

72
RTOS_Labs_common/LD19.h Normal file
View File

@@ -0,0 +1,72 @@
/*!
* @defgroup LD19
* @brief Asynchronous serial communication to LiDAR Sensor LD19 distance sensor
<table>
<caption id="LD19pins0">LiDAR Sensor LD19 distance sensor interface</caption>
<tr><th>Pin <th>LD19 <th>MSPM0
<tr><td>1 <td>Tx <td>PB13 RxD: is UART3 Rx (LD19 to MSPM0)
<tr><td>2 <td>PWM <td>PB12 GPIO write a 0 to run fastest (MSPM0 to LD19)
<tr><td>3 <td>GND <td>ground
<tr><td>4 <td>P5V <td>5V
</table>
* @{*/
/**
* @file LD19.h
* @brief Initialize LD19, interrupt synchronization
* @details LD19 initialization. UART3 230400 bps baud,
* 1 start, 8 data bits, 1 stop, no parity.<br>
* @version RTOS
* @author Jonathan Valvano
* @copyright Valvano 2025
* @warning AS-IS
* @note For more information see http://users.ece.utexas.edu/~valvano/
* @date Nov 5, 2025
<table>
<caption id="LD19pins1">LiDAR Sensor LD19 distance sensor interface</caption>
<tr><th>Pin <th>LD19 <th>MSPM0
<tr><td>1 <td>Tx <td>PB13 RxD: is UART3 Rx (LD19 to MSPM0)
<tr><td>2 <td>PWM <td>PB12 GPIO write a 0 to run fastest (MSPM0 to LD19)
<tr><td>3 <td>GND <td>ground
<tr><td>4 <td>P5V <td>5V
</table>
UART3 is shared between LD19 and TFLuna3 (can have either but not both)
******************************************************************************/
#ifndef __LD19_H__
#define __LD19_H__
/**
* initializeLiDAR Sensor LD19 distance sensor 230400 bps baud rate.<br>
* interrupt synchronization
* @param d 0 for debug, pointer to array of 540 elements for read time
* @return none
* @brief Initialize LD19
*/
void LD19_Init(uint16_t *d);
// the following functions only needed for low-level debugging
/**
* Wait for new serial port input<br>
* Uses interrupt synchronization<br>
* This function waits if the receive software FIFO is empty
* @param Input: none
* @return 8-bit code from TF Luna
*/
uint8_t LD19_InChar(void);
/**
* Returns how much data available for reading
* @param none
* @return number of elements in receive software FIFO
*/
uint32_t LD19_InStatus(void);
#endif // __LD19_H__
/** @}*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

519
RTOS_Labs_common/LPF.c Normal file
View File

@@ -0,0 +1,519 @@
// LPF.c
// Runs on any microcontroller
// implements seven FIR low-pass filters
// Jonathan Valvano
// January 15, 2020
/* This example accompanies the books
"Embedded Systems: Introduction to ARM Cortex M Microcontrollers",
ISBN: 978-1469998749, Jonathan Valvano, copyright (c) 2020
"Embedded Systems: Real Time Interfacing to ARM Cortex M Microcontrollers",
ISBN: 978-1463590154, Jonathan Valvano, copyright (c) 2020
"Embedded Systems: Real-Time Operating Systems for ARM Cortex-M Microcontrollers",
ISBN: 978-1466468863, Jonathan Valvano, copyright (c) 2020
Copyright 2020 by Jonathan W. Valvano, valvano@mail.utexas.edu
You may use, edit, run or distribute this file
as long as the above copyright notice remains
THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED
OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
VALVANO SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
For more information about my classes, my research, and my books, see
http://users.ece.utexas.edu/~valvano/
*/
#include <stdint.h>
#include "../RTOS_Labs_common/LPF.h"
// Newton's method
// s is an integer
// sqrt(s) is an integer
uint32_t sqrt2(uint32_t s){
uint32_t t; // t*t will become s
int n; // loop counter to make sure it stops running
t = s/10+1; // initial guess
for(n = 16; n; --n){ // guaranteed to finish
t = ((t*t+s)/t)/2;
}
return t;
}
#define FILTERMAX 16
//**************Low pass Digital filter**************
int32_t Size1; // Size-point average, Size=2 to FILTERMAX
int32_t x1[FILTERMAX]; // one copy of data in MACQ
uint32_t I1; // index to oldest
int32_t LPFSum1; // sum of the last Size samples
int32_t sigma1;
int32_t snr1; // signal to noise ratio
void LPF_Init(int32_t initial, int32_t size){ int i;
if(size>FILTERMAX) size=FILTERMAX; // max
Size1 = size;
I1 = Size1-1;
LPFSum1 = Size1*initial; // prime MACQ with initial data
for(i=0; i<Size1; i++){
x1[i] = initial;
}
}
// calculate one filter output, called at sampling rate
// Input: new ADC data Output: filter output
// y(n) = (x(n)+x(n-1)+...+x(n-Size-1)/Size
int32_t LPF_Calc(int32_t newdata){
if(I1 == 0){
I1 = Size1-1; // wrap
} else{
I1--; // make room for data
}
LPFSum1 = LPFSum1+newdata-x1[I1]; // subtract oldest, add newest
x1[I1] = newdata; // save new data
return LPFSum1/Size1;
}
int32_t Noise1(void){ int32_t sum,mean;
if(Size1<2) return 0;
sum = 0;
for(int i=0;i<Size1;i++){
sum = sum+x1[i];
}
mean = sum/Size1; // DC component
sum = 0;
for(int i=0;i<Size1;i++){
sum = sum+(x1[i]-mean)*(x1[i]-mean); // total energy in AC part
}
sigma1 = sqrt2(sum/(Size1-1));
snr1 = mean/sigma1;
return sigma1;
}
int32_t Size2; // Size-point average, Size=2 to FILTERMAX
int32_t x2[FILTERMAX]; // one copy of data in MACQ
uint32_t I2; // index to oldest
int32_t LPFSum2; // sum of the last Size samples
int32_t sigma2;
int32_t snr2; // signal to noise ratio
void LPF_Init2(int32_t initial, int32_t size){ int i;
if(size>FILTERMAX) size=FILTERMAX; // max
Size2 = size;
I2 = Size2-1;
LPFSum2 = Size2*initial; // prime MACQ with initial data
for(i=0; i<Size2; i++){
x2[i] = initial;
}
}
// calculate one filter output, called at sampling rate
// Input: new ADC data Output: filter output, DAC data
// y(n) = (x(n)+x(n-1)+...+x(n-Size-1)/Size
int32_t LPF_Calc2(int32_t newdata){
if(I2 == 0){
I2 = Size2-1; // wrap
} else{
I2--; // make room for data
}
LPFSum2 = LPFSum2+newdata-x2[I2]; // subtract oldest, add newest
x2[I2] = newdata; // save new data
return LPFSum2/Size2;
}
int32_t Noise2(void){ int32_t sum,mean;
if(Size2<2) return 0;
sum = 0;
for(int i=0;i<Size2;i++){
sum = sum+x2[i];
}
mean = sum/Size2; // DC component
sum = 0;
for(int i=0;i<Size2;i++){
sum = sum+(x2[i]-mean)*(x2[i]-mean); // total energy in AC part
}
sigma2 = sqrt2(sum/(Size2-1));
snr2 = mean/sigma2;
return sigma2;
}
int32_t Size3; // Size-point average, Size=2 to FILTERMAX
int32_t x3[FILTERMAX]; // one copy of data in MACQ
uint32_t I3; // index to oldest
int32_t LPFSum3; // sum of the last Size samples
int32_t sigma3;
int32_t snr3; // signal to noise ratio
void LPF_Init3(int32_t initial, int32_t size){ int i;
if(size>FILTERMAX) size=FILTERMAX; // max
Size3 = size;
I3 = Size3-1;
LPFSum3 = Size3*initial; // prime MACQ with initial data
for(i=0; i<Size3; i++){
x3[i] = initial;
}
}
// calculate one filter output, called at sampling rate
// Input: new ADC data Output: filter output, DAC data
// y(n) = (x(n)+x(n-1)+...+x(n-Size-1)/Size
int32_t LPF_Calc3(int32_t newdata){
if(I3 == 0){
I3 = Size3-1; // wrap
} else{
I3--; // make room for data
}
LPFSum3 = LPFSum3+newdata-x3[I3]; // subtract oldest, add newest
x3[I3] = newdata; // save new data
return LPFSum3/Size3;
}
int32_t Noise3(void){ int32_t sum,mean;
if(Size3<2) return 0;
sum = 0;
for(int i=0;i<Size3;i++){
sum = sum+x3[i];
}
mean = sum/Size3; // DC component
sum = 0;
for(int i=0;i<Size3;i++){
sum = sum+(x3[i]-mean)*(x3[i]-mean); // total energy in AC part
}
sigma3 = sqrt2(sum/(Size3-1));
snr3 = (mean)/sigma3;
return sigma3;
}
int32_t Size4; // Size-point average, Size=2 to FILTERMAX
int32_t x4[FILTERMAX]; // one copy of data in MACQ
uint32_t I4; // index to oldest
int32_t LPFSum4; // sum of the last Size samples
int32_t sigma4;
int32_t snr4; // signal to noise ratio
void LPF_Init4(int32_t initial, int32_t size){ int i;
if(size>FILTERMAX) size=FILTERMAX; // max
Size4 = size;
I4 = Size4-1;
LPFSum4 = Size4*initial; // prime MACQ with initial data
for(i=0; i<Size4; i++){
x4[i] = initial;
}
}
// calculate one filter output, called at sampling rate
// Input: new ADC data Output: filter output, DAC data
// y(n) = (x(n)+x(n-1)+...+x(n-Size-1)/Size
int32_t LPF_Calc4(int32_t newdata){
if(I4 == 0){
I4 = Size4-1; // wrap
} else{
I4--; // make room for data
}
LPFSum4 = LPFSum4+newdata-x4[I4]; // subtract oldest, add newest
x4[I4] = newdata; // save new data
return LPFSum4/Size4;
}
int32_t Noise4(void){ int32_t sum,mean;
if(Size4<2) return 0;
sum = 0;
for(int i=0;i<Size4;i++){
sum = sum+x4[i];
}
mean = sum/Size4; // DC component
sum = 0;
for(int i=0;i<Size4;i++){
sum = sum+(x4[i]-mean)*(x4[i]-mean); // total energy in AC part
}
sigma4 = sqrt2(sum/(Size4-1));
snr4 = mean/sigma4;
return sigma4;
}
int32_t Size5; // Size-point average, Size=2 to FILTERMAX
int32_t x5[FILTERMAX]; // one copy of data in MACQ
uint32_t I5; // index to oldest
int32_t LPFSum5; // sum of the last Size samples
int32_t sigma5;
int32_t snr5; // signal to noise ratio
void LPF_Init5(int32_t initial, int32_t size){ int i;
if(size>FILTERMAX) size=FILTERMAX; // max
Size5 = size;
I5 = Size5-1;
LPFSum5 = Size5*initial; // prime MACQ with initial data
for(i=0; i<Size5; i++){
x5[i] = initial;
}
}
// calculate one filter output, called at sampling rate
// Input: new ADC data Output: filter output, DAC data
// y(n) = (x(n)+x(n-1)+...+x(n-Size-1)/Size
int32_t LPF_Calc5(int32_t newdata){
if(I5 == 0){
I5 = Size5-1; // wrap
} else{
I5--; // make room for data
}
LPFSum5 = LPFSum5+newdata-x5[I5]; // subtract oldest, add newest
x5[I5] = newdata; // save new data
return LPFSum5/Size5;
}
int32_t Noise5(void){ int32_t sum,mean;
if(Size5<2) return 0;
sum = 0;
for(int i=0;i<Size5;i++){
sum = sum+x5[i];
}
mean = sum/Size5; // DC component
sum = 0;
for(int i=0;i<Size5;i++){
sum = sum+(x5[i]-mean)*(x5[i]-mean); // total energy in AC part
}
sigma5 = sqrt2(sum/(Size5-1));
snr5 = mean/sigma5;
return sigma5;
}
int32_t Size6; // Size-point average, Size=2 to FILTERMAX
int32_t x6[FILTERMAX]; // one copy of data in MACQ
uint32_t I6; // index to oldest
int32_t LPFSum6; // sum of the last Size samples
int32_t sigma6;
int32_t snr6; // signal to noise ratio
void LPF_Init6(int32_t initial, int32_t size){ int i;
if(size>FILTERMAX) size=FILTERMAX; // max
Size6 = size;
I6 = Size6-1;
LPFSum6 = Size6*initial; // prime MACQ with initial data
for(i=0; i<Size6; i++){
x6[i] = initial;
}
}
// calculate one filter output, called at sampling rate
// Input: new ADC data Output: filter output, DAC data
// y(n) = (x(n)+x(n-1)+...+x(n-Size-1)/Size
int32_t LPF_Calc6(int32_t newdata){
if(I6 == 0){
I6 = Size6-1; // wrap
} else{
I6--; // make room for data
}
LPFSum6 = LPFSum6+newdata-x6[I6]; // subtract oldest, add newest
x6[I6] = newdata; // save new data
return LPFSum6/Size6;
}
int32_t Noise6(void){ int32_t sum,mean;
if(Size6<2) return 0;
sum = 0;
for(int i=0;i<Size6;i++){
sum = sum+x6[i];
}
mean = sum/Size6; // DC component
sum = 0;
for(int i=0;i<Size6;i++){
sum = sum+(x6[i]-mean)*(x6[i]-mean); // total energy in AC part
}
sigma6 = sqrt2(sum/(Size6-1));
snr6 = mean/sigma6;
return sigma6;
}
int32_t Size7; // Size-point average, Size=2 to FILTERMAX
int32_t x7[FILTERMAX]; // one copy of data in MACQ
uint32_t I7; // index to oldest
int32_t LPFSum7; // sum of the last Size samples
int32_t sigma7;
int32_t snr7; // signal to noise ratio
void LPF_Init7(int32_t initial, int32_t size){ int i;
if(size>FILTERMAX) size=FILTERMAX; // max
Size7 = size;
I7 = Size7-1;
LPFSum7 = Size7*initial; // prime MACQ with initial data
for(i=0; i<Size7; i++){
x7[i] = initial;
}
}
// calculate one filter output, called at sampling rate
// Input: new ADC data Output: filter output, DAC data
// y(n) = (x(n)+x(n-1)+...+x(n-Size-1)/Size
int32_t LPF_Calc7(int32_t newdata){
if(I7 == 0){
I7 = Size7-1; // wrap
} else{
I7--; // make room for data
}
LPFSum7 = LPFSum7+newdata-x7[I7]; // subtract oldest, add newest
x7[I7] = newdata; // save new data
return LPFSum7/Size7;
}
int32_t Noise7(void){ int32_t sum,mean;
if(Size7<2) return 0;
sum = 0;
for(int i=0;i<Size7;i++){
sum = sum+x7[i];
}
mean = sum/Size7; // DC component
sum = 0;
for(int i=0;i<Size7;i++){
sum = sum+(x7[i]-mean)*(x7[i]-mean); // total energy in AC part
}
sigma7 = sqrt2(sum/(Size7-1));
snr7 = mean/sigma7;
return sigma7;
}
int32_t u1,u2,u3; // last three points
int32_t Median(int32_t newdata){
int32_t result;
u3 = u2;
u2 = u1;
u1 = newdata;
if(u1>u2){
if(u2>u3){
result = u2; // u1>u2,u2>u3 u1>u2>u3
}else{
if(u1>u3){
result = u3; // u1>u2,u3>u2,u1>u3 u1>u3>u2
}else{
result = u1; // u1>u2,u3>u2,u3>u1 u3>u1>u2
}
}
}else{
if(u3>u2){
result = u2; // u2>u1,u3>u2 u3>u2>u1
}else{
if(u1>u3){
result = u1; // u2>u1,u2>u3,u1>u3 u2>u1>u3
}
else{
result = u3; // u2>u1,u2>u3,u3>u1 u2>u3>u1
}
}
}
return(result);
}
// if the ADC is sampled at 1 kHz
// 60-Hz notch high-Q, IIR filter, assuming fs=2000 Hz
// y(n) = (256x(n) -503x(n-1) + 256x(n-2) + 498y(n-1)-251y(n-2))/256 (2k sampling)
// y(n) = (256x(n) -476x(n-1) + 256x(n-2) + 471y(n-1)-251y(n-2))/256 (1k sampling)
int32_t Filter(int32_t data){
static int32_t x[6]; // this MACQ needs twice memory allocation
static int32_t y[6];
static uint32_t n=3; // 3, 4, or 5
n++;
if(n==6) n=3;
x[n] = x[n-3] = data; // two copies of new data
y[n] = (256*(x[n]+x[n-2])-476*x[n-1]+471*y[n-1]-251*y[n-2]+128)/256;
y[n-3] = y[n]; // two copies of filter outputs too
return y[n];
}
// Independent second instance of the 60-Hz notch IIR filter (e.g. for left IR sensor)
int32_t Filter2(int32_t data){
static int32_t x[6];
static int32_t y[6];
static uint32_t n=3;
n++;
if(n==6) n=3;
x[n] = x[n-3] = data;
y[n] = (256*(x[n]+x[n-2])-476*x[n-1]+471*y[n-1]-251*y[n-2]+128)/256;
y[n-3] = y[n];
return y[n];
}
int32_t mx7[7]; // last 7 inputs
int32_t f7[7]; // found flag
// 11 usec running at 80 MHz
int32_t Median5(int32_t x){ int i,j; int32_t max;
for(i=3; i>=0; i--){
mx7[i+1] = mx7[i]; // push into MACQ
f7[i]=1; // 1 means look at it
}
f7[5] = 1;
mx7[0] = x;
max = mx7[0]; j=0;
for(i=1; i<7; i++){
if(mx7[i] > max){
max = mx7[i];
j = i;
}
}
f7[j] = 0; // 0 means ignore the max
max = INT32_MIN;
for(i=0; i<7; i++){
if((mx7[i] > max)&&f7[i]){
max = mx7[i];
j = i;
}
}
f7[j] = 0; // 0 means ignore the second highest
max = INT32_MIN;
for(i=0; i<7; i++){
if((mx7[i] > max)&&f7[i]){
max = mx7[i];
}
}
return max; // median is third highest
}
// Independent second instance of Median5 with separate MACQ (avoids sharing mx7/f7 with Median5)
int32_t mx7_2[7];
int32_t f7_2[7];
int32_t Median5_2(int32_t x){ int i,j; int32_t max;
for(i=3; i>=0; i--){
mx7_2[i+1] = mx7_2[i];
f7_2[i]=1;
}
f7_2[5] = 1;
mx7_2[0] = x;
max = mx7_2[0]; j=0;
for(i=1; i<7; i++){
if(mx7_2[i] > max){
max = mx7_2[i];
j = i;
}
}
f7_2[j] = 0;
max = INT32_MIN;
for(i=0; i<7; i++){
if((mx7_2[i] > max)&&f7_2[i]){
max = mx7_2[i];
j = i;
}
}
f7_2[j] = 0;
max = INT32_MIN;
for(i=0; i<7; i++){
if((mx7_2[i] > max)&&f7_2[i]){
max = mx7_2[i];
}
}
return max;
}
// 16 usec running at 80 MHz
int32_t Median7(int32_t x){ int i,j; int32_t max;
for(i=5; i>=0; i--){
mx7[i+1] = mx7[i]; // push into MACQ
f7[i]=1; // 1 means look at it
}
f7[6] = 1;
mx7[0] = x;
max = mx7[0]; j=0;
for(i=1; i<7; i++){
if(mx7[i] > max){
max = mx7[i];
j = i;
}
}
f7[j] = 0; // 0 means ignore the max
max = INT32_MIN;
for(i=0; i<7; i++){
if((mx7[i] > max)&&f7[i]){
max = mx7[i];
j = i;
}
}
f7[j] = 0; // 0 means ignore the second highest
max = INT32_MIN;
for(i=0; i<7; i++){
if((mx7[i] > max)&&f7[i]){
max = mx7[i];
j = i;
}
}
f7[j] = 0;// 0 means ignore the third highest
max = INT32_MIN;
for(i=0; i<7; i++){
if((mx7[i] > max)&&f7[i]){
max = mx7[i];
}
}
return max; // median is fourth highest
}

239
RTOS_Labs_common/LPF.h Normal file
View File

@@ -0,0 +1,239 @@
/**
* @file LPF.h
* @brief implements four FIR low-pass filters
* @details Finite length LPF<br>
1) Size is the depth 2 to 16<br>
2) y(n) = (sum(x(n)+x(n-1)+...+x(n-size-1))/size<br>
3) To use a filter<br>
a) initialize it once<br>
b) call the filter at the sampling rate<br>
* @version from TI-RSLK MAX v1.1
* @author Daniel Valvano and Jonathan Valvano
* @copyright Copyright 2020 by Jonathan W. Valvano, valvano@mail.utexas.edu,
* @warning AS-IS
* @note For more information see http://users.ece.utexas.edu/~valvano/
* @date January 15, 2020
******************************************************************************/
/* This example accompanies the books
"Embedded Systems: Introduction to ARM Cortex M Microcontrollers",
ISBN: 978-1469998749, Jonathan Valvano, copyright (c) 2020
"Embedded Systems: Real Time Interfacing to ARM Cortex M Microcontrollers",
ISBN: 978-1463590154, Jonathan Valvano, copyright (c) 2020
"Embedded Systems: Real-Time Operating Systems for ARM Cortex-M Microcontrollers",
ISBN: 978-1466468863, Jonathan Valvano, copyright (c) 2020
Copyright 2020 by Jonathan W. Valvano, valvano@mail.utexas.edu
You may use, edit, run or distribute this file
as long as the above copyright notice remains
THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED
OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
VALVANO SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
For more information about my classes, my research, and my books, see
http://users.ece.utexas.edu/~valvano/
*/
#include <stdint.h>
/**
* Newton's method sqrt
* @param s is an integer
* @return sqrt(s) is an integer
* @brief square root
*/
uint32_t sqrt2(uint32_t s);
/**
* Initialize first LPF<br>
* Set all data to an initial value<br>
* @param initial value to preload into MACQ
* @param size depth of the filter, 2 to 16
* @return none
* @brief Initialize first LPF
*/
void LPF_Init(int32_t initial, int32_t size);
/**
* First LPF, calculate one filter output<br>
* Called at sampling rate
* @param newdata new ADC data
* @return result filter output
* @brief FIR low pass filter
*/
int32_t LPF_Calc(int32_t newdata);
int32_t Noise1(void);
/**
* Initialize second LPF<br>
* Set all data to an initial value<br>
* @param initial value to preload into MACQ
* @param size depth of the filter, 2 to 16
* @return none
* @brief Initialize second LPF
*/
void LPF_Init2(int32_t initial, int32_t size);
/**
* Second LPF, calculate one filter output<br>
* Called at sampling rate
* @param newdata new ADC data
* @return result filter output
* @brief FIR low pass filter
*/
int32_t LPF_Calc2(int32_t newdata);
int32_t Noise2(void);
/**
* Initialize third LPF<br>
* Set all data to an initial value<br>
* @param initial value to preload into MACQ
* @param size depth of the filter, 2 to 16
* @return none
* @brief Initialize third LPF
*/
void LPF_Init3(int32_t initial, int32_t size);
/**
* Third LPF, calculate one filter output<br>
* Called at sampling rate
* @param newdata new ADC data
* @return result filter output
* @brief FIR low pass filter
*/
int32_t LPF_Calc3(int32_t newdata);
int32_t Noise3(void);
/**
* Initialize fourth LPF<br>
* Set all data to an initial value<br>
* @param initial value to preload into MACQ
* @param size depth of the filter, 2 to 16
* @return none
* @brief Initialize third LPF
*/
void LPF_Init4(int32_t initial, int32_t size);
/**
* Fourth LPF, calculate one filter output<br>
* Called at sampling rate
* @param newdata new ADC data
* @return result filter output
* @brief FIR low pass filter
*/
int32_t LPF_Calc4(int32_t newdata);
int32_t Noise4(void);
/**
* Initialize fifth LPF<br>
* Set all data to an initial value<br>
* @param initial value to preload into MACQ
* @param size depth of the filter, 2 to 16
* @return none
* @brief Initialize fifth LPF
*/
void LPF_Init5(int32_t initial, int32_t size);
/**
* fifth LPF, calculate one filter output<br>
* Called at sampling rate
* @param newdata new ADC data
* @return result filter output
* @brief FIR low pass filter
*/
int32_t LPF_Calc5(int32_t newdata);
int32_t Noise5(void);
/**
* Initialize Sixth LPF<br>
* Set all data to an initial value<br>
* @param initial value to preload into MACQ
* @param size depth of the filter, 2 to 16
* @return none
* @brief Initialize Sixth LPF
*/
void LPF_Init6(int32_t initial, int32_t size);
/**
* Sixth LPF, calculate one filter output<br>
* Called at sampling rate
* @param newdata new ADC data
* @return result filter output
* @brief FIR low pass filter
*/
int32_t LPF_Calc6(int32_t newdata);
int32_t Noise6(void);
/**
* Initialize Seventh LPF<br>
* Set all data to an initial value<br>
* @param initial value to preload into MACQ
* @param size depth of the filter, 2 to 16
* @return none
* @brief Initialize Seventh LPF
*/
void LPF_Init7(int32_t initial, int32_t size);
/**
* Seventh LPF, calculate one filter output<br>
* Called at sampling rate
* @param newdata new ADC data
* @return result filter output
* @brief FIR low pass filter
*/
int32_t LPF_Calc7(int32_t newdata);
int32_t Noise7(void);
/**
* 3-wide non recursive Median filter <br>
* Called with new data at sampling rate
* @param newdata new ADC data
* @return result filter output
* @brief Median filter
*/
int32_t Median(int32_t newdata);
/**
* 5-wide non recursive Median filter <br>
* Called with new data at sampling rate
* @param newdata new ADC data
* @return result filter output
* @brief Median filter
*/
int32_t Median5(int32_t newdata);
/**
* 5-wide non recursive Median filter, second independent instance<br>
* Uses separate internal arrays from Median5 — safe to call simultaneously.
* @param newdata new ADC data
* @return result filter output
* @brief Median filter (second instance)
*/
int32_t Median5_2(int32_t newdata);
/**
* 7-wide non recursive Median filter <br>
* Called with new data at sampling rate
* @param newdata new ADC data
* @return result filter output
* @brief Median filter
*/
int32_t Median7(int32_t newdata);
/**
* 60-Hz notch high-Q, alpha=0.99, IIR filter, assuming fs=1000 Hz.<br>
* y(n) = (256x(n) -503x(n-1) + 256x(n-2) + 498y(n-1)-251y(n-2))/256 (2k sampling)<br>
* y(n) = (256x(n) -476x(n-1) + 256x(n-2) + 471y(n-1)-251y(n-2))/256 (1k sampling)
* @param data new ADC data
* @return result filter output
* @brief 60-Hz notch high-Q, IIR filter
*/
int32_t Filter(int32_t data);
/**
* Independent second instance of the 60-Hz notch IIR filter<br>
* Identical coefficients to Filter(), but separate internal state.<br>
* Use for a second simultaneous signal (e.g. left IR sensor).
* @param data new ADC data
* @return result filter output
* @brief 60-Hz notch high-Q, IIR filter (second instance)
*/
int32_t Filter2(int32_t data);

View File

@@ -0,0 +1,144 @@
/* LaunchPad.c
* Jonathan Valvano
* November 7, 2025
* RTOS version
* Derived from gpio_toggle_output_LP_MSPM0G3507_nortos_ticlang
*/
#include <ti/devices/msp/msp.h>
#include "../RTOS_Labs_common/LaunchPad.h"
void Delay(uint32_t cycles){
/* There will be a 2 cycle delay here to fetch & decode instructions
* if branch and linking to this function */
/* Subtract 2 net cycles for constant offset: +2 cycles for entry jump,
* +2 cycles for exit, -1 cycle for a shorter loop cycle on the last loop,
* -1 for this instruction */
#ifdef __GNUC__
__asm(".syntax unified");
#endif
__asm volatile(
" SUBS R0, R0, #2; \n"
"Delay_Loop: SUBS R0, R0, #4; \n" // C=1 if no overflow
" NOP; \n" // C=0 when R0 passes through 0
" BHS Delay_Loop; \n"
/* Return: 2 cycles */
);
}
// 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, index 39 in IOMUX PINCM table
// PB21 is S2 negative logic switch, index 48 in IOMUX PINCM table
void ActivatePortA_B(void){
// Reset two GPIO peripherals
// RSTCLR
// bits 31-24 unlock key 0xB1
// bit 1 is Clear reset sticky bit
// bit 0 is reset gpio port
GPIOA->GPRCM.RSTCTL = (uint32_t)0xB1000003;
GPIOB->GPRCM.RSTCTL = (uint32_t)0xB1000003;
// Enable power to two GPIO peripherals
// PWREN
// bits 31-24 unlock key 0x26
// bit 0 is Enable Power
GPIOA->GPRCM.PWREN = (uint32_t)0x26000001;
GPIOB->GPRCM.PWREN = (uint32_t)0x26000001;
Delay(24); // time for gpio to power up
}
// Initialize LEDs and switches
// Input: none
// Output:none
void LaunchPad_Init(void){
ActivatePortA_B();
// PINCM
// bit 25 is HiZ
// bit 20 is drive strength (only available for PA10 PA11 PA28 PA31)
// bit 18 is input enable control
// bit 17 is pull up control
// bit 16 is pull down control
// bit 7 is PC peripheral connected, enable transparent data flow
// bit 0 selects GPIO function
IOMUX->SECCFG.PINCM[PA0INDEX] = (uint32_t) 0x00000081;
IOMUX->SECCFG.PINCM[PA18INDEX] = (uint32_t) 0x00050081; // input, pull down
IOMUX->SECCFG.PINCM[PB21INDEX] = (uint32_t) 0x00060081; // input, pull up
IOMUX->SECCFG.PINCM[PB22INDEX] = (uint32_t) 0x00000081;
IOMUX->SECCFG.PINCM[PB26INDEX] = (uint32_t) 0x00000081;
IOMUX->SECCFG.PINCM[PB27INDEX] = (uint32_t) 0x00000081;
// DOE31_0 Data output enable
GPIOA->DOE31_0 |= RED1;
GPIOB->DOE31_0 |= BLUE | RED | GREEN; // enable outputs
// DOUT31_0 read/write data output pins
GPIOA->DOUT31_0 &= ~RED1; // LED1 off
GPIOB->DOUT31_0 &= ~(BLUE | RED | GREEN); // clear LED2
}
// **** LaunchPad_InS1 *****
// Read S1, positive logic switch on PA18
// return 0 if S1 is not pressed
// 0x00040000 if S1 is pressed
uint32_t LaunchPad_InS1(void){ uint32_t data;
data = GPIOA->DIN31_0; // read all of Port A
data = data & S1; // select bit 18
return data;
}
// **** LaunchPad_InS2 *****
// Read S2, negative logic switch on PB21
// return 0 if S2 is not pressed
// 0x00200000 if S2 is pressed
uint32_t LaunchPad_InS2(void){ uint32_t data;
data = GPIOB->DIN31_0; // read all of Port B
data = data ^ S2; // convert to positive logic
return data & S2; // select bit 21
}
// **** LaunchPad_LED1 *****
// Set LED1, negative logic LED on PA0
// Input led=0 to PA0=1, turn off LED1
// led=1 to PA0=0, turn on LED1
void LaunchPad_LED1(uint32_t led){uint32_t data;
data = GPIOA->DOUT31_0; // read all of previous values
data = data & ~0x01; // clear bit 0
data = data | (led^1); // new value for bit 0
GPIOA->DOUT31_0 = data; // change bit 0
}
// **** LaunchPad_LED1on *****
// Set LED1 on PA0, negative logic
// Makes PA0=0 to turn on LED
void LaunchPad_LED1on(void){
GPIOA->DOUTCLR31_0 = RED1;
}
// **** LaunchPad_LED1off *****
// Set LED1 on PA0, negative logic
// Makes PA0=1 to turn off LED
void LaunchPad_LED1off(void){
GPIOA->DOUTSET31_0 = RED1;
}
// **** LaunchPad_LED *****
// Set LED, 3-color positive logic LED on PB22,PB26,PB27
// Input led=0 to turn off LED
// led bit 22 sets blue color
// led bit 26 sets red color
// led bit 27 sets green color
void LaunchPad_LED(uint32_t led){uint32_t data;
data = GPIOB->DOUT31_0; // read all of previous values
data = data & ~(BLUE|RED|GREEN); // clear bits 22,26,27
data = data | led; // new values for bits 22,26,27
GPIOB->DOUT31_0 = data; // change bits 22,26,27
}
// **** LaunchPad_LEDwhite *****
// Set LED to white, 3-color positive logic LED on PB22,PB26,PB27
void LaunchPad_LEDwhite(void){
GPIOB->DOUTSET31_0 = BLUE|RED|GREEN;
}
// **** LEDoff *****
// turn off LED, 3-color positive logic LED on PB22,PB26,PB27
void LaunchPad_LEDoff(void){
GPIOB->DOUTCLR31_0 = BLUE|RED|GREEN;
}

View File

@@ -0,0 +1,314 @@
/*!
* @defgroup LaunchPad
* @brief LaunchPad input/output
<table>
<caption id="LaunchPadpins">Pins on the MSPM0G3507 LaunchPad</caption>
<tr><th>Pin <th>GPIO<th>Hardware
<tr><td>PA0 <td>output<td>red LED1, index 0 in IOMUX PINCM table, negative logic
<tr><td>PB22<td>output<td>BLUE LED2, index 49 in IOMUX PINCM table
<tr><td>PB26<td>output<td>RED LED2, index 56 in IOMUX PINCM table
<tr><td>PB27<td>output<td>GREEN LED2, index 57 in IOMUX PINCM table
<tr><td>PA18<td>input <td>S1 positive logic switch, index 39 in IOMUX PINCM table
<tr><td>PB21<td>input <td>S2 negative logic switch, index 48 in IOMUX PINCM table
</table>
* @{*/
/**
* @file LaunchPad.h
* @brief Initialize LaunchPad switches and LEDs
* @details MSPM0G3507 LaunchPad Development Kit (LP-MSPM0G3507)<br>
* For more information see<br> https://www.ti.com/product/LP-MSPM0G3507/part-details/LP-MSPM0G3507<br>
* \image html Fg01_07_02_LaunchPad.png width=500px
* \image latex Fg01_07_02_LaunchPad.png "TI MSPM0 LaunchPad" width=10cm
* The following is a simplified circuit diagram<br>
* \image html LaunchPadCircuit.png width=500px
* \image latex LaunchPadCircuit.png "LP-MSPM0G3507 Circuit Diagram" width=10cm<br>
* - LaunchPad Jumpers
* - J4 Connects PA0 to red LED1
* - J5: Connects PB22 to blue LED2
* - J6: Connects PB26 to red LED2
* - J7: Connects PB27 to green LED2
* @version ECE319K v1.2
* @author Daniel Valvano and Jonathan Valvano
* @copyright Copyright 2025 by Jonathan W. Valvano, valvano@mail.utexas.edu,
* @warning AS-IS
* @note For more information see http://users.ece.utexas.edu/~valvano/
* @date November 3, 2025
<table>
<caption id="LaunchPadpins2">Pins on the MSPM0G3507 LaunchPad</caption>
<tr><th>Pin <th>GPIO<th>Hardware
<tr><td>PA0 <td>output<td>RED LED1, index 0 in IOMUX PINCM table, negative logic
<tr><td>PB22<td>output<td>BLUE LED2, index 49 in IOMUX PINCM table
<tr><td>PB26<td>output<td>RED LED2, index 56 in IOMUX PINCM table
<tr><td>PB27<td>output<td>GREEN LED2, index 57 in IOMUX PINCM table
<tr><td>PA18<td>input <td>S1 positive logic switch, index 39 in IOMUX PINCM table
<tr><td>PB21<td>input <td>S2 negative logic switch, index 48 in IOMUX PINCM table
</table>
Mode=0 selects the GPIO pin<br>
<table>
<caption id="PINCM">PINCM Mode values for the MSPM0G3507</caption>
<tr><th>Name<th>Value<th>Mode=2<th>Mode=3<th>Mode=4<th>Mode=5<th>Mode=6<th>Mode=7<th>Mode=8<th>Mode=9
<tr><td>PA0INDEX<td>0<td>UART0_TX<td>I2C0_SDA<td>TIMA0_C0<td>TIMA_FAL1<td>TIMG8_C1<td>FCC_IN<td> <td>
<tr><td>PA1INDEX<td>1<td>UART0_RX<td>I2C0_SCL<td>TIMA0_C1<td>TIMA_FAL2<td>TIMG8_IDX<td>TIMG8_C0<td> <td>
<tr><td>PA2INDEX<td>6<td>TIMG8_C1<td>SPI0_CS0<td>TIMG7_C1<td>SPI1_CS0<td> <td> <td> <td>
<tr><td>PA3INDEX<td>7<td>TIMG8_C0<td>SPI0_CS1<td>UART2_CTS<td>TIMA0_C2<td>COMP1_OUT<td>TIMG7_C0<td>TIMA0_C1<td>I2C1_SDA
<tr><td>PA4INDEX<td>8<td>TIMG8_C1<td>SPI0_POCI<td>UART2_RTS<td>TIMA0_C3<td>LFCLK_IN<td>TIMG7_C1<td>TIMA0_C1N<td>I2C1_SCL
<tr><td>PA5INDEX<td>9<td>TIMG8_C0<td>SPI0_PICO<td>TIMA_FAL1<td>TIMG0_C0<td>TIMG6_C0<td>FCC_IN<td> <td>
<tr><td>PA6INDEX<td>10<td>TTIMG8_C1<td>SPI0_SCK<td>TIMA_FAL0<td>TIMG0_C1<td>HFCLK_IN<td>TIMG6_C1<td>TIMA0_C2N<td>
<tr><td>PA7INDEX<td>13<td>COMP0_OUT<td>CLK_OUT<td>TIMG8_C0<td>TIMA0_C2<td>TIMG8_IDX<td>TIMG7_C1<td>TIMA0_C1<td>
<tr><td>PA8INDEX<td>18<td>UART1_TX<td>SPI0_CS0<td>UART0_RTS<td>TIMA0_C0<td>TIMA1_C0N<td> <td> <td>
<tr><td>PA9INDEX<td>19<td>UART1_RX<td>SPI0_PICO<td>UART0_CTS<td>TIMA0_C1<td>RTC_OUT<td>TIMA0_C0N<td>TIMA1_C1N<td>CLK_OUT
<tr><td>PA10INDEX<td>20<td>UART0_TX<td>SPI0_POCI<td>I2C0_SDA<td>TIMA1_C0<td>TIMG12_C0<td>TIMA0_C2<td>I2C1_SDA<td>CLK_OUT
<tr><td>PA11INDEX<td>21<td>UART0_RX<td>SPI0_SCK<td>I2C0_SCL<td>TIMA1_C1<td>COMP0_OUT<td>TIMA0_C2N<td>I2C1_SCL<td>
<tr><td>PA12INDEX<td>33<td>UART3_CTS<td>SPI0_SCK<td>TIMG0_C0<td>CAN_TX<td>TIMA0_C3<td>FCC_IN<td> <td>
<tr><td>PA13INDEX<td>34<td>UART3_RTS<td>SPI0_POCI<td>UART3_RX<td>TIMG0_C1<td>CAN_RX<td>TIMA0_C3N<td> <td>
<tr><td>PA14INDEX<td>35<td>UART0_CTS<td>SPI0_PICO<td>UART3_TX<td>TIMG12_C0<td>CLK_OUT<td> <td> <td>
<tr><td>PA15INDEX<td>36<td>UART0_RTS<td>SPI1_CS2<td>I2C1_SCL<td>TIMA1_C0<td>TIMG8_IDX<td>TIMA1_C0N<td>TIMA0_C2<td>
<tr><td>PA16INDEX<td>37<td>COMP2_OUT<td>SPI1_POCI<td>I2C1_SDA<td>TIMA1_C1<td>TIMA1_C1N<td>TIMA0_C2N<td>FCC_IN<td>
<tr><td>PA17INDEX<td>38<td>UART1_TX<td>SPI1_SCK<td>I2C1_SCL<td>TIMA0_C3<td>TIMG7_C0<td>TIMA1_C0<td> <td>
<tr><td>PA18INDEX<td>39<td>UART1_RX<td>SPI1_PICO<td>I2C1_SDA<td>TIMA0_C3N<td>TIMG7_C1<td>TIMA1_C1<td> <td>
<tr><td>PA19INDEX<td>40<td>SWDIO<td> <td> <td> <td> <td> <td> <td>
<tr><td>PA20INDEX<td>41<td>SWCLK<td> <td> <td> <td> <td> <td> <td>
<tr><td>PA21INDEX<td>45<td>UART2_TX<td>TIMG8_C0<td>UART1_CTS<td>TIMA0_C0<td>TIMG6_C0<td> <td> <td>
<tr><td>PA22INDEX<td>46<td>UART2_RX<td>TIMG8_C1<td>UART1_RTS<td>TIMA0_C1<td>CLK_OUT<td>TIMA0_C0N<td>TIMG6_C1<td>
<tr><td>PA23INDEX<td>52<td>UART2_TX<td>SPI0_CS3<td>TIMA0_C3<td>TIMG0_C0<td>UART3_CTS<td>TIMG7_C0<td>TIMG8_C0<td>
<tr><td>PA24INDEX<td>53<td>UART2_RX<td>SPI0_CS2<td>TIMA0_C3N<td>TIMG0_C1<td>UART3_RTS<td>TIMG7_C1<td>TIMA1_C1<td>
<tr><td>PA25INDEX<td>54<td>UART3_RX<td>SPI1_CS3<td>TIMG12_C1<td>TIMA0_C3<td>TIMA0_C1N<td> <td> <td>
<tr><td>PA26INDEX<td>58<td>UART3_TX<td>SPI1_CS0<td>TIMG8_C0<td>TIMA_FAL0<td>CAN_TX<td>TIMG7_C0<td> <td>
<tr><td>PA27INDEX<td>59<td>RTC_OUT<td>SPI1_CS1<td>TIMG8_C1<td>TIMA_FAL2<td>CAN_RX<td>TIMG7_C1<td> <td>
<tr><td>PA28INDEX<td>2<td>UART0_TX<td>I2C0_SDA<td>TIMA0_C3<td>TIMA_FAL0<td>TIMG7_C0<td>TIMA1_C0<td> <td>
<tr><td>PA29INDEX<td>3<td>I2C1_SCL<td>UART2_RTS<td>TIMG8_C0<td>TIMG6_C0<td> <td> <td> <td>
<tr><td>PA30INDEX<td>4<td>I2C1_SDA<td>UART2_CTS<td>TIMG8_C1<td>TIMG6_C1<td> <td> <td> <td>
<tr><td>PA31INDEX<td>5<td>UART0_RX<td>I2C0_SCL<td>TIMA0_C3N<td>TIMG12_C1<td>CLK_OUT<td>TIMG7_C1<td>TIMA1_C1<td>
<tr><td>PB0INDEX<td>11<td>UART0_TX<td>SPI1_CS2<td>TIMA1_C0<td>TIMA0_C2<td> <td> <td> <td>
<tr><td>PB1INDEX<td>12<td>UART0_RX<td>SPI1_CS3<td>TIMA1_C1<td>TIMA0_C2N<td> <td> <td> <td>
<tr><td>PB2INDEX<td>14<td>UART3_TX<td>UART2_CTS<td>I2C1_SCL<td>TIMA0_C3<td>UART1_CTS<td>TIMG6_C0<td>TIMA1_C0<td>
<tr><td>PB3INDEX<td>15<td>UART3_RX<td>UART2_RTS<td>I2C1_SDA<td>TIMA0_C3N<td>UART1_RTS<td>TIMG6_C1<td>TIMA1_C1<td>
<tr><td>PB4INDEX<td>16<td>UART1_TX<td>UART3_CTS<td>TIMA1_C0<td>TIMA0_C2<td>TIMA1_C0N<td> <td> <td>
<tr><td>PB5INDEX<td>17<td>UART1_RX<td>UART3_RTS<td>TIMA1_C1<td>TIMA0_C2N<td>TIMA1_C1N<td> <td> <td>
<tr><td>PB6INDEX<td>22<td>UART1_TX<td>SPI1_CS0<td>SPI0_CS1<td>TIMG8_C0<td>UART2_CTS<td>TIMG6_C0<td>TIMA1_C0N<td>
<tr><td>PB7INDEX<td>23<td>UART1_RX<td>SPI1_POCI<td>SPI0_CS2<td>TIMG8_C1<td>UART2_RTS<td>TIMG6_C1<td>TIMA1_C1N<td>
<tr><td>PB8INDEX<td>24<td>UART1_CTS<td>SPI1_PICO<td>TIMA0_C0<td>COMP1_OUT<td> <td> <td> <td>
<tr><td>PB9INDEX<td>25<td>UART1_RTS<td>SPI1_SCK<td>TIMA0_C1<td>TIMA0_C0N<td> <td> <td> <td>
<tr><td>PB10INDEX<td>26<td>TIMG0_C0<td>TIMG8_C0<td>COMP1_OUT<td>TIMG6_C0<td> <td> <td> <td>
<tr><td>PB11INDEX<td>27<td>TIMG0_C1<td>TIMG8_C1<td>CLK_OUT<td>TIMG6_C1<td> <td> <td> <td>
<tr><td>PB12INDEX<td>28<td>UART3_TX<td>TIMA0_C2<td>TIMA_FAL1<td>TIMA0_C1<td> <td> <td> <td>
<tr><td>PB13INDEX<td>29<td>UART3_RX<td>TIMA0_C3<td>TIMG12_C0<td>TIMA0_C1N<td> <td> <td> <td>
<tr><td>PB14INDEX<td>30<td>SPI1_CS3<td>SPI1_POCI<td>SPI0_CS3<td>TIMG12_C1<td>TIMG8_IDX<td>TIMA0_C0<td> <td>
<tr><td>PB15INDEX<td>31<td>UART2_TX<td>SPI1_PICO<td>UART3_CTS<td>TIMG8_C0<td>TIMG7_C0<td> <td> <td>
<tr><td>PB16INDEX<td>32<td>UART2_RX<td>SPI1_SCK<td>UART3_RTS<td>TIMG8_C1<td>TIMG7_C1<td> <td> <td>
<tr><td>PB17INDEX<td>42<td>UART2_TX<td>SPI0_PICO<td>SPI1_CS1<td>TIMA1_C0<td>TIMA0_C2<td> <td> <td>
<tr><td>PB18INDEX<td>43<td>UART2_RX<td>SPI0_SCK<td>SPI1_CS2<td>TIMA1_C1<td>TIMA0_C2N<td> <td> <td>
<tr><td>PB19INDEX<td>44<td>COMP2_OUT<td>SPI0_POCI<td>TIMG8_C1<td>UART0_CTS<td>TIMG7_C1<td> <td> <td>
<tr><td>PB20INDEX<td>47<td>SPI0_CS2<td>SPI1_CS0<td>TIMA0_C2<td>TIMG12_C0<td>TIMA_FAL1<td>TIMA0_C1<td>TIMA1_C1N<td>
<tr><td>PB21INDEX<td>48<td>SPI1_POCI<td>TIMG8_C0<td> <td> <td> <td> <td> <td>
<tr><td>PB22INDEX<td>49<td>SPI1_PICO<td>TIMG8_C1<td> <td> <td> <td> <td> <td>
<tr><td>PB23INDEX<td>50<td>SPI1_SCK<td>COMP0_OUT<td>TIMA_FAL0<td> <td> <td> <td> <td>
<tr><td>PB24INDEX<td>51<td>SPI0_CS3<td>SPI0_CS1<td>TIMA0_C3<td>TIMG12_C1<td>TIMA0_C1N<td>TIMA1_C0N<td> <td>
<tr><td>PB25INDEX<td>55<td>UART0_CTS<td>SPI0_CS0<td>TIMA_FAL2<td> <td> <td> <td> <td>
<tr><td>PB26INDEX<td>56<td>UART0_RTS<td>SPI0_CS1<td>TIMA0_C3<td>TIMG6_C0<td>TIMA1_C0<td> <td> <td>
<tr><td>PB27INDEX<td>57<td>COMP2_OUT<td>SPI1_CS1<td>TIMA0_C3N<td>TIMG6_C1<td>TIMA1_C1<td> <td> <td>
</table>
******************************************************************************/
#ifndef __LAUNCHPAD_H__
#define __LAUNCHPAD_H__
#include <stdint.h>
/**
* \brief RED1 is a constant to select red LED1 on Port A, PA0
*/
#define RED1 1
/**
* \brief BLUE is a constant to select blue LED2 on Port B, PB22
*/
#define BLUE (1<<22)
/**
* \brief RED is a constant to select red LED2 on Port B, PB26
*/
#define RED (1<<26)
/**
* \brief GREEN is a constant to select green LED2 on Port B, PB27
*/
#define GREEN (1<<27)
/**
* \brief S1 is a constant to select switch S1 on Port A, PA18
*/
#define S1 (1<<18)
/**
* \brief S2 is a constant to select switch S2 on Port B, PB21
*/
#define S2 (1<<21)
/**
* Initialize LEDs and switches on MSPM0G3507 LaunchPad
* - PA0 output RED LED1, negative logic
* - PB22 output BLUE LED2, positive logic
* - PB26 output RED LED2, positive logic
* - PB27 output GREEN LED2,positive logic
* - PA18 input S1 switch, positive logic
* - PB21 input S2 switch, negative logic
*
* @param none
* @return none
* @brief Initialize LaunchPad
* @note In most ECE319K example code, this function is called first and will reset and enable power to Port A and Port B
* @warning Do not call this twice
*/
void LaunchPad_Init(void);
/**
* Read S1, positive logic switch on PA18
* @param none
* @return 0 if S1 is not pressed, 0x00040000 if S1 is pressed
* @see LaunchPad_Init()
* @brief Input S1
*/
uint32_t LaunchPad_InS1(void);
/**
* Read S2, negative logic switch on PA18. The software converts to positive logic.
* @param none
* @return 0 if S3 is not pressed, 0x00200000 if S2 is pressed
* @see LaunchPad_Init()
* @brief Input S2
*/
uint32_t LaunchPad_InS2(void);
/**
* Set LED1, negative logic LED on PA0
* - led=0 to PA0=1, turn off LED1
* - led=1 to PA0=0, turn on LED1
*
* @param led 1 to turn on, 0 to turn off
* @return none
* @brief Output to LED1
*/
void LaunchPad_LED1(uint32_t led);
/**
* Turn on LED1. Makes PA0=0 to turn on LED1
* @param none
* @return none
* @brief Turn on LED1
*/
void LaunchPad_LED1on(void);
/**
* Turn off LED1. Makes PA0=1 to turn off LED
* @param none
* @return none
* @brief Turn off LED1
*/
void LaunchPad_LED1off(void);
/**
* Set LED, 3-color positive logic LED on PB22,PB26,PB27
* - led=0 to turn off LED
* - led bit 22 sets blue color
* - led bit 26 sets red color
* - led bit 27 sets green color
*
* @param led sets the color of LED
* @return none
* @brief Output to LED
*/
void LaunchPad_LED(uint32_t led);
/**
* Set LED to white. Make PB22=1,PB26=1,PB27=1 to create white
* @param none
* @return none
* @brief Make LED white
*/
void LaunchPad_LEDwhite(void);
/**
* Turn off LED. Make PB22=0,PB26=0,PB27=0 to turn off LED
* @param none
* @return none
* @brief Turn off LED
*/
void LaunchPad_LEDoff(void);
/**
* \brief The following constants are used to index into the PINCM table
*/
// Mode2 Mode3 Mode4 Mode5 Mode6 Mode7 Mode8 Mode9
#define PA0INDEX 0 // UART0_TX I2C0_SDA TIMA0_C0 TIMA_FAL1 TIMG8_C1 FCC_IN
#define PA1INDEX 1 // UART0_RX I2C0_SCL TIMA0_C1 TIMA_FAL2 TIMG8_IDX TIMG8_C0
#define PA2INDEX 6 // TIMG8_C1 SPI0_CS0 TIMG7_C1 SPI1_CS0
#define PA3INDEX 7 // TIMG8_C0 SPI0_CS1 UART2_CTS TIMA0_C2 COMP1_OUT TIMG7_C0 TIMA0_C1 I2C1_SDA
#define PA4INDEX 8 // TIMG8_C1 SPI0_POCI UART2_RTS TIMA0_C3 LFCLK_IN TIMG7_C1 TIMA0_C1N I2C1_SCL
#define PA5INDEX 9 // TIMG8_C0 SPI0_PICO TIMA_FAL1 TIMG0_C0 TIMG6_C0 FCC_IN
#define PA6INDEX 10 // TTIMG8_C1 SPI0_SCK TIMA_FAL0 TIMG0_C1 HFCLK_IN TIMG6_C1 TIMA0_C2N
#define PA7INDEX 13 // COMP0_OUT CLK_OUT TIMG8_C0 TIMA0_C2 TIMG8_IDX TIMG7_C1 TIMA0_C1
#define PA8INDEX 18 // UART1_TX SPI0_CS0 UART0_RTS TIMA0_C0 TIMA1_C0N
#define PA9INDEX 19 // UART1_RX SPI0_PICO UART0_CTS TIMA0_C1 RTC_OUT TIMA0_C0N TIMA1_C1N CLK_OUT
#define PA10INDEX 20 // UART0_TX SPI0_POCI I2C0_SDA TIMA1_C0 TIMG12_C0 TIMA0_C2 I2C1_SDA CLK_OUT
#define PA11INDEX 21 // UART0_RX SPI0_SCK I2C0_SCL TIMA1_C1 COMP0_OUT TIMA0_C2N I2C1_SCL
#define PA12INDEX 33 // UART3_CTS SPI0_SCK TIMG0_C0 CAN_TX TIMA0_C3 FCC_IN
#define PA13INDEX 34 // UART3_RTS SPI0_POCI UART3_RX TIMG0_C1 CAN_RX TIMA0_C3N
#define PA14INDEX 35 // UART0_CTS SPI0_PICO UART3_TX TIMG12_C0 CLK_OUT
#define PA15INDEX 36 // UART0_RTS SPI1_CS2 I2C1_SCL TIMA1_C0 TIMG8_IDX TIMA1_C0N TIMA0_C2
#define PA16INDEX 37 // COMP2_OUT SPI1_POCI I2C1_SDA TIMA1_C1 TIMA1_C1N TIMA0_C2N FCC_IN
#define PA17INDEX 38 // UART1_TX SPI1_SCK I2C1_SCL TIMA0_C3 TIMG7_C0 TIMA1_C0
#define PA18INDEX 39 // UART1_RX SPI1_PICO I2C1_SDA TIMA0_C3N TIMG7_C1 TIMA1_C1
#define PA19INDEX 40 // SWDIO
#define PA20INDEX 41 // SWCLK
#define PA21INDEX 45 // UART2_TX TIMG8_C0 UART1_CTS TIMA0_C0 TIMG6_C0
#define PA22INDEX 46 // UART2_RX TIMG8_C1 UART1_RTS TIMA0_C1 CLK_OUT TIMA0_C0N TIMG6_C1
#define PA23INDEX 52 // UART2_TX SPI0_CS3 TIMA0_C3 TIMG0_C0 UART3_CTS TIMG7_C0 TIMG8_C0
#define PA24INDEX 53 // UART2_RX SPI0_CS2 TIMA0_C3N TIMG0_C1 UART3_RTS TIMG7_C1 TIMA1_C1
#define PA25INDEX 54 // UART3_RX SPI1_CS3 TIMG12_C1 TIMA0_C3 TIMA0_C1N
#define PA26INDEX 58 // UART3_TX SPI1_CS0 TIMG8_C0 TIMA_FAL0 CAN_TX TIMG7_C0
#define PA27INDEX 59 // RTC_OUT SPI1_CS1 TIMG8_C1 TIMA_FAL2 CAN_RX TIMG7_C1
#define PA28INDEX 2 // UART0_TX I2C0_SDA TIMA0_C3 TIMA_FAL0 TIMG7_C0 TIMA1_C0
#define PA29INDEX 3 // I2C1_SCL UART2_RTS TIMG8_C0 TIMG6_C0
#define PA30INDEX 4 // I2C1_SDA UART2_CTS TIMG8_C1 TIMG6_C1
#define PA31INDEX 5 // UART0_RX I2C0_SCL TIMA0_C3N TIMG12_C1 CLK_OUT TIMG7_C1 TIMA1_C1
#define PB0INDEX 11 // UART0_TX SPI1_CS2 TIMA1_C0 TIMA0_C2
#define PB1INDEX 12 // UART0_RX SPI1_CS3 TIMA1_C1 TIMA0_C2N
#define PB2INDEX 14 // UART3_TX UART2_CTS I2C1_SCL TIMA0_C3 UART1_CTS TIMG6_C0 TIMA1_C0
#define PB3INDEX 15 // UART3_RX UART2_RTS I2C1_SDA TIMA0_C3N UART1_RTS TIMG6_C1 TIMA1_C1
#define PB4INDEX 16 // UART1_TX UART3_CTS TIMA1_C0 TIMA0_C2 TIMA1_C0N
#define PB5INDEX 17 // UART1_RX UART3_RTS TIMA1_C1 TIMA0_C2N TIMA1_C1N
#define PB6INDEX 22 // UART1_TX SPI1_CS0 SPI0_CS1 TIMG8_C0 UART2_CTS TIMG6_C0 TIMA1_C0N
#define PB7INDEX 23 // UART1_RX SPI1_POCI SPI0_CS2 TIMG8_C1 UART2_RTS TIMG6_C1 TIMA1_C1N
#define PB8INDEX 24 // UART1_CTS SPI1_PICO TIMA0_C0 COMP1_OUT
#define PB9INDEX 25 // UART1_RTS SPI1_SCK TIMA0_C1 TIMA0_C0N
#define PB10INDEX 26 // TIMG0_C0 TIMG8_C0 COMP1_OUT TIMG6_C0
#define PB11INDEX 27 // TIMG0_C1 TIMG8_C1 CLK_OUT TIMG6_C1
#define PB12INDEX 28 // UART3_TX TIMA0_C2 TIMA_FAL1 TIMA0_C1
#define PB13INDEX 29 // UART3_RX TIMA0_C3 TIMG12_C0 TIMA0_C1N
#define PB14INDEX 30 // SPI1_CS3 SPI1_POCI SPI0_CS3 TIMG12_C1 TIMG8_IDX TIMA0_C0
#define PB15INDEX 31 // UART2_TX SPI1_PICO UART3_CTS TIMG8_C0 TIMG7_C0
#define PB16INDEX 32 // UART2_RX SPI1_SCK UART3_RTS TIMG8_C1 TIMG7_C1
#define PB17INDEX 42 // UART2_TX SPI0_PICO SPI1_CS1 TIMA1_C0 TIMA0_C2
#define PB18INDEX 43 // UART2_RX SPI0_SCK SPI1_CS2 TIMA1_C1 TIMA0_C2N
#define PB19INDEX 44 // COMP2_OUT SPI0_POCI TIMG8_C1 UART0_CTS TIMG7_C1
#define PB20INDEX 47 // SPI0_CS2 SPI1_CS0 TIMA0_C2 TIMG12_C0 TIMA_FAL1 TIMA0_C1 TIMA1_C1N
#define PB21INDEX 48 // SPI1_POCI TIMG8_C0
#define PB22INDEX 49 // SPI1_PICO TIMG8_C1
#define PB23INDEX 50 // SPI1_SCK COMP0_OUT TIMA_FAL0
#define PB24INDEX 51 // SPI0_CS3 SPI0_CS1 TIMA0_C3 TIMG12_C1 TIMA0_C1N TIMA1_C0N
#define PB25INDEX 55 // UART0_CTS SPI0_CS0 TIMA_FAL2
#define PB26INDEX 56 // UART0_RTS SPI0_CS1 TIMA0_C3 TIMG6_C0 TIMA1_C0
#define PB27INDEX 57 // COMP2_OUT SPI1_CS1 TIMA0_C3N TIMG6_C1 TIMA1_C1
#endif // __LAUNCHPAD_H__
/** @}*/

1065
RTOS_Labs_common/OS.c Normal file

File diff suppressed because it is too large Load Diff

388
RTOS_Labs_common/OS.h Normal file
View File

@@ -0,0 +1,388 @@
/**
* @file OS.h
* @brief Real Time Operating System for Labs 1, 2, 3, 4 and 5
* @details ECE445M
* @version V1.0
* @author Valvano
* @copyright Copyright 2026 by Jonathan W. Valvano, valvano@mail.utexas.edu,
* @warning AS-IS
* @note For more information see http://users.ece.utexas.edu/~valvano/
* @date January 10, 2026
******************************************************************************/
#ifndef __OS_H
#define __OS_H 1
#include <stdint.h>
/**
* \brief Set which lab is active so I can use the same OS.c for all Labs
*/
#define LAB1 0
#define LAB2 0
#define LAB3 0
#define LAB4 0
#define LAB5 0
#define LAB6 1
/**
* \brief Times assuming a 80 MHz
*/
#define TIME_1MS 80000
#define TIME_2MS (2*TIME_1MS)
#define TIME_500US (TIME_1MS/2)
#define TIME_250US (TIME_1MS/4)
// Thread constants
#define MAXNUMTHREADS 10
#define STACKSIZE 256
#define NUMPRIORITIES 8
#define MAXEVENTS 200
// Fifo size
#define MAXFIFOSIZE 256
// Forward declaration so Sema4 can hold TCB pointers
typedef struct TCB TCB_t;
/**
* \brief Semaphore structure. Feel free to change the type of semaphore, there are lots of good solutions
*/
struct Sema4{
int32_t Value; // >0 means free, otherwise means busy
TCB_t* blockedList; // head of per-semaphore blocked queue (oldest waiter)
TCB_t* blockedTail; // tail of per-semaphore blocked queue (newest waiter)
};
typedef struct Sema4 Sema4_t;
// TCB state
typedef enum {FREE, ACTIVE} State_t;
// Thread Control Block structure
struct TCB{
int32_t* sp;
TCB_t* next;
TCB_t* prev;
State_t state;
uint8_t id;
uint32_t sleep;
uint32_t priority;
Sema4_t* blocked;
};
// Periodic thread structure
typedef struct Event Event_t;
struct Event{
void (*task)(void);
uint32_t timeToNext;
uint32_t period;
uint32_t priority;
};
// Mailbox structure
typedef struct Mailbox{
uint32_t data;
Sema4_t free;
Sema4_t dataValid;
} Mailbox_t;
// Fifo structure
typedef struct Fifo{
uint32_t PutI;
uint32_t GetI;
uint32_t data[MAXFIFOSIZE];
Sema4_t CurrentSize;
uint32_t MaxSize;
uint32_t LostData;
} Fifo_t;
/**
* @details Initialize operating system, disable interrupts until OS_Launch.
* Initialize OS controlled I/O: serial, ADC, systick, LaunchPad I/O and timers.
* Interrupts not yet enabled.
* @param none
* @return none
* @brief Initialize OS
*/
void OS_Init(void);
// ******** OS_InitSemaphore ************
// initialize semaphore
// input: pointer to a semaphore
// output: none
void OS_InitSemaphore(Sema4_t *semaPt, int32_t value);
// ******** OS_Wait ************
// decrement semaphore
// Lab2 spinlock
// Lab3 block if less than zero
// input: pointer to a counting semaphore
// output: none
void OS_Wait(Sema4_t *semaPt);
// ******** OS_Signal ************
// increment semaphore
// Lab2 spinlock
// Lab3 wakeup blocked thread if appropriate
// input: pointer to a counting semaphore
// output: none
void OS_Signal(Sema4_t *semaPt);
// ******** OS_bWait ************
// Lab2 spinlock, set to 0
// Lab3 block if less than zero
// input: pointer to a binary semaphore
// output: none
void OS_bWait(Sema4_t *semaPt);
// ******** OS_bSignal ************
// Lab2 spinlock, set to 1
// Lab3 wakeup blocked thread if appropriate
// input: pointer to a binary semaphore
// output: none
void OS_bSignal(Sema4_t *semaPt);
//******** OS_AddThread ***************
// add a foreground thread to the scheduler
// Inputs: pointer to a void/void foreground task
// number of bytes allocated for its stack
// priority, 0 is highest, 255 is the lowest
// Priorities are relative to other foreground threads
// Outputs: 1 if successful, 0 if this thread can not be added
// In Lab 2, you can ignore both the stackSize and priority fields
// In Lab 3, you can ignore the stackSize fields
// In Lab 4, the stackSize can be 128, 256, or 512 bytes
int OS_AddThread(void(*task)(void),
uint32_t stackSize, uint32_t priority);
//******** OS_Id ***************
// returns the thread ID for the currently running thread
// Inputs: none
// Outputs: Thread ID, number greater than zero
uint32_t OS_Id(void);
//******** OS_AddPeriodicThread ***************
// Add a background periodic thread
// typically this function receives the highest priority
// Inputs: task is pointer to a void/void background function
// period in ms
// priority 0 is the highest, 3 is the lowest
// Priorities are relative to other background periodic threads
// Outputs: 1 if successful, 0 if this thread can not be added
// You are free to select the resolution of period
// It is assumed that the user task will run to completion and return
// This task can not spin, block, loop, sleep, or kill
// This task can call OS_Signal OS_bSignal OS_AddThread
// This task does not have a Thread ID
// In lab 2, this command will be called 0 or 1 times
// In lab 3, this command will be called 0 to 4 times
// In labs 3-7, there will be 0 to 4 background periodic threads, and this priority field
// determines the relative priority of these threads
// For Lab 3, it ok to make reasonable limits to reduce the complexity. E.g.,
// - You can assume there are 0 to 4 background periodic threads
// - You can assume the priorities are sequential 0,1,2,3
// - You can assume a maximum thread execution time, e.g., 50us
// - You can assume a slowest period, e.g., 50ms
// - You can limit possible periods, e.g., 1,2,4,5,10,20,25,50ms
// - You can assume (E0/T0)+(E1/T1)+(E2/T2)+(E3/T3) is much less than 1
int OS_AddPeriodicThread(void(*task)(void),
uint32_t period, uint32_t priority);
// ******** OS_AddS1Task ***************
// add a background task to run whenever the S1 (PA18) button is pushed
// Inputs: pointer to a void/void background function
// priority 0 is the highest, 1 is the lowest
// Outputs: 1 if successful, 0 if this thread can not be added
// It is assumed that the user task will run to completion and return
// This task can not spin, block, loop, sleep, or kill
// This task can call OS_Signal OS_bSignal OS_AddThread
// This task does not have a Thread ID
// Because of the pin conflict with TFLuna, this command will not be called
// In lab 2, the priority field can be ignored
// In labs 3-7, there will be many background threads, and this priority field
// determines the relative priority of these threads
int OS_AddS1Task(void(*task)(void), uint32_t priority);
// ******** OS_AddS2Task ***************
// add a background task to run whenever the S2 (PB21) button is pushed
// Inputs: pointer to a void/void background function
// priority 0 is highest, 1 is lowest
// Outputs: 1 if successful, 0 if this thread can not be added
// It is assumed user task will run to completion and return
// This task can not spin block loop sleep or kill
// This task can call issue OS_Signal, it can call OS_AddThread
// This task does not have a Thread ID
// In labs 3-7, this command will be called will be called 0 or 1 times
// In labs 3-7, there will be many background threads, and this priority field
// determines the relative priority of these threads
int OS_AddS2Task(void(*task)(void), uint32_t priority);
// ******** OS_AddPA28Task ***************
// add a background task to run whenever the bump (PA28) button is pushed
// Inputs: pointer to a void/void background function
// priority 0 is the highest, 1 is the lowest
// Outputs: 1 if successful, 0 if this thread can not be added
// It is assumed that the user task will run to completion and return
// This task can not spin, block, loop, sleep, or kill
// This task can call OS_Signal OS_bSignal OS_AddThread
// This task does not have a Thread ID
// In lab 3, this command will be called 0 or 1 times
// In lab 2, not implemented
// In lab 3, there will be many background threads, and this priority field
// determines the relative priority of these four threads
int OS_AddPA28Task(void(*task)(void), uint32_t priority);
// ******** OS_Sleep ************
// place this thread into a dormant state
// input: number of msec to sleep
// output: none
// You are free to select the time resolution for this function
// OS_Sleep(0) implements cooperative multitasking
void OS_Sleep(uint32_t sleepTime);
// ******** OS_Kill ************
// kill the currently running thread, release its TCB and stack
// input: none
// output: none
void OS_Kill(void);
// ******** OS_Suspend ************
// suspend execution of currently running thread
// scheduler will choose another thread to execute
// Can be used to implement cooperative multitasking
// Same function as OS_Sleep(0)
// input: none
// output: none
void OS_Suspend(void);
// temporarily prevent foreground thread switch (but allow background interrupts)
uint32_t OS_LockScheduler(void);
// resume foreground thread switching
void OS_UnLockScheduler(uint32_t previous);
// ******** OS_Fifo_Init ************
// Initialize the Fifo to be empty
// Inputs: size
// Outputs: none
// In Lab 2, you can ignore the size field
// In Lab 3, you should implement the user-defined fifo size
// In Lab 3, you can put whatever restrictions you want on size
// e.g., 4 to 64 elements
// e.g., must be a power of 2,4,8,16,32,64,128
void OS_Fifo_Init(uint32_t size);
// ******** OS_Fifo_Put ************
// Enter one data sample into the Fifo
// Called from the background, so no waiting
// Inputs: data
// Outputs: true if data is properly saved,
// false if data not saved, because it was full
// Since this is called by interrupt handlers
// this function can not disable or enable interrupts
int OS_Fifo_Put(uint32_t data);
// ******** OS_Fifo_Get ************
// Remove one data sample from the Fifo
// Called in foreground, will spin/block if empty
// Inputs: none
// Outputs: data
uint32_t OS_Fifo_Get(void);
// ******** OS_Fifo_Size ************
// Check the status of the Fifo
// Inputs: none
// Outputs: returns the number of elements in the Fifo
// greater than zero if a call to OS_Fifo_Get will return right away
// zero or less than zero if the Fifo is empty
// zero or less than zero if a call to OS_Fifo_Get will spin or block
int32_t OS_Fifo_Size(void);
// ******** OS_MailBox_Init ************
// Initialize communication channel
// Inputs: none
// Outputs: none
void OS_MailBox_Init(void);
// ******** OS_MailBox_Send ************
// enter mail into the MailBox
// Inputs: data to be sent
// Outputs: none
// This function will be called from a foreground thread
// It will spin/block if the MailBox contains data not yet received
void OS_MailBox_Send(uint32_t data);
// ******** OS_MailBox_Recv ************
// remove mail from the MailBox
// Inputs: none
// Outputs: data received
// This function will be called from a foreground thread
// It will spin/block if the MailBox is empty
uint32_t OS_MailBox_Recv(void);
// ******** OS_Time ************
// return the system time counting up
// Inputs: none
// Outputs: time in 12.5ns units, 0 to 4294967295
// The time resolution should be less than or equal to 1us, and the precision 32 bits
// It is ok to change the resolution and precision of this function as long as
// this function and OS_TimeDifference have the same resolution and precision
uint32_t OS_Time(void);
// ******** OS_TimeDifference ************
// Calculates difference between two times
// Inputs: two times measured with OS_Time
// Outputs: time difference in 12.5ns units
// The time resolution should be less than or equal to 1us, and the precision at least 12 bits
// It is ok to change the resolution and precision of this function as long as
// this function and OS_Time have the same resolution and precision
uint32_t OS_TimeDifference(uint32_t start, uint32_t stop);
// ******** OS_ClearMsTime ************
// sets the system time to zero (from Lab 1)
// Inputs: none
// Outputs: none
// You are free to change how this works
void OS_ClearMsTime(void);
// ******** OS_MsTime ************
// reads the current time in msec
// Inputs: none
// Outputs: time in ms units
// You are free to select the time resolution for this function
// It is ok to make the resolution to match the first call to OS_AddPeriodicThread
uint32_t OS_MsTime(void);
//******** OS_Launch ***************
// start the scheduler, enable interrupts
// Inputs: number of 12.5ns clock cycles for each time slice
// you may select the units of this parameter
// Outputs: none (does not return)
// In Lab 2, you can ignore the theTimeSlice field
// In Lab 3, you should implement the user-defined TimeSlice field
// It is ok to limit the range of theTimeSlice to match the 24-bit SysTick
void OS_Launch(uint32_t theTimeSlice);
// Program has these fields
struct Program{
uint32_t StartOffset; // offset to starting location
uint32_t CodeSize; // size of code segment
uint32_t StackSize; // size of stack segment
uint32_t DataSize; // size of data segment (globals)
char Name[8]; // ASCII string
};
typedef struct Program Program_t;
// Load program from disk and launch process
// open file for reading
// read StartOffset,CodeSize,StackSize,DataSize,Name
// Allocate spaces in RAM for data, stack, and code segments
// Reads the object code from file into the code segment
// Closes the file
// Add program as a process, create a thread for it, and execute
// SP => stack segment
// R7 => data segment
// PC => code segment (entry point at first location)
// Inputs: name is the name of the file on SDC
// priority is the thread priority
// Output: 1 success, 0 failure
int OS_LoadProgram(char *name, uint32_t priority);
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

129
RTOS_Labs_common/PWMA0.c Normal file
View File

@@ -0,0 +1,129 @@
/* PWMA0.c
* Jonathan Valvano
* November 24, 2025
* Derived from timx_timer_mode_pwm_edge_sleep_LP_MSPM0G3507_nortos_ticlang
*/
/*
* Copyright (c) 2026, Texas Instruments Incorporated
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Texas Instruments Incorporated nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <ti/devices/msp/msp.h>
#include "../inc/LaunchPad.h"
#include "../RTOS_Labs_common/PWMA0.h"
#include "../inc/Clock.h"
// Timer A0 is on Power domain PD1
// for 32MHz bus clock, Timer clock is 32MHz
// for 40MHz bus clock, Timer clock is MCLK 40MHz
// for 80MHz bus clock, Timer clock is MCLK 80MHz
// CCP0 PWM output on PB8
// CCP1 PWM output on PB9
// PB8/TIMA0_C0;
// PB9/TIMA0_C1;
uint32_t PWMA0_Period;
// Initialize PWM outputs on PA12 PA13
// Rising edge synchronized
// timerClkSrc = 2 for 32768 Hz LFCLK
// = 4 for 4MHz MFCLK
// = 8 for 80/40/32/4 BUSCLK
// divide clock by timerClkPrescale+1, 0 to 255
// period sets the PWM period
// timerClkSrc could be 40MHz, 32MHz, 4MHz, or 32767Hz
// timerClkDivRatio = 1
// PWMFreq = (timerClkSrc / (timerClkDivRatio * (timerClkPrescale + 1) * period))
// For example, source=LFCLK, prescale=0, period = 1000, PWM frequency = 32.768 Hz
// For example, source=BUSCLK, 80MHz bus, prescale=39, period = 10000, PWM frequency = 200Hz
void PWMA0_Init(uint32_t timerClkSrc, uint32_t timerClkPrescale, uint32_t period, uint32_t duty0, uint32_t duty1){
TIMA0->GPRCM.RSTCTL = (uint32_t)0xB1000003;
TIMA0->GPRCM.PWREN = (uint32_t)0x26000001;
Clock_Delay(2); // time for TimerA0 to power up
IOMUX->SECCFG.PINCM[PB8INDEX] = 0x00000084; // TIMA0 output CCP0
IOMUX->SECCFG.PINCM[PB9INDEX] = 0x00000084; // TIMA0 output CCP1
GPIOB->DOE31_0 |= (1<<8)|(1<<9);
TIMA0->CLKSEL = timerClkSrc; // 8=BUSCLK, 4= MFCLK, 2= LFCLK clock
TIMA0->CLKDIV = 0x00; // divide by 1
TIMA0->COMMONREGS.CPS = timerClkPrescale; // divide by prescale+1,
// 32768Hz/256 = 256Hz, 7.8125
TIMA0->COUNTERREGS.LOAD = period-1; // reload register sets period
PWMA0_Period = period;
TIMA0->COUNTERREGS.CTRCTL = 0x02;
// bits 5-4 CM =0, down
// bits 3-1 REPEAT =001, continue
// bit 0 EN enable (0 for disable, 1 for enable)
TIMA0->COUNTERREGS.CCCTL_01[0] = 0; // no capture
TIMA0->COUNTERREGS.CCCTL_01[1] = 0; // no capture
// COC compare mode
TIMA0->GEN_EVENT0.IMASK = 0x00; // no interrupts
TIMA0->COMMONREGS.CCPD = 0x03; // output CCP1 CCP0
TIMA0->COMMONREGS.CCLKCTL = 1;
TIMA0->COUNTERREGS.CC_01[0] = duty0;
TIMA0->COUNTERREGS.CC_01[1] = duty1;
TIMA0->COUNTERREGS.OCTL_01[0] = 0x0000; // connected to PWM
TIMA0->COUNTERREGS.OCTL_01[1] = 0x0000; // connected to PWM
TIMA0->COUNTERREGS.CCACT_01[0] = 0x0088;
TIMA0->COUNTERREGS.CCACT_01[1] = 0x0088;
// bits 10-9 CUACT 0 for no action on up compare
// bits 7-6 CDACT 10 for make low on compare event down
// bits 4-3 LACT 01 for make high on load event
// bits 1-0 ZACT 00 for no action on zero event
TIMA0->COUNTERREGS.CTRCTL |= 0x01;
}
void PWMA0_SetDuty(uint32_t duty0, uint32_t duty1){
IOMUX->SECCFG.PINCM[PB8INDEX] = 0x00000084; // PB8 TIMA0 output CCP0 PWM low
IOMUX->SECCFG.PINCM[PB9INDEX] = 0x00000084; // PB9 TIMA0 output CCP1 PWM low
TIMA0->COUNTERREGS.CC_01[0] = duty0;
TIMA0->COUNTERREGS.CC_01[1] = duty1;
}
void PWMA0_Forward(uint32_t duty0){
IOMUX->SECCFG.PINCM[PB8INDEX] = 0x00000084; // PB8 TIMA0 output CCP0 PWM low
IOMUX->SECCFG.PINCM[PB9INDEX] = 0x00000081; // PB9 GPIO=high
GPIOB->DOUTSET31_0 = 1<<9; // PB9=1
TIMA0->COUNTERREGS.CC_01[0] = duty0;
}
void PWMA0_Backward(uint32_t duty1){
IOMUX->SECCFG.PINCM[PB8INDEX] = 0x00000081; // PB8 GPIO=high
IOMUX->SECCFG.PINCM[PB9INDEX] = 0x00000084; // PB9 TIMA0 output CCP1 PWM low
GPIOB->DOUTSET31_0 = 1<<8; // PB8=1
TIMA0->COUNTERREGS.CC_01[1] = duty1;
}
void PWMA0_Break(void){
IOMUX->SECCFG.PINCM[PB8INDEX] = 0x00000081; // PB8 GPIO=high
IOMUX->SECCFG.PINCM[PB9INDEX] = 0x00000081; // PB9 GPIO=high
GPIOB->DOUTSET31_0 = (1<<8)|(1<<9); // PB8=1,PB9=1
}
void PWMA0_Coast(void){ // will sleep after 1ms
IOMUX->SECCFG.PINCM[PB8INDEX] = 0x00000081; // PB8 GPIO=high
IOMUX->SECCFG.PINCM[PB9INDEX] = 0x00000081; // PB9 GPIO=high
GPIOB->DOUTCLR31_0 = (1<<8)|(1<<9); // PB8=0,PB9=0
}

131
RTOS_Labs_common/PWMA0.h Normal file
View File

@@ -0,0 +1,131 @@
/*!
* @defgroup PWMA0
* @brief Pulse width modulation
<table>
<caption id="PWMA0pins2">PWM pins on the MSPM0G3507</caption>
<tr><th>Pin <th>Description
<tr><td>PB8 <td>CCP0 PWM output
<tr><td>PB9 <td>CCP1 PWM output
</table>
* @{*/
/**
* @file PWMA0.h
* @brief Pulse width modulation
* @details Hardware creates fixed period, variable duty cycle waves
* \image html PWM_100Hz.png width=500px
* @version RTOS v1.0
* @author Daniel Valvano and Jonathan Valvano
* @copyright Copyright 2026 by Jonathan W. Valvano, valvano@mail.utexas.edu,
* @warning AS-IS
* @note For more information see http://users.ece.utexas.edu/~valvano/
* @date November 24, 2025
<table>
<caption id="PWMA0pins3">PWM pins on the MSPM0G3507</caption>
<tr><th>Pin <th>Description
<tr><td>PB8 <td>CCP0 PWM output
<tr><td>PB9 <td>CCP1 PWM output
</table>
******************************************************************************/
/*
* Derived from timx_timer_mode_pwm_edge_sleep_LP_MSPM0G3507_nortos_ticlang
*/
#ifndef __PWMA0_H__
#define __PWMA0_H__
/**
* \brief use PWMUSELFCLK to select LFCLK
*/
#define PWMUSELFCLK 2
/**
* \brief use PWMUSEMFCLK to select MFCLK
*/
#define PWMUSEMFCLK 4
/**
* \brief use PWMUSEBUSCLK to select bus CLK
*/
#define PWMUSEBUSCLK 8
/**
* Initialize PWMA0 outputs on PB8, PB9.
* Rising edge synchronized. timerClkDivRatio = 1.
* Once started, hardware will continuously output the waves.
* - timerClkSrc =
* - 2 for 32768 Hz LFCLK
* - 4 for 4MHz MFCLK (not tested)
* - 8 for 80/32/4 BUSCLK
* - G0/G8 is on Power domain PD0
* - 32MHz bus clock, BUSCLK clock is 32MHz
* - 40MHz bus clock, BUSCLK clock is ULPCLK 20MHz
* - 80MHz bus clock, BUSCLK clock is ULPCLK 40MHz
* - PWMFreq = (timerClkSrc / (timerClkDivRatio * (timerClkPrescale + 1) * period))
* - For example, source=LFCLK, prescale = 0, period = 1000, PWM frequency = 32.768 Hz
* - For example, source=BUSCLK, bus=40MHz, prescale=19, period = 10000, PWM frequency = 10kHz
*
* @param timerClkSrc is 2 4 or 8
* @param timerClkPrescale divide clock by timerClkPrescale+1, 0 to 255
* @param period sets the PWMA0 period
* @param duty0 sets the duty cycle on PB8
* @param duty1 sets the duty cycle on PB9
* @return none
* @note Will call LaunchPad_Init to reset and activate power
* @see PWMA0_SetDuty
* @brief Initialize PWMA0
*/
void PWMA0_Init(uint32_t timerClkSrc, uint32_t timerClkPrescale,
uint32_t period, uint32_t duty0, uint32_t duty1);
/**
* Set duty cycles on PB8, PB9.
* @param duty0 sets the duty cycle on PB8
* @param duty1 sets the duty cycle on PB9
* @return none
* @note assumes PWMA0_Init was called
* @see PWM_Init
* @brief Set duty cycles
*/
void PWMA0_SetDuty(uint32_t duty0, uint32_t duty1);
/**
* Set duty cycle on PB8, and set PB9 high.
* @param duty0 sets the duty cycle on PB8
* @return none
* @note assumes PWMA0_Init was called
* @see PWMA0_Init
* @brief Motor forward
*/
void PWMA0_Forward(uint32_t duty0);
/**
* Set duty cycle on PB9, and set PB8 high.
* @param duty1 sets the duty cycle on PB9
* @return none
* @note assumes PWMA0_Init was called
* @see PWMA0_Init
* @brief Motor backward
*/
void PWMA0_Backward(uint32_t duty1);
/**
* Set PB8 high, and set PB9 high.
* @param none
* @return none
* @note assumes PWMA0_Init was called
* @see PWMA0_Init
* @brief Motor break
*/
void PWMA0_Break(void);
/**
* Set PB8 low, and set PB9 low. Goes into sleep mode
* @param none
* @return none
* @note assumes PWMA0_Init was called
* @see PWMA0_Init
* @brief Motor coast
*/
void PWMA0_Coast(void);
#endif // __PWMA0_H__
/** @}*/

130
RTOS_Labs_common/PWMA1.c Normal file
View File

@@ -0,0 +1,130 @@
/* PWMA1.c
* Jonathan Valvano
* November 24, 2025
* Derived from timx_timer_mode_pwm_edge_sleep_LP_MSPM0G3507_nortos_ticlang
*/
/*
* Copyright (c) 2026, Texas Instruments Incorporated
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Texas Instruments Incorporated nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//#define CONFIG_MSPM0G350X
//#define SYSCONFIG_WEAK __attribute__((weak))
#include <ti/devices/msp/msp.h>
#include "../inc/LaunchPad.h"
#include "../RTOS_Labs_common/PWMA1.h"
#include "../inc/Clock.h"
// Timer A1 is on Power domain PD1
// for 32MHz bus clock, Timer clock is 32MHz
// for 40MHz bus clock, Timer clock is MCLK 40MHz
// for 80MHz bus clock, Timer clock is MCLK 80MHz
// CCP0 PWM output on PB4
// CCP1 PWM output on PB1
// PB4/TIMA1_C0;
// PB1/TIMA1_C1;
uint32_t PWMA1_Period;
// Initialize PWM outputs on PA12 PA13
// Rising edge synchronized
// timerClkSrc = 2 for 32768 Hz LFCLK
// = 4 for 4MHz MFCLK
// = 8 for 80/40/32/4 BUSCLK
// divide clock by timerClkPrescale+1, 0 to 255
// period sets the PWM period
// timerClkSrc could be 40MHz, 32MHz, 4MHz, or 32767Hz
// timerClkDivRatio = 1
// PWMFreq = (timerClkSrc / (timerClkDivRatio * (timerClkPrescale + 1) * period))
// For example, source=LFCLK, prescale=0, period = 1000, PWM frequency = 32.768 Hz
// For example, source=BUSCLK, 80MHz bus, prescale=39, period = 10000, PWM frequency = 200Hz
void PWMA1_Init(uint32_t timerClkSrc, uint32_t timerClkPrescale, uint32_t period, uint32_t duty0, uint32_t duty1){
TIMA1->GPRCM.RSTCTL = (uint32_t)0xB1000003;
TIMA1->GPRCM.PWREN = (uint32_t)0x26000001;
Clock_Delay(2); // time for TimerA1 to power up
IOMUX->SECCFG.PINCM[PB4INDEX] = 0x00000084; // TIMA1 output CCP0
IOMUX->SECCFG.PINCM[PB1INDEX] = 0x00000084; // TIMA1 output CCP1
GPIOB->DOE31_0 |= (1<<4)|(1<<1);
TIMA1->CLKSEL = timerClkSrc; // 8=BUSCLK, 4= MFCLK, 2= LFCLK clock
TIMA1->CLKDIV = 0x00; // divide by 1
TIMA1->COMMONREGS.CPS = timerClkPrescale; // divide by prescale+1,
// 32768Hz/256 = 256Hz, 7.8125
TIMA1->COUNTERREGS.LOAD = period-1; // reload register sets period
PWMA1_Period = period;
TIMA1->COUNTERREGS.CTRCTL = 0x02;
// bits 5-4 CM =0, down
// bits 3-1 REPEAT =001, continue
// bit 0 EN enable (0 for disable, 1 for enable)
TIMA1->COUNTERREGS.CCCTL_01[0] = 0; // no capture
TIMA1->COUNTERREGS.CCCTL_01[1] = 0; // no capture
// COC compare mode
TIMA1->GEN_EVENT0.IMASK = 0x00; // no interrupts
TIMA1->COMMONREGS.CCPD = 0x03; // output CCP1 CCP0
TIMA1->COMMONREGS.CCLKCTL = 1;
TIMA1->COUNTERREGS.CC_01[0] = duty0;
TIMA1->COUNTERREGS.CC_01[1] = duty1;
TIMA1->COUNTERREGS.OCTL_01[0] = 0x0000; // connected to PWM
TIMA1->COUNTERREGS.OCTL_01[1] = 0x0000; // connected to PWM
TIMA1->COUNTERREGS.CCACT_01[0] = 0x0088;
TIMA1->COUNTERREGS.CCACT_01[1] = 0x0088;
// bits 10-9 CUACT 0 for no action on up compare
// bits 7-6 CDACT 10 for make low on compare event down
// bits 4-3 LACT 01 for make high on load event
// bits 1-0 ZACT 00 for no action on zero event
TIMA1->COUNTERREGS.CTRCTL |= 0x01;
}
void PWMA1_SetDuty(uint32_t duty0, uint32_t duty1){
IOMUX->SECCFG.PINCM[PB4INDEX] = 0x00000084; // PB4 TIMA1 output CCP0 PWM low
IOMUX->SECCFG.PINCM[PB1INDEX] = 0x00000084; // PB1 TIMA1 output CCP1 PWM low
TIMA1->COUNTERREGS.CC_01[0] = duty0;
TIMA1->COUNTERREGS.CC_01[1] = duty1;
}
void PWMA1_Forward(uint32_t duty0){
IOMUX->SECCFG.PINCM[PB4INDEX] = 0x00000084; // PB4 TIMA1 output CCP0 PWM low
IOMUX->SECCFG.PINCM[PB1INDEX] = 0x00000081; // PB1 GPIO=high
GPIOB->DOUTSET31_0 = 1<<1; // PB1=1
TIMA1->COUNTERREGS.CC_01[0] = duty0;
}
void PWMA1_Backward(uint32_t duty1){
IOMUX->SECCFG.PINCM[PB4INDEX] = 0x00000081; // PB4 GPIO=high
IOMUX->SECCFG.PINCM[PB1INDEX] = 0x00000084; // PB1 TIMA1 output CCP1 PWM low
GPIOB->DOUTSET31_0 = 1<<4; // PB4=1
TIMA1->COUNTERREGS.CC_01[1] = duty1;
}
void PWMA1_Break(void){
IOMUX->SECCFG.PINCM[PB4INDEX] = 0x00000081; // PB4 GPIO=high
IOMUX->SECCFG.PINCM[PB1INDEX] = 0x00000081; // PB1 GPIO=high
GPIOB->DOUTSET31_0 = (1<<4)|(1<<1); // PB4=1,PB1=1
}
void PWMA1_Coast(void){ // will sleep after 1ms
IOMUX->SECCFG.PINCM[PB4INDEX] = 0x00000081; // PB4 GPIO=high
IOMUX->SECCFG.PINCM[PB1INDEX] = 0x00000081; // PB1 GPIO=high
GPIOB->DOUTCLR31_0 = (1<<4)|(1<<1); // PB4=0,PB1=0
}

131
RTOS_Labs_common/PWMA1.h Normal file
View File

@@ -0,0 +1,131 @@
/*!
* @defgroup PWMA1
* @brief Pulse width modulation
<table>
<caption id="PWMA1pins2">PWM pins on the MSPM0G3507</caption>
<tr><th>Pin <th>Description
<tr><td>PB4 <td>CCP0 PWM output
<tr><td>PB1 <td>CCP1 PWM output
</table>
* @{*/
/**
* @file PWMA1.h
* @brief Pulse width modulation
* @details Hardware creates fixed period, variable duty cycle waves
* \image html PWM_100Hz.png width=500px
* @version RTOS v1.0
* @author Daniel Valvano and Jonathan Valvano
* @copyright Copyright 2026 by Jonathan W. Valvano, valvano@mail.utexas.edu,
* @warning AS-IS
* @note For more information see http://users.ece.utexas.edu/~valvano/
* @date November 24, 2025
<table>
<caption id="PWMA1pins3">PWM pins on the MSPM0G3507</caption>
<tr><th>Pin <th>Description
<tr><td>PB4 <td>CCP0 PWM output
<tr><td>PB1 <td>CCP1 PWM output
</table>
******************************************************************************/
/*
* Derived from timx_timer_mode_pwm_edge_sleep_LP_MSPM0G3507_nortos_ticlang
*/
#ifndef __PWMA1_H__
#define __PWMA1_H__
/**
* \brief use PWMUSELFCLK to select LFCLK
*/
#define PWMUSELFCLK 2
/**
* \brief use PWMUSEMFCLK to select MFCLK
*/
#define PWMUSEMFCLK 4
/**
* \brief use PWMUSEBUSCLK to select bus CLK
*/
#define PWMUSEBUSCLK 8
/**
* Initialize PWMA1 outputs on PB4, PB1.
* Rising edge synchronized. timerClkDivRatio = 1.
* Once started, hardware will continuously output the waves.
* - timerClkSrc =
* - 2 for 32768 Hz LFCLK
* - 4 for 4MHz MFCLK (not tested)
* - 8 for 80/32/4 BUSCLK
* - A0/A1 is on Power domain PD1
* - 32MHz bus clock, BUSCLK clock is 32MHz
* - 40MHz bus clock, BUSCLK clock is ULPCLK 20MHz
* - 80MHz bus clock, BUSCLK clock is ULPCLK 40MHz
* - PWMFreq = (timerClkSrc / (timerClkDivRatio * (timerClkPrescale + 1) * period))
* - For example, source=LFCLK, prescale = 0, period = 1000, PWM frequency = 32.768 Hz
* - For example, source=BUSCLK, 80MHz bus, prescale=39, period = 10000, PWM frequency = 200Hz
*
* @param timerClkSrc is 2 4 or 8
* @param timerClkPrescale divide clock by timerClkPrescale+1, 0 to 255
* @param period sets the PWM period
* @param duty0 sets the duty cycle on PB4
* @param duty1 sets the duty cycle on PB1
* @return none
* @note Will call LaunchPad_Init to reset and activate power
* @see PWMA1_SetDuty
* @brief Initialize PWMA1
*/
void PWMA1_Init(uint32_t timerClkSrc, uint32_t timerClkPrescale,
uint32_t period, uint32_t duty0, uint32_t duty1);
/**
* Set duty cycles on PB4, PB1.
* @param duty0 sets the duty cycle on PB4
* @param duty1 sets the duty cycle on PB1
* @return none
* @note assumes PWMA1_Init was called
* @see PWMA1_Init
* @brief Set duty cycles
*/
void PWMA1_SetDuty(uint32_t duty0, uint32_t duty1);
/**
* Set duty cycle on PB4 (time low), and set PB1 high.
* @param duty0 sets the duty cycle on PB4
* @return none
* @note assumes PWMA1_Init was called
* @see PWMA1_Init
* @brief Motor forward
*/
void PWMA1_Forward(uint32_t duty0);
/**
* Set duty cycle on PB1 (time low), and set PB4 high.
* @param duty1 sets the duty cycle on PB1
* @return none
* @note assumes PWMA1_Init was called
* @see PWMA1_Init
* @brief Motor backward
*/
void PWMA1_Backward(uint32_t duty1);
/**
* Set PB1 high, and set PB4 high.
* @param none
* @return none
* @note assumes PWMA1_Init was called
* @see PWMA1_Init
* @brief Motor break
*/
void PWMA1_Break(void);
/**
* Set PB1 low, and set PB4 low. Goes into sleep mode
* @param none
* @return none
* @note assumes PWMA1_Init was called
* @see PWMA1_Init
* @brief Motor coast
*/
void PWMA1_Coast(void);
#endif // __PWMA1_H__
/** @}*/

99
RTOS_Labs_common/PWMG6.c Normal file
View File

@@ -0,0 +1,99 @@
/* PWMG6.c
* Jonathan Valvano
* November 24, 2025
* Derived from timx_timer_mode_pwm_edge_sleep_LP_MSPM0G3507_nortos_ticlang
*/
/*
* Copyright (c) 2026, Texas Instruments Incorporated
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Texas Instruments Incorporated nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <ti/devices/msp/msp.h>
#include "../inc/LaunchPad.h"
#include "../RTOS_Labs_common/PWMG6.h"
#include "../inc/Clock.h"
// Timer G6 is on Power domain PD1
// for 32MHz bus clock, Timer clock is 32MHz
// for 40MHz bus clock, Timer clock is MCLK 40MHz
// for 80MHz bus clock, Timer clock is MCLK 80MHz
// CCP0 PWM output on PB6
// PB6/TIMG6_C0;
uint32_t PWMG6_Period;
// Initialize PWM output on PB6
// Rising edge synchronized
// timerClkSrc = 2 for 32768 Hz LFCLK
// = 4 for 4MHz MFCLK
// = 8 for 80/40/32/4 BUSCLK
// divide clock by timerClkPrescale+1, 0 to 255
// period sets the PWM period
// timerClkSrc could be 40MHz, 32MHz, 4MHz, or 32767Hz
// timerClkDivRatio = 1
// PWMFreq = (timerClkSrc / (timerClkDivRatio * (timerClkPrescale + 1) * period))
// For example, source=LFCLK, prescale=0, period = 1000, PWM frequency = 32.768 Hz
// For example, source=BUSCLK, 80MHz bus, prescale=39, period = 10000, PWM frequency = 200Hz
void PWMG6_Init(uint32_t timerClkSrc, uint32_t timerClkPrescale, uint32_t period, uint32_t duty){
TIMG6->GPRCM.RSTCTL = (uint32_t)0xB1000003;
TIMG6->GPRCM.PWREN = (uint32_t)0x26000001;
Clock_Delay(2); // time for TimerG6 to power up
IOMUX->SECCFG.PINCM[PB6INDEX] = 0x00000087; // TIMG6 output CCP0
GPIOB->DOE31_0 |= (1<<6);
TIMG6->CLKSEL = timerClkSrc; // 8=BUSCLK, 4= MFCLK, 2= LFCLK clock
TIMG6->CLKDIV = 0x00; // divide by 1
TIMG6->COMMONREGS.CPS = timerClkPrescale; // divide by prescale+1,
// 32768Hz/256 = 256Hz, 7.8125
TIMG6->COUNTERREGS.LOAD = period-1; // reload register sets period
PWMG6_Period = period;
TIMG6->COUNTERREGS.CTRCTL = 0x02;
// bits 5-4 CM =0, down
// bits 3-1 REPEAT =001, continue
// bit 0 EN enable (0 for disable, 1 for enable)
TIMG6->COUNTERREGS.CCCTL_01[0] = 0; // no capture
// COC compare mode
TIMG6->GEN_EVENT0.IMASK = 0x00; // no interrupts
TIMG6->COMMONREGS.CCPD = 0x01; // output CCP0
TIMG6->COMMONREGS.CCLKCTL = 1;
TIMG6->COUNTERREGS.CC_01[0] = PWMG6_Period-duty;
TIMG6->COUNTERREGS.OCTL_01[0] = 0x0000; // connected to PWM
TIMG6->COUNTERREGS.CCACT_01[0] = 0x0088;
// bits 10-9 CUACT 0 for no action on up compare
// bits 7-6 CDACT 10 for make low on compare event down
// bits 4-3 LACT 01 for make high on load event
// bits 1-0 ZACT 00 for no action on zero event
TIMG6->COUNTERREGS.CTRCTL |= 0x01;
}
void PWMG6_SetDuty(uint32_t duty){
TIMG6->COUNTERREGS.CC_01[0] = PWMG6_Period-duty;
}

88
RTOS_Labs_common/PWMG6.h Normal file
View File

@@ -0,0 +1,88 @@
/*!
* @defgroup PWMG6
* @brief Pulse width modulation
<table>
<caption id="PWMG6pins2">PWM pins on the MSPM0G3507</caption>
<tr><th>Pin <th>Description
<tr><td>PB6 <td>CCP0 PWM output
</table>
* @{*/
/**
* @file PWMG6.h
* @brief Pulse width modulation
* @details Hardware creates fixed period, variable duty cycle waves
* \image html PWM_100Hz.png width=500px
* @version RTOS v1.0
* @author Daniel Valvano and Jonathan Valvano
* @copyright Copyright 2026 by Jonathan W. Valvano, valvano@mail.utexas.edu,
* @warning AS-IS
* @note For more information see http://users.ece.utexas.edu/~valvano/
* @date November 24, 2025
<table>
<caption id="PWMG6pins3">PWM pins on the MSPM0G3507</caption>
<tr><th>Pin <th>Description
<tr><td>PB6 <td>CCP0 PWM output
</table>
******************************************************************************/
/*
* Derived from timx_timer_mode_pwm_edge_sleep_LP_MSPM0G3507_nortos_ticlang
*/
#ifndef __PWMG6_H__
#define __PWMG6_H__
/**
* \brief use PWMUSELFCLK to select LFCLK
*/
#define PWMUSELFCLK 2
/**
* \brief use PWMUSEMFCLK to select MFCLK
*/
#define PWMUSEMFCLK 4
/**
* \brief use PWMUSEBUSCLK to select bus CLK
*/
#define PWMUSEBUSCLK 8
/**
* Initialize PWMG6 output on PB6.
* Rising edge synchronized. timerClkDivRatio = 1.
* Once started, hardware will continuously output the waves.
* - timerClkSrc =
* - 2 for 32768 Hz LFCLK
* - 4 for 4MHz MFCLK (not tested)
* - 8 for 80/32/4 BUSCLK
* - A0/A1 is on Power domain PD1
* - 32MHz bus clock, BUSCLK clock is 32MHz
* - 40MHz bus clock, BUSCLK clock is ULPCLK 20MHz
* - 80MHz bus clock, BUSCLK clock is ULPCLK 40MHz
* - PWMFreq = (timerClkSrc / (timerClkDivRatio * (timerClkPrescale + 1) * period))
* - For example, source=LFCLK, prescale = 0, period = 1000, PWM frequency = 32.768 Hz
* - For example, source=BUSCLK, 80MHz bus, prescale=39, period = 40000, PWM frequency = 50Hz (20ms)
*
* @param timerClkSrc is 2 4 or 8
* @param timerClkPrescale divide clock by timerClkPrescale+1, 0 to 255
* @param period sets the PWM period
* @param duty sets the duty cycle on PB6
* @return none
* @note Will call LaunchPad_Init to reset and activate power
* @see PWMG6_SetDuty
* @brief Initialize PWMG6
*/
void PWMG6_Init(uint32_t timerClkSrc, uint32_t timerClkPrescale,
uint32_t period, uint32_t duty);
/**
* Set duty cycles on PB6.
* @param duty sets the duty cycle on PB6 (high time)
* @return none
* @note assumes PWMG6_Init was called
* @see PWMG6_Init
* @brief Set duty cycles
*/
void PWMG6_SetDuty(uint32_t duty);
#endif // __PWMG6_H__
/** @}*/

View File

@@ -0,0 +1,85 @@
// RTOS_FIFO.c
// Runs on any Microcontroller
// Provide functions that initialize a FIFO, put data in, get data out,
// and return the current size. The FIFO
// uses index implementation.
// Daniel Valvano
// June 20, 2025
/*
Copyright 2025 by Jonathan W. Valvano, valvano@mail.utexas.edu
You may use, edit, run or distribute this file
as long as the above copyright notice remains
THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED
OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
VALVANO SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
For more information about my classes, my research, and my books, see
http://users.ece.utexas.edu/~valvano/
*/
#include <ti/devices/msp/msp.h>
#include "../RTOS_Labs_common/RTOS_UART.h"
#include "../RTOS_Labs_common/RTOS_FIFO.h"
#include "../RTOS_Labs_common/OS.h"
// Two-index implementation of the transmit FIFO
// can hold 0 to TXFIFOSIZE-1 elements
uint32_t volatile TxPutI; // where to put next
uint32_t volatile TxGetI; // where to get next
char static TxFifo[TXFIFOSIZE];
Sema4_t TxRoomLeft;
Sema4_t RxDataAvailable;
void TxFifo_Init(void){
TxPutI = TxGetI = 0; // empty
OS_InitSemaphore(&TxRoomLeft, TXFIFOSIZE-1);
}
int TxFifo_Put(char data){
uint32_t newPutI = (TxPutI+1)&(TXFIFOSIZE-1);
OS_Wait(&TxRoomLeft);
TxFifo[TxPutI] = data; // save in Fifo
TxPutI = newPutI; // next place to put
return 1;
}
char TxFifo_Get(void){char data;
if(TxGetI == TxPutI) return 0; // fail if empty
data = TxFifo[TxGetI]; // retrieve data
TxGetI = (TxGetI+1)&(TXFIFOSIZE-1); // next place to get
OS_Signal(&TxRoomLeft);
return data;
}
uint32_t TxFifo_Size(void){
return (TxPutI-TxGetI)&(TXFIFOSIZE-1);
}
// Two-index implementation of the receive FIFO
// can hold 0 to RXFIFOSIZE-1 elements
uint32_t volatile RxPutI; // where to put next
uint32_t volatile RxGetI; // where to get next
char static RxFifo[RXFIFOSIZE];
void RxFifo_Init(void){
RxPutI = RxGetI = 0; // empty
OS_InitSemaphore(&RxDataAvailable, 0);
}
int RxFifo_Put(char data){
uint32_t newPutI = (RxPutI+1)&(RXFIFOSIZE-1);
if(newPutI == RxGetI) return 0; // fail if full
RxFifo[RxPutI] = data; // save in Fifo
RxPutI = newPutI; // next place to put
OS_Signal(&RxDataAvailable);
return 1;
}
char RxFifo_Get(void){char data;
OS_Wait(&RxDataAvailable);
data = RxFifo[RxGetI]; // retrieve data
RxGetI = (RxGetI+1)&(RXFIFOSIZE-1); // next place to get
return data;
}
uint32_t RxFifo_Size(void){
return (RxPutI-RxGetI)&(RXFIFOSIZE-1);
}

View File

@@ -0,0 +1,159 @@
/*!
* @defgroup FIFO
* @brief First in first out queue
* @{*/
/**
* @file RTOS_FIFO.h
* @brief Provide functions for a first in first out queue
* @details Runs on any Microcontroller.
* Provide functions that initialize a FIFO, put data in, get data out,
* and return the current size. The file includes two FIFOs
* using index implementation.
* @version ECE445M v1.0
* @author Daniel Valvano and Jonathan Valvano
* @copyright Copyright 2023 by Jonathan W. Valvano, valvano@mail.utexas.edu,
* @warning AS-IS
* @note For more information see http://users.ece.utexas.edu/~valvano/
* @date June 20, 2025
*/
#ifndef __FIFO_H__
#define __FIFO_H__
/**
* \brief TXFIFOSIZE the size of the transmit FIFO, which can hold 0 to TXFIFOSIZE-1 elements.
* The size must be a power of 2.
*/
#define TXFIFOSIZE 64 // must be a power of 2
/**
* Initialize the transmit FIFO
* @param none
* @return none
* @see TxFifo_Put() TxFifo_Get() TxFifo_Size()
* @brief Initialize FIFO
* @note TXFIFOSIZE the size of the transmit FIFO
*/
void TxFifo_Init(void);
/**
* Put character into the transmit FIFO
* @param data is a new character to save
* @return 0 for fail because full, 1 for success
* @see TxFifo_Init() TxFifo_Get() TxFifo_Size()
* @brief Put FIFO
* @note TXFIFOSIZE the size of the transmit FIFO
*/
int TxFifo_Put(char data);
/**
* Get character from the transmit FIFO
* @param none
* @return 0 for fail because empty, nonzero is data
* @see TxFifo_Init() TxFifo_Put() TxFifo_Size()
* @brief Get FIFO
* @note TXFIFOSIZE the size of the transmit FIFO
*/
char TxFifo_Get(void);
/**
* Determine how many elements are currently stored in the transmit FIFO
* @param none
* @return number of elements in FIFO
* @see TxFifo_Init() TxFifo_Put() TxFifo_Get()
* @brief number of elements in FIFO
* @note Does not change the FIFO
*/
uint32_t TxFifo_Size(void);
/**
* \brief RXFIFOSIZE the size of the receive FIFO, which can hold 0 to RXFIFOSIZE-1 elements
* The size must be a power of 2.
*/
#define RXFIFOSIZE 16 // must be a power of 2
/**
* Initialize the receive FIFO
* @param none
* @return none
* @see RxFifo_Put() RxFifo_Get() RxFifo_Size()
* @brief Initialize FIFO
* @note RXFIFOSIZE the size of the receive FIFO
*/
void RxFifo_Init(void);
/**
* Put character into the receive FIFO
* @param data is a new character to save
* @return 0 for fail because full, 1 for success
* @see RxFifo_Init() RxFifo_Get() RxFifo_Size()
* @brief Put FIFO
* @note RXFIFOSIZE the size of the receive FIFO
*/
int RxFifo_Put(char data);
/**
* Get character from the receive FIFO
* @param none
* @return 0 for fail because empty, nonzero is data
* @see RxFifo_Init() RxFifo_Put() RxFifo_Size()
* @brief Get FIFO
* @note RXFIFOSIZE the size of the receive FIFO
*/
char RxFifo_Get(void);
/**
* Determine how many elements are currently stored in the receive FIFO
* @param none
* @return number of elements in FIFO
* @see RxFifo_Init() RxFifo_Put() RxFifo_Get()
* @brief number of elements in FIFO
* @note Does not change the FIFO
*/
uint32_t RxFifo_Size(void);
// macro to create an index FIFO
#define AddIndexFifo(NAME,SIZE,TYPE,SUCCESS,FAIL) \
uint32_t volatile NAME ## PutI; \
uint32_t volatile NAME ## GetI; \
TYPE static NAME ## Fifo [SIZE]; \
void NAME ## Fifo_Init(void){ \
NAME ## PutI = NAME ## GetI = 0; \
} \
int NAME ## Fifo_Put (TYPE data){ \
if(( NAME ## PutI - NAME ## GetI ) & ~(SIZE-1)){ \
return(FAIL); \
} \
NAME ## Fifo[ NAME ## PutI &(SIZE-1)] = data; \
NAME ## PutI++; \
return(SUCCESS); \
} \
int NAME ## Fifo_Get (TYPE *datapt){ \
if( NAME ## PutI == NAME ## GetI ){ \
return(FAIL); \
} \
*datapt = NAME ## Fifo[ NAME ## GetI &(SIZE-1)]; \
NAME ## GetI++; \
return(SUCCESS); \
} \
unsigned short NAME ## Fifo_Size (void){\
return ((unsigned short)( NAME ## PutI - NAME ## GetI )); \
}
// e.g.,
// AddIndexFifo(Tx,32,unsigned char, 1,0)
// SIZE must be a power of two
// creates TxFifo_Init() TxFifo_Get() and TxFifo_Put()
#endif // __FIFO_H__
/** @}*/

167
RTOS_Labs_common/RTOS_PWM.c Normal file
View File

@@ -0,0 +1,167 @@
/* RTOS_PWM.c
* Jonathan Valvano
* June 19, 2025
* Derived from timx_timer_mode_pwm_edge_sleep_LP_MSPM0G3507_nortos_ticlang
*/
/*
* Copyright (c) 2025, Texas Instruments Incorporated
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Texas Instruments Incorporated nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define CONFIG_MSPM0G350X
#define SYSCONFIG_WEAK __attribute__((weak))
#include <ti/devices/msp/msp.h>
#include "../inc/LaunchPad.h"
#include "../RTOS_Labs_common/RTOS_PWM.h"
#include "../inc/Clock.h"
// Timer A0 is on Power domain PD1
// for 32MHz bus clock, Timer clock is 32MHz
// for 40MHz bus clock, Timer clock is MCLK 40MHz
// for 80MHz bus clock, Timer clock is MCLK 80MHz
// CCP0 PWM output on PB4
// CCP1 PWM output on PB1
// PA8/TIMA0_C0;
// PA9/TIMA0_C1;
// Initialize PWM outputs on PA8 PA9
// Rising edge synchronized
// timerClkSrc = 2 for 32768 Hz LFCLK
// = 4 for 4MHz MFCLK
// = 8 for 80/40/32/4 BUSCLK
// divide clock by timerClkPrescale+1, 0 to 255
// period sets the PWM period
// timerClkSrc could be 40MHz, 32MHz, 4MHz, or 32767Hz
// timerClkDivRatio = 1
// PWMFreq = (timerClkSrc / (timerClkDivRatio * (timerClkPrescale + 1) * period))
// For example, source=LFCLK, prescale=0, period = 1000, PWM frequency = 32.768 Hz
// For example, source=BUSCLK, 80MHz bus, prescale=79, period = 10000, PWM frequency = 100Hz
void PWM0_Init(uint32_t timerClkSrc, uint32_t timerClkPrescale, uint32_t period, uint32_t duty0, uint32_t duty1){
TIMA0->GPRCM.RSTCTL = (uint32_t)0xB1000003;
TIMA0->GPRCM.PWREN = (uint32_t)0x26000001;
Clock_Delay(2); // time for TimerA0 to power up
IOMUX->SECCFG.PINCM[PA8INDEX] = 0x00000085; // TIMA0 output CCP0
IOMUX->SECCFG.PINCM[PA9INDEX] = 0x00000085; // TIMA0 output CCP1
TIMA0->CLKSEL = timerClkSrc; // 8=BUSCLK, 4= MFCLK, 2= LFCLK clock
TIMA0->CLKDIV = 0x00; // divide by 1
TIMA0->COMMONREGS.CPS = timerClkPrescale; // divide by prescale+1,
// 32768Hz/256 = 256Hz, 7.8125
TIMA0->COUNTERREGS.LOAD = period-1; // reload register sets period
// interrupts at 200kHz/period, 5us*period
TIMA0->COUNTERREGS.CTRCTL = 0x02;
// bits 5-4 CM =0, down
// bits 3-1 REPEAT =001, continue
// bit 0 EN enable (0 for disable, 1 for enable)
TIMA0->COUNTERREGS.CCCTL_01[0] = 0; // no capture
TIMA0->COUNTERREGS.CCCTL_01[1] = 0; // no capture
// COC compare mode
TIMA0->GEN_EVENT0.IMASK = 0x00; // no interrupts
TIMA0->COMMONREGS.CCPD = 0x03; // output CCP1 CCP0
TIMA0->COMMONREGS.CCLKCTL = 1;
TIMA0->COUNTERREGS.CC_01[0] = duty0;
TIMA0->COUNTERREGS.CC_01[1] = duty1;
TIMA0->COUNTERREGS.OCTL_01[0] = 0x0000; // connected to PWM
TIMA0->COUNTERREGS.OCTL_01[1] = 0x0000; // connected to PWM
TIMA0->COUNTERREGS.CCACT_01[0] = 0x0088;
TIMA0->COUNTERREGS.CCACT_01[1] = 0x0088;
// bits 10-9 CUACT 0 for no action on up compare
// bits 7-6 CDACT 10 for make low on compare event down
// bits 4-3 LACT 01 for make high on load event
// bits 1-0 ZACT 00 for no action on zero event
TIMA0->COUNTERREGS.CTRCTL |= 0x01;
}
void PWM0_SetDuty(uint32_t duty0, uint32_t duty1){
TIMA0->COUNTERREGS.CC_01[0] = duty0;
TIMA0->COUNTERREGS.CC_01[1] = duty1;
}
// Timer A1 is on Power domain PD1
// for 32MHz bus clock, Timer clock is 32MHz
// for 40MHz bus clock, Timer clock is MCLK 40MHz
// for 80MHz bus clock, Timer clock is MCLK 80MHz
// CCP0 PWM output on PB4
// CCP1 PWM output on PB1
// PB4/TIMA1_C0;
// PB1/TIMA1_C1;
// Initialize PWM outputs on PB1 PB4
// Rising edge synchronized
// timerClkSrc = 2 for 32768 Hz LFCLK
// = 4 for 4MHz MFCLK
// = 8 for 80/40/32/4 BUSCLK
// divide clock by timerClkPrescale+1, 0 to 255
// period sets the PWM period
// timerClkSrc could be 40MHz, 32MHz, 4MHz, or 32767Hz
// timerClkDivRatio = 1
// PWMFreq = (timerClkSrc / (timerClkDivRatio * (timerClkPrescale + 1) * period))
// For example, source=LFCLK, prescale=0, period = 1000, PWM frequency = 32.768 Hz
// For example, source=BUSCLK, 80MHz bus, prescale=79, period = 10000, PWM frequency = 100Hz
void PWM1_Init(uint32_t timerClkSrc, uint32_t timerClkPrescale, uint32_t period, uint32_t duty0, uint32_t duty1){
TIMA1->GPRCM.RSTCTL = (uint32_t)0xB1000003;
TIMA1->GPRCM.PWREN = (uint32_t)0x26000001;
Clock_Delay(2); // time for TimerA1 to power up
IOMUX->SECCFG.PINCM[PB4INDEX] = 0x00000084; // TIMA1 output CCP0
IOMUX->SECCFG.PINCM[PB1INDEX] = 0x00000084; // TIMA1 output CCP1
TIMA1->CLKSEL = timerClkSrc; // 8=BUSCLK, 4= MFCLK, 2= LFCLK clock
TIMA1->CLKDIV = 0x00; // divide by 1
TIMA1->COMMONREGS.CPS = timerClkPrescale; // divide by prescale+1,
// 32768Hz/256 = 256Hz, 7.8125
TIMA1->COUNTERREGS.LOAD = period-1; // reload register sets period
// interrupts at 200kHz/period, 5us*period
TIMA1->COUNTERREGS.CTRCTL = 0x02;
// bits 5-4 CM =0, down
// bits 3-1 REPEAT =001, continue
// bit 0 EN enable (0 for disable, 1 for enable)
TIMA1->COUNTERREGS.CCCTL_01[0] = 0; // no capture
TIMA1->COUNTERREGS.CCCTL_01[1] = 0; // no capture
// COC compare mode
TIMA1->GEN_EVENT0.IMASK = 0x00; // no interrupts
TIMA1->COMMONREGS.CCPD = 0x03; // output CCP1 CCP0
TIMA1->COMMONREGS.CCLKCTL = 1;
TIMA1->COUNTERREGS.CC_01[0] = duty0;
TIMA1->COUNTERREGS.CC_01[1] = duty1;
TIMA1->COUNTERREGS.OCTL_01[0] = 0x0000; // connected to PWM
TIMA1->COUNTERREGS.OCTL_01[1] = 0x0000; // connected to PWM
TIMA1->COUNTERREGS.CCACT_01[0] = 0x0088;
TIMA1->COUNTERREGS.CCACT_01[1] = 0x0088;
// bits 10-9 CUACT 0 for no action on up compare
// bits 7-6 CDACT 10 for make low on compare event down
// bits 4-3 LACT 01 for make high on load event
// bits 1-0 ZACT 00 for no action on zero event
TIMA1->COUNTERREGS.CTRCTL |= 0x01;
}
void PWM1_SetDuty(uint32_t duty0, uint32_t duty1){
TIMA1->COUNTERREGS.CC_01[0] = duty0;
TIMA1->COUNTERREGS.CC_01[1] = duty1;
}

134
RTOS_Labs_common/RTOS_PWM.h Normal file
View File

@@ -0,0 +1,134 @@
/*!
* @defgroup RTOS_PWM
* @brief Pulse width modulation
<table>
<caption id="RTOSPWMpins2">PWM pins on the MSPM0G3507</caption>
<tr><th>Pin <th>Description
<tr><td>PA8 <td>TA0_C0 PWM output
<tr><td>PA9 <td>TA0_C1 PWM output
<tr><td>PB4 <td>TA1_C0 PWM output
<tr><td>PB1 <td>TA1_C1 PWM output
</table>
* @{*/
/**
* @file RTOS_PWM.h
* @brief Pulse width modulation
* @details Hardware creates fixed period, variable duty cycle waves
* \image html PWM_100Hz.png width=500px
* @version ECE319K v1.0
* @author Daniel Valvano and Jonathan Valvano
* @copyright Copyright 2025 by Jonathan W. Valvano, valvano@mail.utexas.edu,
* @warning AS-IS
* @note For more information see http://users.ece.utexas.edu/~valvano/
* @date June 19, 2025
<table>
<caption id="RTOSPWMpins3">PWM pins on the MSPM0G3507</caption>
<tr><th>Pin <th>Description
<tr><td>PA8 <td>TA0_C0 PWM output
<tr><td>PA9 <td>TA0_C1 PWM output
<tr><td>PB4 <td>TA1_C0 PWM output
<tr><td>PB1 <td>TA1_C1 PWM output
</table>
******************************************************************************/
/*
* Derived from timx_timer_mode_pwm_edge_sleep_LP_MSPM0G3507_nortos_ticlang
*/
#ifndef __PWM1_H__
#define __PWM1_H__
/**
* \brief use PWMUSELFCLK to select LFCLK
*/
#define PWMUSELFCLK 2
/**
* \brief use PWMUSEMFCLK to select MFCLK
*/
#define PWMUSEMFCLK 4
/**
* \brief use PWMUSEBUSCLK to select bus CLK
*/
#define PWMUSEBUSCLK 8
/**
* Initialize PWM outputs on PA8 PA9.
* Rising edge synchronized. timerClkDivRatio = 1.
* Once started, hardware will continuously output the waves.
* - timerClkSrc =
* - 2 for 32768 Hz LFCLK
* - 4 for 4MHz MFCLK (not tested)
* - 8 for 80/32/4 BUSCLK
* - TimerA0 is on Power domain PD1
* - for 32MHz bus clock, Timer clock is 32MHz
* - for 40MHz bus clock, Timer clock is MCLK 40MHz
* - for 80MHz bus clock, Timer clock is MCLK 80MHz
* - PWMFreq = (timerClkSrc / (timerClkDivRatio * (timerClkPrescale + 1) * period))
* - For example, source=LFCLK, prescale = 0, period = 1000, PWM frequency = 32.768 Hz
* - For example, source=BUSCLK, 80MHz bus, prescale=79, period = 10000, PWM frequency = 100Hz
*
* @param timerClkSrc is 2 4 or 8
* @param timerClkPrescale divide clock by timerClkPrescale+1, 0 to 255
* @param period sets the PWM period
* @param duty0 sets the duty cycle on PA8
* @param duty1 sets the duty cycle on PA9
* @return none
* @note Assumes LaunchPad_Init has been called to reset and activate power
* @see PWM0_SetDuty
* @brief Initialize PWM
*/
void PWM0_Init(uint32_t timerClkSrc, uint32_t timerClkPrescale,
uint32_t period, uint32_t duty0, uint32_t duty1);
/**
* Set duty cycles on PA12 PA13.
* @param duty0 sets the duty cycle on PA8
* @param duty1 sets the duty cycle on PA9
* @return none
* @note assumes PWM_Init was called
* @see PWM_Init
* @brief Set duty cycles
*/
void PWM0_SetDuty(uint32_t duty0, uint32_t duty1);
/**
* Initialize PWM outputs on PB4 B0.
* Rising edge synchronized. timerClkDivRatio = 1.
* Once started, hardware will continuously output the waves.
* - timerClkSrc =
* - 2 for 32768 Hz LFCLK
* - 4 for 4MHz MFCLK (not tested)
* - 8 for 80/32/4 BUSCLK
* - G0/G8 is on Power domain PD0
* - 32MHz bus clock, BUSCLK clock is 32MHz
* - 40MHz bus clock, BUSCLK clock is ULPCLK 20MHz
* - 80MHz bus clock, BUSCLK clock is ULPCLK 40MHz
* - PWMFreq = (timerClkSrc / (timerClkDivRatio * (timerClkPrescale + 1) * period))
* - For example, source=LFCLK, prescale = 0, period = 1000, PWM frequency = 32.768 Hz
* - For example, source=BUSCLK, 80MHz bus, prescale=79, period = 10000, PWM frequency = 100Hz
*
* @param timerClkSrc is 2 4 or 8
* @param timerClkPrescale divide clock by timerClkPrescale+1, 0 to 255
* @param period sets the PWM period
* @param duty0 sets the duty cycle on PB4
* @param duty1 sets the duty cycle on PB1
* @return none
* @note Assumes LaunchPad_Init has been called to reset and activate power
* @see PWM1_SetDuty
* @brief Initialize PWM
*/
void PWM1_Init(uint32_t timerClkSrc, uint32_t timerClkPrescale,
uint32_t period, uint32_t duty0, uint32_t duty1);
/**
* Set duty cycles on PA12 PA13.
* @param duty0 sets the duty cycle on PB4
* @param duty1 sets the duty cycle on PB1
* @return none
* @note assumes PWM_Init was called
* @see PWM1_Init
* @brief Set duty cycles
*/
void PWM1_SetDuty(uint32_t duty0, uint32_t duty1);
#endif // __PWM_H__
/** @}*/

View File

@@ -0,0 +1,402 @@
/* RTOS_UART.c
* Jonathan Valvano
* June 11, 2025
* Derived from uart_rw_multibyte_fifo_poll_LP_MSPM0G3507_nortos_ticlang
* PA.10 UART0 Tx to XDS Rx
* PA.11 UART0 Rx from XDS Tx
* Insert jumper J25: Connects PA10 to XDS_UART
* Insert jumper J26: Connects PA11 to XDS_UART
*/
#include <ti/devices/msp/msp.h>
#include "../RTOS_Labs_common/RTOS_UART.h"
#include "file.h"
#include <stdio.h>
#include <string.h>
//------------UART_OutString------------
// Output String (NULL termination)
// Input: pointer to a NULL-terminated string to be transferred
// Output: none
void UART_OutString(char *pt){
while(*pt){
UART_OutChar(*pt);
pt++;
}
}
//------------UART_InUDec------------
// InUDec accepts ASCII input in unsigned decimal format
// and converts to a 32-bit unsigned number
// valid range is 0 to 4294967295 (2^32-1)
// Input: none
// Output: 32-bit unsigned number
// If you enter a number above 4294967295, it will return an incorrect value
// Backspace will remove last digit typed
uint32_t UART_InUDec(void){
uint32_t number=0, length=0;
char character;
character = UART_InChar();
while(character != CR){ // accepts until <enter> is typed
// The next line checks that the input is a digit, 0-9.
// If the character is not 0-9, it is ignored and not echoed
if((character>='0') && (character<='9')) {
number = 10*number+(character-'0'); // this line overflows if above 4294967295
length++;
UART_OutChar(character);
}
// If the input is a backspace, then the return number is
// changed and a backspace is outputted to the screen
else if((character==BS) && length){
number /= 10;
length--;
UART_OutChar(character);
}
character = UART_InChar();
}
return number;
}
//-----------------------UART_OutUDec-----------------------
// Output a 32-bit number in unsigned decimal format
// Input: 32-bit number to be transferred
// Output: none
// Variable format 1-10 digits with no space before or after
void UART_OutUDec(uint32_t n){
// This function uses recursion to convert decimal number
// of unspecified length as an ASCII string
if(n >= 10){
UART_OutUDec(n/10);
n = n%10;
}
UART_OutChar(n+'0'); /* n is between 0 and 9 */
}
//-----------------------UART_OutUDec3-----------------------
// Output a 32-bit number in unsigned decimal format
// Input: 32-bit number to be transferred
// Output: none
// Fixed format 3 digits with space after
void UART_OutUDec3(uint32_t n){
if(n>999){
UART_OutString("***");
}else if(n >= 100){
UART_OutChar(n/100+'0');
n = n%100;
UART_OutChar(n/10+'0');
n = n%10;
UART_OutChar(n+'0');
}else if(n >= 10){
UART_OutChar(' ');
UART_OutChar(n/10+'0');
n = n%10;
UART_OutChar(n+'0');
}else{
UART_OutChar(' ');
UART_OutChar(' ');
UART_OutChar(n+'0');
}
UART_OutChar(' ');
}
//-----------------------UART_OutUDec5-----------------------
// Output a 32-bit number in unsigned decimal format
// Input: 32-bit number to be transferred
// Output: none
// Fixed format 5 digits with space after
void UART_OutUDec5(uint32_t n){
if(n>99999){
UART_OutString("*****");
}else if(n >= 10000){
UART_OutChar(n/10000+'0');
n = n%10000;
UART_OutChar(n/1000+'0');
n = n%1000;
UART_OutChar(n/100+'0');
n = n%100;
UART_OutChar(n/10+'0');
n = n%10;
UART_OutChar(n+'0');
}else if(n >= 1000){
UART_OutChar(' ');
UART_OutChar(n/1000+'0');
n = n%1000;
UART_OutChar(n/100+'0');
n = n%100;
UART_OutChar(n/10+'0');
n = n%10;
UART_OutChar(n+'0');
}else if(n >= 100){
UART_OutChar(' ');
UART_OutChar(' ');
UART_OutChar(n/100+'0');
n = n%100;
UART_OutChar(n/10+'0');
n = n%10;
UART_OutChar(n+'0');
}else if(n >= 10){
UART_OutChar(' ');
UART_OutChar(' ');
UART_OutChar(' ');
UART_OutChar(n/10+'0');
n = n%10;
UART_OutChar(n+'0');
}else{
UART_OutChar(' ');
UART_OutChar(' ');
UART_OutChar(' ');
UART_OutChar(' ');
UART_OutChar(n+'0');
}
UART_OutChar(' ');
}
//-----------------------UART_OutSDec-----------------------
// Output a 32-bit number in signed decimal format
// Input: 32-bit number to be transferred
// Output: none
// Variable format 1-10 digits with no space before or after
void UART_OutSDec(int32_t n){
if(n<0){
UART_OutChar('-'); n = -n;
}
UART_OutUDec((uint32_t)n);
}
//---------------------UART_InUHex----------------------------------------
// Accepts ASCII input in unsigned hexadecimal (base 16) format
// Input: none
// Output: 32-bit unsigned number
// No '$' or '0x' need be entered, just the 1 to 8 hex digits
// It will convert lower case a-f to uppercase A-F
// and converts to a 16 bit unsigned number
// value range is 0 to FFFFFFFF
// If you enter a number above FFFFFFFF, it will return an incorrect value
// Backspace will remove last digit typed
uint32_t UART_InUHex(void){
uint32_t number=0, digit, length=0;
char character;
character = UART_InChar();
while(character != CR){
digit = 0x10; // assume bad
if((character>='0') && (character<='9')){
digit = character-'0';
}
else if((character>='A') && (character<='F')){
digit = (character-'A')+0xA;
}
else if((character>='a') && (character<='f')){
digit = (character-'a')+0xA;
}
// If the character is not 0-9 or A-F, it is ignored and not echoed
if(digit <= 0xF){
number = number*0x10+digit;
length++;
UART_OutChar(character);
}
// Backspace outputted and return value changed if a backspace is inputted
else if((character==BS) && length){
number /= 0x10;
length--;
UART_OutChar(character);
}
character = UART_InChar();
}
return number;
}
//--------------------------UART_OutUHex----------------------------
// Output a 32-bit number in unsigned hexadecimal format
// Input: 32-bit number to be transferred
// Output: none
// Variable format 1 to 8 digits with no space before or after
void UART_OutUHex(uint32_t number){
// This function uses recursion to convert the number of
// unspecified length as an ASCII string
if(number >= 0x10){
UART_OutUHex(number/0x10);
UART_OutUHex(number%0x10);
}
else{
if(number < 0xA){
UART_OutChar(number+'0');
}
else{
UART_OutChar((number-0x0A)+'A');
}
}
}
void OutHex(uint32_t number){
if(number < 0xA){
UART_OutChar(number+'0');
}
else{
UART_OutChar((number-0x0A)+'A');
}
}
void UART_OutUHex2(uint32_t number){
UART_OutString(" 0x");
OutHex(number/0x10);
OutHex(number%0x10);
}
//------------UART_InString------------
// Accepts ASCII characters from the serial port
// and adds them to a string until <enter> is typed
// or until max length of the string is reached.
// It echoes each character as it is inputted.
// If a backspace is inputted, the string is modified
// and the backspace is echoed
// terminates the string with a null character
// uses interrupt synchronization on
// Input: pointer to empty buffer, size of buffer
// Output: Null terminated string
// -- Modified by Agustinus Darmawan + Mingjie Qiu --
void UART_InString(char *bufPt, uint16_t max) {
int length=0;
char character;
character = UART_InChar();
while(character != CR){
if(character == BS){
if(length){
bufPt--;
length--;
UART_OutChar(BS);
}
}
else if(length < max){
*bufPt = character;
bufPt++;
length++;
UART_OutChar(character);
}
character = UART_InChar();
}
*bufPt = 0;
}
/****************Fixed_Fix2Str***************
converts fixed point number to ASCII string
format signed 16-bit with resolution 0.01
range -327.67 to +327.67
Input: signed 16-bit integer part of fixed point number
-32768 means invalid fixed-point number
Output: null-terminated string exactly 8 characters plus null
Examples
12345 to " 123.45"
-22100 to "-221.00"
-102 to " -1.02"
31 to " 0.31"
-32768 to " ***.**"
*/
void Fixed_Fix2Str(long const num,char *string){
short n;
if((num>99999)||(num<-99990)){
strcpy((char *)string," ***.**");
return;
}
if(num<0){
n = -num;
string[0] = '-';
} else{
n = num;
string[0] = ' ';
}
if(n>9999){
string[1] = '0'+n/10000;
n = n%10000;
string[2] = '0'+n/1000;
} else{
if(n>999){
if(num<0){
string[0] = ' ';
string[1] = '-';
} else {
string[1] = ' ';
}
string[2] = '0'+n/1000;
} else{
if(num<0){
string[0] = ' ';
string[1] = ' ';
string[2] = '-';
} else {
string[1] = ' ';
string[2] = ' ';
}
}
}
n = n%1000;
string[3] = '0'+n/100;
n = n%100;
string[4] = '.';
string[5] = '0'+n/10;
n = n%10;
string[6] = '0'+n;
string[7] = 0;
}
//--------------------------UART_Fix2----------------------------
// Output a 32-bit number in 0.01 fixed-point format
// Input: 32-bit number to be transferred -99999 to +99999
// Output: none
// Fixed format
// 12345 to " 123.45"
// -22100 to "-221.00"
// -102 to " -1.02"
// 31 to " 0.31"
// error " ***.**"
void UART_Fix2(long number){
char message[10];
Fixed_Fix2Str(number,message);
UART_OutString(message);
}
int uart_open(const char *path, unsigned flags, int llv_fd){
UART_Init(1);
return 0;
}
int uart_close( int dev_fd){
return 0;
}
int uart_read(int dev_fd, char *buf, unsigned count){char ch;
ch = UART_InChar(); // receive from keyboard
ch = *buf; // return by reference
UART_OutChar(ch); // echo
return 1;
}
int uart_write(int dev_fd, const char *buf, unsigned count){ unsigned int num=count;
while(num){
UART_OutChar(*buf);
buf++;
num--;
}
return count;
}
off_t uart_lseek(int dev_fd, off_t ioffset, int origin){
return 0;
}
int uart_unlink(const char * path){
return 0;
}
int uart_rename(const char *old_name, const char *new_name){
return 0;
}
//------------UART_InitPrintf------------
// Initialize the UART for 115,200 baud rate (assuming 32 40 or 80 MHz bus clock),
// 8 bit word length, no parity bits, one stop bit
// Input: none
// Output: none
void UART_InitPrintf(void){int ret_val; FILE *fptr;
UART_Init(1);
ret_val = add_device("uart", _SSA, uart_open, uart_close, uart_read, uart_write, uart_lseek, uart_unlink, uart_rename);
if(ret_val) return; // error
fptr = fopen("uart","w");
if(fptr == 0) return; // error
freopen("uart:", "w", stdout); // redirect stdout to uart
setvbuf(stdout, NULL, _IONBF, 0); // turn off buffering for stdout
}

View File

@@ -0,0 +1,241 @@
/*!
* @defgroup UART
* @brief Asynchronous serial communication
<table>
<caption id="UARTpins">UART pins on the MSPM0G3507</caption>
<tr><th>Pin <th>Description
<tr><td>PA10 <td>UART0 Tx to XDS Rx
<tr><td>PA11 <td>UART0 Rx from XDS Tx
</table>
* @{*/
/**
* @file UART.h
* @brief Initialize UART0
* @details UART0 initialization. 115200 baud,
* 1 start, 8 data bits, 1 stop, no parity.<br>
* @version ECE319K v1.0
* @author Daniel Valvano and Jonathan Valvano
* @copyright Copyright 2023 by Jonathan W. Valvano, valvano@mail.utexas.edu,
* @warning AS-IS
* @note For more information see http://users.ece.utexas.edu/~valvano/
* @date August 13, 2023
<table>
<caption id="UARTpins2">UART pins on the MSPM0G3507</caption>
<tr><th>Pin <th>Description
<tr><td>PA10 <td>UART0 Tx to XDS Rx
<tr><td>PA11 <td>UART0 Rx from XDS Tx
</table>
******************************************************************************/
#ifndef __UART_H__
#define __UART_H__
// standard ASCII symbols
/**
* \brief CR is carriage return
*/
#define CR 0x0D
/**
* \brief LF is line feed
*/
#define LF 0x0A
/**
* \brief BS is back space
*/
#define BS 0x08
/**
* \brief ESC is escape character
*/
#define ESC 0x1B
/**
* \brief SP is space
*/
#define SP 0x20
/**
* \brief DEL is delete
*/
#define DEL 0x7F
/*
* Derived from uart_rw_multibyte_fifo_poll_LP_MSPM0G3507_nortos_ticlang
*/
/**
* initialize 0 for 115200 baud rate.
* - PA10 = UART0 Tx to XDS Rx
* - PA11 = UART0 Rx from XDS Tx
*
* There are two implementations:
* - UART_Init in <b>UARTbusywait.c</b> implements busy-wait synchronization
* - UART_Init in <b>UARTints.c</b> implements interrupt synchronization
*
* @param priority 0 for highest, 3 for lowest
* @return none
* @brief Initialize UART0
*/
void UART_Init(uint32_t priority);
/**
* Wait for new serial port input
* @param none
* @return char ASCII code for key typed
* @brief input from UART0
*/
char UART_InChar(void);
/**
* Output 8-bit to serial port
* @param data is an 8-bit ASCII character to be transferred
* @return none
* @brief output character to UART0
*/
void UART_OutChar(char data);
/**
* Output String with NULL termination
* @param pt is pointer to a NULL-terminated string to be transferred
* @return none
* @brief output string to UART0
*/
void UART_OutString(char *pt);
/**
* InUDec accepts ASCII input in unsigned decimal format
* and converts to a 32-bit unsigned number
* valid range is 0 to 4294967295 (2^32-1)
* @param none
* @return 32-bit unsigned number
* @note If you enter a number above 4294967295, it will return an incorrect value
* Backspace will remove last digit typed
* @brief input a number from UART0
*/
uint32_t UART_InUDec(void);
/**
* Output a 32-bit number in unsigned decimal format
* @param n 32-bit number to be transferred
* @return none
* @note Variable format 1-10 digits with no space before or after
* @brief output a number to UART0
*/
void UART_OutUDec(uint32_t n);
/**
* Output a 32-bit number in unsigned decimal format
* @param n 32-bit number to be transferred
* @return none
* @note fixed format 3 digits with no space before or after
* @brief output a number to UART0
*/
void UART_OutUDec3(uint32_t n);
/**
* Output a 32-bit number in unsigned decimal format
* @param n 32-bit number to be transferred
* @return none
* @note Variable fixed format 5 digits with no space before or after
* @brief output a number to UART0
*/
void UART_OutUDec5(uint32_t n);
/**
* Output a 32-bit number in signed decimal format
* @param n 32-bit number to be transferred
* @return none
* @note Variable format 1-10 digits with no space before or after
* @brief output a signed number to UART0
*/
void UART_OutSDec(int32_t n);
/**
* Accepts ASCII input in unsigned hexadecimal (base 16) format
* No '$' or '0x' need be entered, just the 1 to 8 hex digits
* It will convert lower case a-f to uppercase A-F
* and converts to a 16 bit unsigned number
* value range is 0 to FFFFFFFF
* If you enter a number above FFFFFFFF, it will return an incorrect value
* Backspace will remove last digit typed
* @param none
* @return 32-bit unsigned number
* @brief input a hex number from UART0
*/
uint32_t UART_InUHex(void);
/**
* Output a 32-bit number in unsigned hexadecimal format
* @param number 32-bit number to be transferred
* @return none
* @note Variable format 1 to 8 digits with no space before or after
* @brief output a hex number to UART0
*/
void UART_OutUHex(uint32_t number);
/**
* Output an 8-bit number in unsigned hexadecimal format
* @param number 32-bit number to be transferred
* @return none
* @note Fixed format, 5 characters e.g., " 0x1A"
* @brief output a 2-digit hex number to UART0
*/
void UART_OutUHex2(uint32_t number);
/**
* Accepts ASCII characters from the serial port
* and adds them to a string until <enter> is typed
* or until max length of the string is reached.
* It echoes each character as it is inputted.
* If a backspace is inputted, the string is modified
* and the backspace is echoed
* terminates the string with a null character
* Calls UART_InChar
* @param bufPt is a pointer to empty buffer,
* @param max is the size of the buffer
* @return none
* @note Modified by Agustinus Darmawan + Mingjie Qiu --
* @brief input a string from UART0
*/
void UART_InString(char *bufPt, uint16_t max);
/**
* Output a 32-bit number in 0.01 fixed-point format
* Fixed format <br>
* 12345 to " 123.45" <br>
* -22100 to "-221.00" <br>
* -102 to " -1.02" <br>
* 31 to " 0.31" <br>
* error " ***.**"
* @param number32-bit number to be transferred -99999 to +99999
* @return none
* @brief output a fixed-point number to UART0
*/
void UART_Fix2(long number);
/**
* Initialize the UART for 115,200 baud rate (assuming 48 MHz bus clock),
* 8 bit word length, no parity bits, one stop bit.
* Calls UART_Init()
* @param none
* @return none
* @brief Initialize UART0 to use printf
*/
void UART_InitPrintf(void);
// initialize UART1 for 115200 baud rate
void UART1_Init(void);
//------------UART1_InChar------------
// Wait for new serial port input
// Input: none
// Output: ASCII code for key typed
char UART1_InChar(void);
//------------UART1_OutChar------------
// Output 8-bit to serial port
// Input: letter is an 8-bit ASCII character to be transferred
// Output: none
void UART1_OutChar(char data);
#endif // __UART_H__
/** @}*/

View File

@@ -0,0 +1,160 @@
/* RTOS_UARTints.c
* Jonathan Valvano
* June 10, 2025
* Derived from uart_rw_multibyte_fifo_poll_LP_MSPM0G3507_nortos_ticlang
* uart_echo_interrupts_standby_LP_MSPM0G3507_nortos_ticlang
* PA.10 UART0 Tx to XDS Rx
* PA.11 UART0 Rx from XDS Tx
* Insert jumper J25: Connects PA10 to XDS_UART
* Insert jumper J26: Connects PA11 to XDS_UART
*/
#include <ti/devices/msp/msp.h>
#include "../RTOS_Labs_common/RTOS_UART.h"
#include "../inc/Clock.h"
#include "../RTOS_Labs_common/RTOS_FIFO.h"
#include "../RTOS_Labs_common/LaunchPad.h"
// power Domain PD0
// for 32MHz bus clock, bus clock is 32MHz
// for 40MHz bus clock, bus clock is ULPCLK 20MHz
// for 80MHz bus clock, bus clock is ULPCLK 40MHz
// assume 32 40 or 80 MHz bus clock
// initialize UART for 115200 baud rate
// priority 0 for highest, 3 for lowest
// interrupt synchronization
void UART_Init(uint32_t priority){
// RSTCLR to GPIOA and UART0 peripherals
// bits 31-24 unlock key 0xB1
// bit 1 is Clear reset sticky bit
// bit 0 is reset gpio port
// assumes GPIOA has been reset and enabled in LaunchPad_Init
UART0->GPRCM.RSTCTL = 0xB1000003;
// Enable power to GPIOA and UART0 peripherals
// PWREN
// bits 31-24 unlock key 0x26
// bit 0 is Enable Power
UART0->GPRCM.PWREN = 0x26000001;
Clock_Delay(24); // time for uart to power up
// configure PA11 PA10 as alternate UART0 function
IOMUX->SECCFG.PINCM[PA10INDEX] = 0x00000082;
//bit 7 PC connected
//bits 5-0=2 for UART0_Tx
IOMUX->SECCFG.PINCM[PA11INDEX] = 0x00040082;
//bit 18 INENA input enable
//bit 7 PC connected
//bits 5-0=2 for UART0_Rx
TxFifo_Init();
RxFifo_Init();
UART0->CLKSEL = 0x08; // bus clock
UART0->CLKDIV = 0x00; // no divide
UART0->CTL0 &= ~0x01; // disable UART0
UART0->CTL0 = 0x00020018;
// bit 17 FEN=1 enable FIFO
// bits 16-15 HSE=00 16x oversampling
// bit 14 CTSEN=0 no CTS hardware
// bit 13 RTSEN=0 no RTS hardware
// bit 12 RTS=0 not RTS
// bits 10-8 MODE=000 normal
// bits 6-4 TXE=001 enable TxD
// bit 3 RXE=1 enable TxD
// bit 2 LBE=0 no loop back
// bit 0 ENABLE 0 is disable, 1 to enable
if(Clock_Freq() == 40000000){
// 20000000/16 = 1,250,000 Hz
// Baud = 115200
// 1,250,000/115200 = 10.850694
// divider = 10+54/64 = 10.84375
UART0->IBRD = 10;
UART0->FBRD = 54; // baud =1,250,000/10.84375 = 115,274
}else if (Clock_Freq() == 32000000){
// 32000000/16 = 2,000,000
// Baud = 115200
// 2,000,000/115200 = 17.361
// divider = 17+23/64 = 17.359
UART0->IBRD = 17;
UART0->FBRD = 23;
}else if (Clock_Freq() == 80000000){
// 40000000/16 = 2,500,000 Hz
// Baud = 115200
// 2,500,000/115200 = 21.701388
// divider = 21+45/64 = 21.703125
UART0->IBRD = 21;
UART0->FBRD = 45; // baud =2,500,000/21.703125 = 115,191
}else return;
UART0->LCRH = 0x00000030;
// bits 5-4 WLEN=11 8 bits
// bit 3 STP2=0 1 stop
// bit 2 EPS=0 parity select
// bit 1 PEN=0 no parity
// bit 0 BRK=0 no break
UART0->CPU_INT.IMASK = 0x0C01;
// bit 11 TXINT
// bit 10 RXINT
// bit 0 Receive timeout
UART0->IFLS = 0x0422;
// bits 11-8 RXTOSEL receiver timeout select 4 (0xF highest)
// bits 6-4 RXIFLSEL 2 is greater than or equal to half
// bits 2-0 TXIFLSEL 2 is less than or equal to half
NVIC->ICPR[0] = 1<<15; // UART0 is IRQ 15
NVIC->ISER[0] = 1<<15;
NVIC->IP[3] = (NVIC->IP[3]&(~0xFF000000))|(priority<<30); // set priority (bits 31,30) IRQ 15
UART0->CTL0 |= 0x01; // enable UART0
}
// copy from hardware RX FIFO to software RX FIFO
// stop when hardware RX FIFO is empty or discard data if software RX FIFO is full
void static copyHardwareToSoftware(void){
char letter;
while((UART0->STAT&0x04) == 0){
// while(((UART0->STAT&0x04) == 0) && (RxFifo_Size() < (RxSIZE - 1))){
letter = UART0->RXDATA;
RxFifo_Put(letter);
}
}
//------------UART_InChar------------
// Wait for new serial port input
// Input: none
// Output: ASCII code for key typed
char UART_InChar(void){
char letter;
letter = RxFifo_Get();
return(letter);
}
// copy from software TX FIFO to hardware TX FIFO
// stop when software TX FIFO is empty or hardware TX FIFO is full
void static copySoftwareToHardware(void){
char letter;
while(((UART0->STAT&0x80) == 0) && (TxFifo_Size() > 0)){
letter = TxFifo_Get();
UART0->TXDATA = letter;
}
}
//------------UART_OutChar------------
// Output 8-bit to serial port
// Input: letter is an 8-bit ASCII character to be transferred
// Output: none
void UART_OutChar(char data){
TxFifo_Put(data);
UART0->CPU_INT.IMASK &= ~0x0800; // disarm TX FIFO interrupt
copySoftwareToHardware();
UART0->CPU_INT.IMASK |= 0x0800; // rearm TX FIFO interrupt
}
void UART0_IRQHandler(void){ uint32_t status;
status = UART0->CPU_INT.IIDX; // reading clears bit in RIS
if(status == 0x01){ // 0x01 receive timeout
copyHardwareToSoftware();
}else if(status == 0x0B){ // 0x0B receive
copyHardwareToSoftware();
}else if(status == 0x0C){ // 0x0C transmit
copySoftwareToHardware();
if(TxFifo_Size() == 0){ // software TX FIFO is empty
UART0->CPU_INT.IMASK &= ~0x0800; // disable TX FIFO interrupt
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 KiB

280
RTOS_Labs_common/SPI.c Normal file
View File

@@ -0,0 +1,280 @@
/* SPI.c
* Jonathan Valvano
* December 27, 2025
* Derived from uart_rw_multibyte_fifo_poll_LP_MSPM0G3507_nortos_ticlang
*/
// hardware connections
// **********ST7735 TFT and SDC*******************
// ST7735
// Backlight (pin 10) to +3.3 V
// MISO (pin 9) from SPI1 POCI PB7
// SCK (pin 8) to SPI1 SCLK: PB9
// MOSI (pin 7) to SPI1 PICO: PB8
// TFT_CS (pin 6) to LCD, GPIO: PB6
// CARD_CS (pin 5) to SDC, GPIO PB0
// Data/Command (pin 4) to PB16 (GPIO), high for data, low for command
// RESET (pin 3) to PB15 (GPIO)
// VCC (pin 2) to +3.3 V
// Gnd (pin 1) to ground
// **********HiLetgo ST7735 TFT and SDC *******************
// ST7735
// LED- (pin 16) TFT, to ground
// LED+ (pin 15) TFT, to +3.3 V
// SD_CS (pin 14) SDC, to chip select, GPIO PB0
// MOSI (pin 13) SDC, to MOSI PB8 MOSI SPI1 PICO
// MISO (pin 12) SDC, to MISO from SPI1 POCI PB7
// SCK (pin 11) SDC, to serial clock
// CS (pin 10) TFT, // TFT_CS to LCD, GPIO: PB6
// SCL (pin 9) TFT, to PB9 SPI1 SCLK
// SDA (pin 8) TFT, to PB8 MOSI SPI1 PICO
// A0 (pin 7) TFT, to PB16 Data/Command, high for data, low for command
// RESET (pin 6) TFT, to PB15 reset (GPIO), low to reset
// NC (pins 3,4,5)
// VCC (pin 2) to +3.3 V
// GND (pin 1) to ground
#include <ti/devices/msp/msp.h>
#include "../RTOS_Labs_common/SPI.h"
#include "../inc/Clock.h"
#include "../inc/Timer.h"
#include "../inc/LaunchPad.h"
/*
// calls Clock_Freq to get bus clock
// initialize SPI for 8 MHz baud clock
// busy-wait synchronization
// SPI0,SPI1 in power domain PD1 SysClk equals bus CPU clock
void SPI_Init(void){uint32_t busfreq = Clock_Freq();
// assumes GPIOA and GPIOB are reset and powered previously
// RSTCLR to SPI1 peripherals
// bits 31-24 unlock key 0xB1
// bit 1 is Clear reset sticky bit
// bit 0 is reset gpio port
SPI1->GPRCM.RSTCTL = 0xB1000003;
// Enable power to SPI1 peripherals
// PWREN
// bits 31-24 unlock key 0x26
// bit 0 is Enable Power
SPI1->GPRCM.PWREN = 0x26000001;
// configure PB9 PB6 PB8 as alternate SPI1 function
IOMUX->SECCFG.PINCM[PB9INDEX] = 0x00000083; // SPI1 SCLK
IOMUX->SECCFG.PINCM[PB6INDEX] = 0x00000083; // SPI1 CS0
IOMUX->SECCFG.PINCM[PB8INDEX] = 0x00000083; // SPI1 PICO
IOMUX->SECCFG.PINCM[PB15INDEX] = 0x00000081; // GPIO output, LCD !RST
IOMUX->SECCFG.PINCM[PB16INDEX] = 0x00000081; // GPIO output, LCD RS
// ** IOMUX->SECCFG.PINCM[PA13INDEX] = 0x00000081; // GPIO output, LCD RS
Clock_Delay(24); // time for gpio to power up
// ** GPIOA->DOE31_0 |= 1<<13; // PA13 is LCD RS
GPIOB->DOE31_0 |= 1<<16; // PA13 is LCD RS
GPIOB->DOE31_0 |= 1<<15; // PB15 is LCD !RST
GPIOB->DOUTSET31_0 = 1<<16; // RS=1
// ** GPIOA->DOUTSET31_0 = 1<<13; // RS=1
GPIOB->DOUTSET31_0 = 1<<15; // !RST = 1
SPI1->CLKSEL = 8; // SYSCLK
// bit 3 SYSCLK
// bit 2 MFCLK
// bit 1 LFCLK
SPI1->CLKDIV = 0; // divide by 1
// bits 2-0 n (0 to 7), divide by n+1
//Set the bit rate clock divider to generate the serial output clock
// outputBitRate = (spiInputClock) / ((1 + SCR) * 2)
// 8,000,000 = (16,000,000)/((0 + 1) * 2)
// 8,000,000 = (32,000,000)/((1 + 1) * 2)
// 6,666,667 = (40,000,000)/((2 + 1) * 2)
// 10,000,000 = (40,000,000)/((1 + 1) * 2)
// 8,000,000 = (80,000,000)/((4 + 1) * 2)
// 8,000,000 = (Clock_Freq)/((m + 1) * 2)
// m = (Clock_Freq/16000000) - 1
if(busfreq <= 16000000){
SPI1->CLKCTL = 0; // frequency= busfreq/2
}else if(busfreq == 40000000){
SPI1->CLKCTL = 1; // frequency= 10MHz
// SPI1->CLKCTL = 2; // frequency= 6.66MHz
}else{
SPI1->CLKCTL = busfreq/16000000 -1; // 8 MHz
}
SPI1->CTL0 = 0x0027;
// bit 14 CSCLR=0 not cleared
// bits 13-12 CSSEL=0 CS0
// bit 9 SPH = 0
// bit 8 SPO = 0
// bits 6-5 FRF = 01 (4 wire)
// bits 4-0 n=7, data size is n+1 (8bit data)
SPI1->CTL1 = 0x0015;
// bits 29-24 RXTIMEOUT=0
// bits 23-16 REPEATX=0 disabled
// bits 15-12 CDMODE=0 manual
// bit 11 CDENABLE=0 CS3
// bit 7-5 =0 no parity
// bit 4=1 MSB first
// bit 3=0 POD (not used, not peripheral)
// bit 2=1 CP controller mode
// bit 1=0 LBM disable loop back
// bit 0=1 enable SPI
SPI_Reset();
}
//---------SPI_OutData------------
// Output 8-bit data to SPI port
// Input: data is an 8-bit data to be transferred
// Output: none
void SPI_OutData(char data){
while((SPI1->STAT&0x02) == 0x00){}; // spin if TxFifo full
GPIOA->DOUTSET31_0 = 1<<13; // RS=PA13=1 for data
SPI1->TXDATA = data;
}
//---------SPI_OutCommand------------
// Output 8-bit command to SPI port
// Input: data is an 8-bit data to be transferred
// Output: none
void SPI_OutCommand(char command){
while((SPI1->STAT&0x10) == 0x10){}; // spin if SPI busy
GPIOA->DOUTCLR31_0 = 1<<13; // RS=PA13=0 for command
SPI1->TXDATA = command;
while((SPI1->STAT&0x10) == 0x10){}; // spin if SPI busy
}
*/
// SDC CS initialization
void CS_Init(void){
IOMUX->SECCFG.PINCM[SDC_CS_INDEX] = (uint32_t) 0x00000081;
SDC_CS->DOE31_0 |= SDC_CS_PIN;
SDC_CS_HIGH(); // PB0=1
}
//---------SPI1_Reset------------
// Reset LCD
// Input: none
// Output: none
void SPI1_Reset(void){
TFT_RST_HIGH(); // PB15=!RST=1
Clock_Delay1ms(500); // 500ms (calibrated with logic analyzer)
TFT_RST_LOW(); // PB15=!RST=0
Clock_Delay1ms(500); // 500ms
TFT_RST_HIGH(); // PB15=!RST=1
Clock_Delay1ms(500); // 500ms
}
//********SPI1_Init*****************
// Initialize SPI1 interface to SDC and TFT
// inputs: useSDC, 1 for enabling SDC features
// outputs: none
// outputBitRate = (spiInputClock) / ((1 + SCR) * 2)
// 99 for 400,000 bps slow mode, used during initialization
// 4 for 8,000,000 bps fast mode, used during disk I/O
// Version for both SDC and TFT
int SPI1_init_Flag=0;
void SPI1_Init(void){
if(SPI1_init_Flag) return;
CS_Init();
uint32_t busfreq = Clock_Freq();
// assumes GPIOA and GPIOB are reset and powered previously
// RSTCLR to SPI1 peripherals
// bits 31-24 unlock key 0xB1
// bit 1 is Clear reset sticky bit
// bit 0 is reset gpio port
SPI1->GPRCM.RSTCTL = 0xB1000003;
// Enable power to SPI1 peripherals
// PWREN
// bits 31-24 unlock key 0x26
// bit 0 is Enable Power
SPI1->GPRCM.PWREN = 0x26000001;
// configure PB9 PB6 PB8 as alternate SPI1 function
IOMUX->SECCFG.PINCM[PB9INDEX] = 0x00000083; // SPI1 SCLK
IOMUX->SECCFG.PINCM[PB8INDEX] = 0x00000083; // SPI1 PICO
IOMUX->SECCFG.PINCM[TFT_RST_INDEX] = 0x00000081; // GPIO output, LCD !RST
IOMUX->SECCFG.PINCM[TFT_DC_INDEX] = 0x00000081; // GPIO output, LCD D/C RS
IOMUX->SECCFG.PINCM[PB7INDEX] = 0x00040083; // SPI1 POCI
IOMUX->SECCFG.PINCM[TFT_CS_INDEX] = 0x00000081; // PB6 is regular GPIO
Clock_Delay(24); // time for gpio to power up
TFT_DC->DOE31_0 |= TFT_DC_PIN; // PB16 is LCD RS
TFT_DC_HIGH();
TFT_RST->DOE31_0 |= TFT_RST_PIN;
TFT_RST_HIGH();
TFT_CS->DOE31_0 |= TFT_CS_PIN; // PB6 is regular GPIO
TFT_CS_HIGH(); // disable LCD
SPI1->CLKSEL = 8; // SYSCLK
// bit 3 SYSCLK
// bit 2 MFCLK
// bit 1 LFCLK
SPI1->CLKDIV = 0; // divide by 1
// bits 2-0 n (0 to 7), divide by n+1
//Set the bit rate clock divider to generate the serial output clock
// outputBitRate = (spiInputClock) / ((1 + SCR) * 2)
// 8,000,000 = (16,000,000)/((0 + 1) * 2)
// 8,000,000 = (32,000,000)/((1 + 1) * 2)
// 6,666,667 = (40,000,000)/((2 + 1) * 2)
// 10,000,000 = (40,000,000)/((1 + 1) * 2)
// 8,000,000 = (80,000,000)/((4 + 1) * 2)
// 8,000,000 = (Clock_Freq)/((m + 1) * 2)
// m = (Clock_Freq/16000000) - 1
if(busfreq <= 16000000){
SPI1->CLKCTL = 0; // frequency= busfreq/2
}else if(busfreq == 40000000){
SPI1->CLKCTL = 1; // frequency= 10MHz
// SPI1->CLKCTL = 2; // frequency= 6.66MHz
}else{
SPI1->CLKCTL = busfreq/16000000 -1; // 8 MHz
}
SPI1->CTL0 = 0x0007;
// bit 14 CSCLR=0 not cleared
// bits 13-12 CSSEL=0 CS0
// bit 9 SPH = 0
// bit 8 SPO = 0
// bits 6-5 FRF = 00 (3 wire)
// bits 4-0 n=7, data size is n+1 (8bit data)
SPI1->CTL1 = 0x0015;
// bits 29-24 RXTIMEOUT=0
// bits 23-16 REPEATX=0 disabled
// bits 15-12 CDMODE=0 manual
// bit 11 CDENABLE=0 CS3
// bit 7-5 =0 no parity
// bit 4=1 MSB first
// bit 3=0 POD (not used, not peripheral)
// bit 2=1 CP controller mode
// bit 1=0 LBM disable loop back
// bit 0=1 enable SPI
SPI1_init_Flag = 1;
SPI1_Reset();
}
/* STAT register
* 4 BUSY 0h = SPI is in idle mode. 1h = SPI is currently transmitting and/or receiving data, or transmit FIFO is not empty.
3 RNF Receive FIFO not full 0h = Receive FIFO is full. 1h = Receive FIFO is not full.
2 RFE Receive FIFO empty. 0h = Receive FIFO is not empty. 1h = Receive FIFO is empty.
1 TNF Transmit FIFO not full 0h = Transmit FIFO is full. 1h = Transmit FIFO is not full.
0 TFE Transmit FIFO empty. 0h = Transmit FIFO is not empty. 1h = Transmit FIFO is empty.*/
//---------TFT_OutData------------
// Output 8-bit data to SPI port
// Input: data is an 8-bit data to be transferred
// Output: none
void TFT_OutData(char data){char response;
while((SPI1->STAT&0x10) == 0x10){}; // spin if SPI busy
SDC_CS_HIGH(); // PB0 high, disable SDC
TFT_CS_LOW(); // PB6 low, enable TFT
TFT_DC_HIGH(); // RS=PB16=1 for data
SPI1->TXDATA = data;
while((SPI1->STAT&0x04) == 0x04){}; // spin SPI RxFifo empty
response = SPI1->RXDATA; // has no meaning, flush
TFT_CS_HIGH(); // PB6 high
}
//---------TFT_OutCommand------------
// Output 8-bit command to SPI port
// Input: data is an 8-bit data to be transferred
// Output: none
void TFT_OutCommand(char command){char response;
while((SPI1->STAT&0x10) == 0x10){}; // spin if SPI busy
SDC_CS_HIGH(); // PB0 high, disable SDC
TFT_CS_LOW(); // PB6 low, enable TFT
TFT_DC_LOW(); // RS=PB16=0 for command
SPI1->TXDATA = command;
while((SPI1->STAT&0x10) == 0x10){}; // spin if SPI busy
response = SPI1->RXDATA; // has no meaning, flush
TFT_CS_HIGH(); // PB6 high
}

217
RTOS_Labs_common/SPI.h Normal file
View File

@@ -0,0 +1,217 @@
/*!
* @defgroup SPI
* @brief Synchronous serial communication
<table>
<caption id="AdafruitLCDpins8">Adafruit ST7735R pins </caption>
<tr><th>Pin <th>Connection <th>Description
<tr><td>10<td>+3.3<td>Backlight
<tr><td>9 <td>PB7 <td>SPI1 MISO (used for SDC)
<tr><td>8 <td>PB9 <td>SPI1 SCLK clock out
<tr><td>7 <td>PB8 <td>SPI1 PICO data out
<tr><td>6 <td>PB6 <td>GPIO CS0=TFT_CS
<tr><td>5 <td>PB0 <td>CARD_CS (used for SDC)
<tr><td>4 <td>PB16<td>Data/Command(GPIO), high for data, low for command
<tr><td>3 <td>PB15<td>RESET, low to reset, (GPIO)
<tr><td>2 <td>+3.3<td>VCC
<tr><td>1 <td>Gnd <td>ground
</table>
<table>
<caption id="HiLetgopins8">HiLetgo ST7735 TFT and SDC pins </caption>
<tr><th>signal<th>Pin<th>Connection
<tr><td>LED- <td>16<td>TFT, to ground
<tr><td>LED+ <td>15<td>TFT, to +3.3 V
<tr><td>SD_CS <td>14<td>SDC, to PB0 chip select
<tr><td>MOSI <td>13<td>SDC, to PB8 MOSI
<tr><td>MISO <td>12<td>SDC, to PB7 MISO
<tr><td>SCK <td>11<td>SDC, to PB9 serial clock
<tr><td>CS <td>10<td>TFT, to PB6 SPI1 CS0
<tr><td>SCL <td> 9<td>TFT, to PB9 SPI1 SCLK
<tr><td>SDA <td> 8<td>TFT, to PB8 MOSI SPI1 PICO
<tr><td>A0 <td> 7<td>TFT, to PB16 Data/Command, high for data, low for command
<tr><td>RESET <td> 6<td>TFT, to PB15 reset (GPIO), low to reset
<tr><td>NC <td>3,4,5<td>not connected
<tr><td>VCC <td> 2<td>to +3.3 V
<tr><td>GND <td> 1<td>to ground
</table>
* @{*/
/**
* @file SPI.h
* @brief 160 by 128 pixel LCD
* @details Software driver functions for ST7735R display<br>
* This is a library for the Adafruit 1.8" SPI display.<br>
* This library works with the Adafruit 1.8" TFT Breakout w/SD card<br>
* ----> http://www.adafruit.com/products/358<br>
* as well as Adafruit raw 1.8" TFT display<br>
* ----> http://www.adafruit.com/products/618<br>
* Check out the links above for our tutorials and wiring diagrams<br>
* These displays use SPI to communicate, 4 or 5 pins are required to
* interface (RST is optional)
* Adafruit invests time and resources providing this open source code,
* please support Adafruit and open-source hardware by purchasing
* products from Adafruit!
* Written by Limor Fried/Ladyada for Adafruit Industries.
* MIT license, all text above must be included in any redistribution
* @version ECE445M RTOS
* @author Daniel Valvano and Jonathan Valvano
* @copyright Copyright 2025 by Jonathan W. Valvano, valvano@mail.utexas.edu,
* @warning AS-IS
* @note For more information see http://users.ece.utexas.edu/~valvano/
* @date December 27, 2025
* interface <br>
* \image html ST7735interface.png width=500px
* <br><br>ST7735 160 by 128 pixel LCD<br>
* \image html ST7735.png width=500px
<table>
<caption id="AdafruitLCDpins2">Adafruit ST7735R pins </caption>
<tr><th>Pin <th>Connection <th>Description
<tr><td>10<td>+3.3<td>Backlight
<tr><td>9 <td>PB7 <td>SPI1 MISO (used for SDC)
<tr><td>8 <td>PB9 <td>SPI1 SCLK clock out
<tr><td>7 <td>PB8 <td>SPI1 PICO data out
<tr><td>6 <td>PB6 <td>GPIO CS0=TFT_CS
<tr><td>5 <td>PB0 <td>CARD_CS (used for SDC)
<tr><td>4 <td>PB16<td>Data/Command(GPIO), high for data, low for command
<tr><td>3 <td>PB15<td>RESET, low to reset, (GPIO)
<tr><td>2 <td>+3.3<td>VCC
<tr><td>1 <td>Gnd <td>ground
</table>
<table>
<caption id="HiLetgopins2">HiLetgo ST7735 TFT and SDC pins </caption>
<tr><th>signal<th>Pin<th>Connection
<tr><td>LED- <td>16<td>TFT, to ground
<tr><td>LED+ <td>15<td>TFT, to +3.3 V
<tr><td>SD_CS <td>14<td>SDC, to PB0 chip select
<tr><td>MOSI <td>13<td>SDC, to PB8 MOSI
<tr><td>MISO <td>12<td>SDC, to PB7 MISO
<tr><td>SCK <td>11<td>SDC, to PB9 serial clock
<tr><td>CS <td>10<td>TFT, to PB6 SPI1 CS0
<tr><td>SCL <td> 9<td>TFT, to PB9 SPI1 SCLK
<tr><td>SDA <td> 8<td>TFT, to PB8 MOSI SPI1 PICO
<tr><td>A0 <td> 7<td>TFT, to PB16 Data/Command, high for data, low for command
<tr><td>RESET <td> 6<td>TFT, to PB15 reset (GPIO), low to reset
<tr><td>NC <td>3,4,5<td>not connected
<tr><td>VCC <td> 2<td>to +3.3 V
<tr><td>GND <td> 1<td>to ground
</table>
<br>
******************************************************************************/
#ifndef __SPI_H__
#define __SPI_H__
// PB0 output used for SDC CS
#define SDC_CS GPIOB
#define SDC_CS_PIN (1<<0) // CS controlled by software
#define SDC_CS_INDEX (PB0INDEX) // PB0 GPIO
#define SDC_CS_LOW() (SDC_CS->DOUTCLR31_0 = SDC_CS_PIN) // PB0 low
#define SDC_CS_HIGH() (SDC_CS->DOUTSET31_0 = SDC_CS_PIN) // PB0 high
// PB6 output used for SDC CS
//#define TFT_CS_LOW() (GPIOB->DOUTCLR31_0 = (1<<6)) // PB6 low
//#define TFT_CS_HIGH() (GPIOB->DOUTSET31_0 = (1<<6)) // PB6 high
#define TFT_CS GPIOB
#define TFT_CS_PIN (1<<6) // TFT CS controlled by software
#define TFT_CS_INDEX (PB6INDEX) // PB6 GPIO
#define TFT_CS_LOW() (TFT_CS->DOUTCLR31_0 = TFT_CS_PIN) // PB6 low
#define TFT_CS_HIGH() (TFT_CS->DOUTSET31_0 = TFT_CS_PIN) // PB6 high
#define TFT_DC GPIOB
#define TFT_DC_PIN (1<<16) // D/C controlled by software
#define TFT_DC_INDEX (PB16INDEX) // PB16 GPIO
#define TFT_DC_LOW() (TFT_DC->DOUTCLR31_0 = TFT_DC_PIN) // PB16 low
#define TFT_DC_HIGH() (TFT_DC->DOUTSET31_0 = TFT_DC_PIN) // PB16 high
#define TFT_RST GPIOB
#define TFT_RST_PIN (1<<15) // !RST controlled by software
#define TFT_RST_INDEX (PB15INDEX) // PB15 GPIO
#define TFT_RST_LOW() (TFT_RST->DOUTCLR31_0 = TFT_RST_PIN) // PB15 low
#define TFT_RST_HIGH() (TFT_RST->DOUTSET31_0 = TFT_RST_PIN) // PB15 high
/**
* Output 8-bit data to SPI port.
* RS=PA13=1 for data.
* @param data is an 8-bit data to be transferred
* @return none
* @brief Output data
*/
void SPI_OutData(char data);
/**
* Output 8-bit command to SPI port.
* RS=PA13=0 for command
* @param command is an 8-bit command to be transferred
* @return none
* @brief Output command
*/
void SPI_OutCommand(char command);
/**
* Reset LCD
* -# drive RST high for 500ms
* -# drive RST low for 500ms
* -# drive RST high for 500ms
*
* @param none
* @return none
* @brief Reset LCD
*/
void SPI1_Reset(void);
/**
* SDC CS initialization
* -PB0 GPIO output
* -drive low to enable SDC
* -drive high to disable SDC
*
* @param none
* @return none
* @brief SDC CS initialization
*/
void CS_Init(void);
/**
* Initialize SPI1 for 8 MHz baud clock
* for both SDC and TFT
* using busy-wait synchronization.
* Calls Clock_Freq to get bus clock
* - PB9 SPI1 SCLK
* - PB6 SPI1 CS0
* - PB8 SPI1 PICO
* - PB15 GPIO !RST =1 for run, =0 for reset
* - PB16 GPIO RS =1 for data, =0 for command
*
* @note SPI0,SPI1 in power domain PD1 SysClk equals bus CPU clock
* @param none
* @return none
* @brief initialize SPI1
*/
void SPI1_Init(void);
/**
* Output 8-bit command to SPI port.
* RS=PB16=0 for command.
* @param data is an 8-bit command to be transferred
* @return none
* @brief Output command
*/
void TFT_OutCommand(char command);
/**
* Output 8-bit data to SPI port.
* RS=PB16=1 for data.
* @param data is an 8-bit data to be transferred
* @return none
* @brief Output data
*/
void TFT_OutData(char data);
#endif // __SPI_H__
/** @}*/

140
RTOS_Labs_common/SPI1.h Normal file
View File

@@ -0,0 +1,140 @@
/*!
* @defgroup SPI
* @brief Synchronous serial communication
<table>
<caption id="SPIpins5">SPI-LCD pins </caption>
<tr><th>Pin <th>Function <th>Description
<tr><td>PB9 <td>SPI1 SCLK <tdLCD SPI clock (SPI)
<tr><td>PB6 <td>GPIO CS <td>LCD SPI CS
<tr><td>PB0 <td>GPIO CS <td>SDC SPI CS
<tr><td>PB8 <td>SPI1 PICO <td>LCD SPI data (SPI)
<tr><td>PB7 <td>SPI1 POCI <td>SCD SPI data (SPI)
<tr><td>PB15<td>GPIO <td>J2.17 LCD !RST =1 for run, =0 for reset
<tr><td>PB16<td>GPIO <td>J4.31 LCD D/C RS =1 for data, =0 for command
</table>
* @{*/
/**
* @file SPI1.h
* @brief Synchronous serial communication
* @details SPI uses a chip select, clock, data out and data in. This interface is used for TFT and SDC
* This interface can be used for MKII LCD and the ST7735R LCD
* \image html SPIinterface.png width=500px
* @version RTOS v7.0
* @author Daniel Valvano and Jonathan Valvano
* @copyright Copyright 2025 by Jonathan W. Valvano, valvano@mail.utexas.edu,
* @warning AS-IS
* @note For more information see http://users.ece.utexas.edu/~valvano/
* @date Dec 27, 2025
<table>
<caption id="SPIpins6">SPI-LCD pins </caption>
<tr><th>Pin <th>Function <th>Description
<tr><td>PB9 <td>SPI1 SCLK <tdLCD SPI clock (SPI)
<tr><td>PB6 <td>GPIO CS <td>LCD SPI CS
<tr><td>PB0 <td>GPIO CS <td>SDC SPI CS
<tr><td>PB8 <td>SPI1 PICO <td>LCD SPI data (SPI)
<tr><td>PB7 <td>SPI1 POCI <td>SCD SPI data (SPI)
<tr><td>PB15<td>GPIO <td>J2.17 LCD !RST =1 for run, =0 for reset
<tr><td>PB16<td>GPIO <td>J4.31 LCD D/C RS =1 for data, =0 for command
</table>
******************************************************************************/
#ifndef __SPI_H__
#define __SPI_H__
// PB0 output used for SDC CS
#define SDC_CS GPIOB
#define SDC_CS_PIN (1<<0) // CS controlled by software
#define SDC_CS_INDEX (PB0INDEX) // PB0 GPIO
#define SDC_CS_LOW() (SDC_CS->DOUTCLR31_0 = SDC_CS_PIN) // PB0 low
#define SDC_CS_HIGH() (SDC_CS->DOUTSET31_0 = SDC_CS_PIN) // PB0 high
// PB6 output used for SDC CS
//#define TFT_CS_LOW() (GPIOB->DOUTCLR31_0 = (1<<6)) // PB6 low
//#define TFT_CS_HIGH() (GPIOB->DOUTSET31_0 = (1<<6)) // PB6 high
#define TFT_CS GPIOB
#define TFT_CS_PIN (1<<6) // TFT CS controlled by software
#define TFT_CS_INDEX (PB6INDEX) // PB6 GPIO
#define TFT_CS_LOW() (TFT_CS->DOUTCLR31_0 = TFT_CS_PIN) // PB6 low
#define TFT_CS_HIGH() (TFT_CS->DOUTSET31_0 = TFT_CS_PIN) // PB6 high
#define TFT_DC GPIOB
#define TFT_DC_PIN (1<<16) // D/C controlled by software
#define TFT_DC_INDEX (PB16INDEX) // PB16 GPIO
#define TFT_DC_LOW() (TFT_DC->DOUTCLR31_0 = TFT_DC_PIN) // PB16 low
#define TFT_DC_HIGH() (TFT_DC->DOUTSET31_0 = TFT_DC_PIN) // PB16 high
#define TFT_RST GPIOB
#define TFT_RST_PIN (1<<15) // !RST controlled by software
#define TFT_RST_INDEX (PB15INDEX) // PB15 GPIO
#define TFT_RST_LOW() (TFT_RST->DOUTCLR31_0 = TFT_RST_PIN) // PB15 low
#define TFT_RST_HIGH() (TFT_RST->DOUTSET31_0 = TFT_RST_PIN) // PB15 high
/**
* Output 8-bit data to SPI port.
* RS=PA13=1 for data.
* @param data is an 8-bit data to be transferred
* @return none
* @brief Output data
*/
void SPI_OutData(char data);
/**
* Output 8-bit command to SPI port.
* RS=PA13=0 for command
* @param command is an 8-bit command to be transferred
* @return none
* @brief Output command
*/
void SPI_OutCommand(char command);
/**
* Reset LCD
* -# drive RST high for 500ms
* -# drive RST low for 500ms
* -# drive RST high for 500ms
*
* @param none
* @return none
* @brief Reset LCD
*/
void SPI1_Reset(void);
// SDC CS initialization
void CS_Init(void);
/**
* Initialize SPI1 for 8 MHz baud clock
* for both SDC and TFT
* using busy-wait synchronization.
* Calls Clock_Freq to get bus clock
* - PB9 SPI1 SCLK
* - PB6 SPI1 CS0
* - PB8 SPI1 PICO
* - PB15 GPIO !RST =1 for run, =0 for reset
* - PA13 GPIO RS =1 for data, =0 for command
*
* @note SPI0,SPI1 in power domain PD1 SysClk equals bus CPU clock
* @param none
* @return none
* @brief initialize SPI1
*/
void SPI1_Init(void);
//---------TFT_OutCommand------------
// Output 8-bit command to SPI port
// Input: data is an 8-bit data to be transferred
// Output: none
void TFT_OutCommand(char command);
/**
* Output 8-bit data to SPI port.
* RS=PB16=1 for data.
* @param data is an 8-bit data to be transferred
* @return none
* @brief Output data
*/
void TFT_OutData(char data);
#endif // __SPI_H__
/** @}*/

2093
RTOS_Labs_common/ST7735.c Normal file

File diff suppressed because it is too large Load Diff

840
RTOS_Labs_common/ST7735.h Normal file
View File

@@ -0,0 +1,840 @@
/*!
* @defgroup ST7735
* @brief ST7735R LCD
<table>
<caption id="AdafruitLCDpins">Adafruit ST7735R pins </caption>
<tr><th>Pin <th>Connection <th>Description
<tr><td>10<td>+3.3<td>Backlight
<tr><td>9 <td>nc <td>SPI1 MISO (used for SDC)
<tr><td>8 <td>PB9 <td>SPI1 SCLK clock out
<tr><td>7 <td>PB8 <td>SPI1 PICO data out
<tr><td>6 <td>PB6 <td>SPI1 CS0=TFT_CS
<tr><td>5 <td>nc <td>CARD_CS (used for SDC)
<tr><td>4 <td>PA13<td>Data/Command(GPIO), high for data, low for command
<tr><td>3 <td>PB15<td>RESET, low to reset, (GPIO)
<tr><td>2 <td>+3.3<td>VCC
<tr><td>1 <td>Gnd <td>ground
</table>
<table>
<caption id="widehkpins">wide.hk ST7735R with ADXL345 accelerometer pins </caption>
<tr><th>Pin<th>Connection
<tr><td>VCC <td> +3.3 V
<tr><td>GND <td> Ground
<tr><td>!SCL <td> SPI1 SCLK: PB9 clock
<tr><td>!SDA <td> SPI1 PICO: PB8 MOSI SPI data from microcontroller to TFT or SDC
<tr><td>DC <td> GPIO PA13 TFT data/command
<tr><td>RES <td> GPIO PB15 TFT reset
<tr><td>CS <td> SPI1 CS0: PB6 TFT_CS, active low to enable TFT
<tr><td>*CS <td> (NC) SDC_CS, active low to enable SDC
<tr><td>MISO <td> (NC) MISO SPI data from SDC to microcontroller
<tr><td>SDA <td> (NC) I2C data for ADXL345 accelerometer
<tr><td>SCL <td> (NC) I2C clock for ADXL345 accelerometer
<tr><td>SDO <td> (NC) I2C alternate address for ADXL345 accelerometer
<tr><td>Backlight + - Light, backlight connected to +3.3 V
</table>
<table>
<caption id="HiLetgopins">HiLetgo ST7735 TFT and SDC pins </caption>
<tr><th>signal<th>Pin<th>Connection
<tr><td>LED- <td>16<td>TFT, to ground
<tr><td>LED+ <td>15<td>TFT, to +3.3 V
<tr><td>SD_CS <td>14<td>SDC, to chip select
<tr><td>MOSI <td>13<td>SDC, to MOSI
<tr><td>MISO <td>12<td>SDC, to MISO
<tr><td>SCK <td>11<td>SDC, to serial clock
<tr><td>CS <td>10<td>TFT, to PB6 SPI1 CS0
<tr><td>SCL <td> 9<td>TFT, to PB9 SPI1 SCLK
<tr><td>SDA <td> 8<td>TFT, to PB8 MOSI SPI1 PICO
<tr><td>A0 <td> 7<td>TFT, to PA13 Data/Command, high for data, low for command
<tr><td>RESET <td> 6<td>TFT, to PB15 reset (GPIO), low to reset
<tr><td>NC <td>3,4,5<td>not connected
<tr><td>VCC <td> 2<td>to +3.3 V
<tr><td>GND <td> 1<td>to ground
</table>
* @{*/
/**
* @file ST7735.h
* @brief 160 by 128 pixel LCD
* @details Software driver functions for ST7735R display<br>
* This is a library for the Adafruit 1.8" SPI display.<br>
* This library works with the Adafruit 1.8" TFT Breakout w/SD card<br>
* ----> http://www.adafruit.com/products/358<br>
* as well as Adafruit raw 1.8" TFT display<br>
* ----> http://www.adafruit.com/products/618<br>
* Check out the links above for our tutorials and wiring diagrams<br>
* These displays use SPI to communicate, 4 or 5 pins are required to
* interface (RST is optional)
* Adafruit invests time and resources providing this open source code,
* please support Adafruit and open-source hardware by purchasing
* products from Adafruit!
* Written by Limor Fried/Ladyada for Adafruit Industries.
* MIT license, all text above must be included in any redistribution
* @version ECE319K v1.0
* @author Daniel Valvano and Jonathan Valvano
* @copyright Copyright 2023 by Jonathan W. Valvano, valvano@mail.utexas.edu,
* @warning AS-IS
* @note For more information see http://users.ece.utexas.edu/~valvano/
* @date Oct 25, 2025
* interface <br>
* \image html ST7735interface.png width=500px
* <br><br>ST7735 160 by 128 pixel LCD<br>
* \image html ST7735.png width=500px
<table>
<caption id="AdafruitLCDpins2">Adafruit ST7735R pins </caption>
<tr><th>Pin <th>Connection <th>Description
<tr><td>10<td>+3.3<td>Backlight
<tr><td>9 <td>nc <td>SPI1 MISO (used for SDC)
<tr><td>8 <td>PB9 <td>SPI1 SCLK clock out
<tr><td>7 <td>PB8 <td>SPI1 PICO data out
<tr><td>6 <td>PB6 <td>SPI1 CS0=TFT_CS
<tr><td>5 <td>nc <td>CARD_CS (used for SDC)
<tr><td>4 <td>PA13<td>Data/Command(GPIO), high for data, low for command
<tr><td>3 <td>PB15<td>RESET, low to reset, (GPIO)
<tr><td>2 <td>+3.3<td>VCC
<tr><td>1 <td>Gnd <td>ground
</table>
<table>
<caption id="HiLetgopins2">HiLetgo ST7735 TFT and SDC pins </caption>
<tr><th>signal<th>Pin<th>Connection
<tr><td>LED- <td>16<td>TFT, to ground
<tr><td>LED+ <td>15<td>TFT, to +3.3 V
<tr><td>SD_CS <td>14<td>SDC, to chip select
<tr><td>MOSI <td>13<td>SDC, to MOSI
<tr><td>MISO <td>12<td>SDC, to MISO
<tr><td>SCK <td>11<td>SDC, to serial clock
<tr><td>CS <td>10<td>TFT, to PB6 SPI1 CS0
<tr><td>SCL <td> 9<td>TFT, to PB9 SPI1 SCLK
<tr><td>SDA <td> 8<td>TFT, to PB8 MOSI SPI1 PICO
<tr><td>A0 <td> 7<td>TFT, to PA13 Data/Command, high for data, low for command
<tr><td>RESET <td> 6<td>TFT, to PB15 reset (GPIO), low to reset
<tr><td>NC <td>3,4,5<td>not connected
<tr><td>VCC <td> 2<td>to +3.3 V
<tr><td>GND <td> 1<td>to ground
</table>
<br>
******************************************************************************/
#ifndef _ST7735H_
#define _ST7735H_
#include <stdint.h>
/**
* \brief some flags for ST7735_InitR()
*/
enum initRFlags{
none,
INITR_GREENTAB,
INITR_REDTAB,
INITR_BLACKTAB
};
/**
* \brief 128 pixels wide
*/
#define ST7735_TFTWIDTH 128
/**
* \brief 160 pixels tall
*/
#define ST7735_TFTHEIGHT 160
/**
* \brief The following constants are possible colors for the LCD in RGB format
*/
#define ST7735_BLACK 0x0000
#define ST7735_BLUE 0xF800
#define ST7735_RED 0x001F
#define ST7735_GREEN 0x07E0
#define ST7735_CYAN 0xFFE0
#define ST7735_MAGENTA 0xF81F
#define ST7735_YELLOW 0x07FF
#define ST7735_WHITE 0xFFFF
#define ST7735_LIGHTGREY ST7735_Color565(228,228,228)
#define ST7735_DARKGREY ST7735_Color565(32,32,32)
#define ST7735_ORANGE ST7735_Color565(255,102,0)
#define ST7735_PURPLE ST7735_Color565(106,13,173)
/**
* Initialize ST7735B color 128x160-pixel TFT LCD
* @param none
* @return none
* @brief Initialize ST7735B LCD
* @note assumes GPIOA and GPIOB are reset and powered previously
*/
void ST7735_InitB(void);
/**
* Initialize ST7735R color 128x160-pixel TFT LCD
* @param option one of none,INITR_GREENTAB,INITR_REDTAB,INITR_BLACKTAB
* @return none
* @brief Initialize ST7735R LCD
* @note assumes GPIOA and GPIOB are reset and powered previously
*/
void ST7735_InitR(enum initRFlags option);
/**
* Color the pixel at the given coordinates with the given color.<br>
* Requires 13 bytes of transmission<br>
* x must be less than 128<br>
* x 0 is on the left, 126 is near the right <br>
* y must be less than 160
* y 159 is near the wires, 0 is the side opposite the wires
* @param x horizontal position of the pixel, columns from the left edge
* @param y vertical position of the pixel, rows from the top edge
* @param color 16-bit color, which can be produced by LCD_Color565()
* @return none
* @brief Color one pixel
*/
void ST7735_DrawPixel(int16_t x, int16_t y, uint16_t color);
/**
* Draw a vertical line at the given coordinates with the given height and color.<br>
* A vertical line is parallel to the longer side of the rectangular display<br>
* Requires (11 + 2*h) bytes of transmission (assuming image fully on screen)
* @param x horizontal position of the start of the line, columns from the left edge
* @param y vertical position of the start of the line, rows from the top edge
* @param h vertical height of the line
* @param color 16-bit color, which can be produced by LCD_Color565()
* @return none
* @brief Draw a vertical line
*/
void ST7735_DrawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
/**
* Draw a horizontal line at the given coordinates with the given width and color.
* A horizontal line is parallel to the shorter side of the rectangular display<br>
* Requires (11 + 2*w) bytes of transmission (assuming image fully on screen)
* @param x horizontal position of the start of the line, columns from the left edge
* @param y vertical position of the start of the line, rows from the top edge
* @param w horizontal width of the line
* @param color 16-bit color, which can be produced by LCD_Color565()
* @return none
* @brief Draw a horizontal line
*/
void ST7735_DrawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
/**
* Fill the screen with the given color.<br>
* Requires 40,971 bytes of transmission
* @param color 16-bit color, which can be produced by ST7735_Color565()
* @return none
* @brief Fill the screen
*/
void ST7735_FillScreen(uint16_t color);
/**
* Draw a filled rectangle at the given coordinates with the given width, height, and color.<br>
* Requires (11 + 2*w*h) bytes of transmission (assuming image fully on screen)
* @param x horizontal position of the top left corner of the rectangle, columns from the left edge
* @param y vertical position of the top left corner of the rectangle, rows from the top edge
* @param w horizontal width of the rectangle
* @param h vertical height of the rectangle
* @param color 16-bit color, which can be produced by ST7735_Color565()
* @return none
* @brief Draw a filled rectangle
*/
void ST7735_FillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
/**
* Draw a small circle (diameter of 6 pixels) at the given coordinates with the given color.<br>
* Requires (11*6+24*2)=114 bytes of transmission (assuming image fully on screen)
* @param x horizontal position of the top left corner of the circle, columns from the left edge
* @param y vertical position of the top left corner of the circle, rows from the top edge
* @param color 16-bit color, which can be produced by ST7735_Color565()
* @return none
* @brief Draw a small circle
*/
void ST7735_DrawSmallCircle(int16_t x, int16_t y, uint16_t color);
/**
* Draw a circle (diameter of 10 pixels) at the given coordinates with the given color.<br>
* Requires (11*10+68*2)=178 bytes of transmission (assuming image on screen)
* @param x horizontal position of the top left corner of the circle, columns from the left edge
* @param y vertical position of the top left corner of the circle, rows from the top edge
* @param color 16-bit color, which can be produced by ST7735_Color565()
* @return none
* @brief Draw a circle
*/
void ST7735_DrawCircle(int16_t x, int16_t y, uint16_t color);
/**
* Pass 8-bit (each) R,G,B and get back 16-bit packed color.
* @param r red value
* @param g green value
* @param b blue value
* @return 16-bit color
* @brief RGB to color creation
*/
uint16_t ST7735_Color565(uint8_t r, uint8_t g, uint8_t b);
/**
* Swaps the red and blue values of the given 16-bit packed color;
* green is unchanged.
* @param x 16-bit color in format B, G, R
* @return 16-bit color in format R, G, B
* @brief Swaps red and blue
*/
uint16_t ST7735_SwapColor(uint16_t x) ;
/**
* Displays a 16-bit color BMP image. A bitmap file that is created
* by a PC image processing program has a header and may be padded
* with dummy columns so the data have four byte alignment. This
* function assumes that all of that has been stripped out, and the
* array image[] has one 16-bit halfword for each pixel to be
* displayed on the screen (encoded in reverse order, which is
* standard for bitmap files). An array can be created in this
* format from a 24-bit-per-pixel .bmp file using the associated
* <b>BmpConvert16.exe</b> converter program.
* (x,y) is the screen location of the lower left corner of BMP image<br>
* Requires (11 + 2*w*h) bytes of transmission (assuming image fully on screen)
* @param x horizontal position of the bottom left corner of the image, columns from the left edge
* @param y vertical position of the bottom left corner of the image, rows from the top edge
* @param image pointer to a 16-bit color BMP image
* @param w number of pixels wide
* @param h number of pixels tall
* @note Must be less than or equal to 128 pixels wide by 160 pixels high
* @return none
* @brief Displays a BMP image
*/
void ST7735_DrawBitmap(int16_t x, int16_t y, const uint16_t *image, int16_t w, int16_t h);
/**
* Simple character draw function. This is the same function from
* Adafruit_GFX.c but adapted for this processor. However, each call
* to ST7735_DrawPixel() calls setAddrWindow(), which needs to send
* many extra data and commands. If the background color is the same
* as the text color, no background will be printed, and text can be
* drawn right over existing images without covering them with a box.<br>
* Requires (11 + 2*size*size)*6*8 bytes of transmission (image fully on screen; textcolor != bgColor)
* @param x horizontal position of the top left corner of the character, columns from the left edge
* @param y vertical position of the top left corner of the character, rows from the top edge
* @param c character to be printed
* @param textColor 16-bit color of the character
* @param bgColor 16-bit color of the background
* @param size number of pixels per character pixel (e.g. size==2 prints each pixel of font as 2x2 square)
* @return none
* @brief Draw a character
*/
void ST7735_DrawCharS(int16_t x, int16_t y, char c, int16_t textColor, int16_t bgColor, uint8_t size);
/**
* Advanced character draw function. This is similar to the function
* from Adafruit_GFX.c but adapted for this processor. However, this
* function only uses one call to setAddrWindow(), which allows it to
* run at least twice as fast.<br>
* Requires (11 + size*size*6*8) bytes of transmission (assuming image fully on screen)
* @param x horizontal position of the top left corner of the character, columns from the left edge
* @param y vertical position of the top left corner of the character, rows from the top edge
* @param c character to be printed
* @param textColor 16-bit color of the character
* @param bgColor 16-bit color of the background
* @param size number of pixels per character pixel (e.g. size==2 prints each pixel of font as 2x2 square)
* @return none
* @brief Draw a character
*/
void ST7735_DrawChar(int16_t x, int16_t y, char c, int16_t textColor, int16_t bgColor, uint8_t size);
/**
* String draw function.
* 16 rows (0 to 15) and 21 characters (0 to 20)<br>
* Requires (11 + size*size*6*8) bytes of transmission for each character
* @param x columns from the left edge (0 to 20)
* @param y rows from the top edge (0 to 12)
* @param pt pointer to a null terminated string to be printed
* @param textColor 16-bit color of the characters
* @return number of characters printed
* @note bgColor is Black and size is 1
* @brief Draw a string
*/
uint32_t ST7735_DrawString(uint16_t x, uint16_t y, char *pt, int16_t textColor);;
/**
* Move the cursor to the desired X- and Y-position. The
* next character of the next unsigned decimal will be
* printed here. X=0 is the leftmost column. Y=0 is the top row.
* The cursor is used by the "Out" functions, but not the "Draw" functions
* @param newX new X-position of the cursor (0<=newX<=20)
* @param newY new Y-position of the cursor (0<=newY<=15)
* @return none
* @brief Move the cursor
*/
void ST7735_SetCursor(uint32_t newX, uint32_t newY);
/**
* Output a 32-bit number in unsigned decimal format.
* Position determined by ST7735_SetCursor command.
* Color set by ST7735_SetTextColor.
* @param n 32-bit number to be transferred
* @return none
* @note Variable format 1-10 digits with no space before or after
* @brief Output an unsigned decimal
*/
void ST7735_OutUDec(uint32_t n);
/**
* Output a 32-bit number in unsigned 4-digit decimal format
* with no space before or after.
* Color set by ST7735_SetTextColor.
* Position determined by ST7735_SetCursor command
* @param n 32-bit number to be transferred
* @return none
* @note Fixed format 4 digits with no space before or after
* @brief Output a 4-digit unsigned decimal
*/
void ST7735_OutUDec4(uint32_t n);
/**
* Output a 32-bit number in unsigned 5-digit decimal format
* with no space before or after.
* Color set by ST7735_SetTextColor.
* Position determined by ST7735_SetCursor command
* @param n 32-bit number to be transferred
* @return none
* @note Fixed format 5 digits with no space before or after
* @brief Output a 5-digit unsigned decimal
*/
void ST7735_OutUDec5(uint32_t n);
/**
* Output a 32-bit number in unsigned 3-digit fixed point, 0.1 resolution
* numbers 0 to 999 printed as " 0.0" to "99.9"
* Position determined by ST7735_SetCursor command
* @param n 32-bit number to be transferred
* @param textColor 16-bit color of the numbers
* @return none
* @note Fixed format 4 characters with no space before or after
* @brief Output a 2-digit fixed-point decimal
*/
void ST7735_OutUFix2_1(uint32_t n, int16_t textColor);
/**
* Output a 32-bit number in unsigned 2-digit hexadecimal format
* numbers 0 to 255 printed as "00," to "FF,"
* Position determined by ST7735_SetCursor command
* @param n 32-bit number to be transferred
* @param textColor 16-bit color of the numbers
* @return none
* @note Fixed format 3 characters with comma after
* @brief Output a 2-digit hexadecimal number
*/
void ST7735_OutUHex2(uint32_t n, int16_t textColor);
/**
* Change the image rotation.
* Requires 2 bytes of transmission
* @param m new rotation value (0 to 3)
* @return none
* @brief Change rotation
*/
void ST7735_SetRotation(uint8_t m) ;
/**
* Send the command to invert all of the colors.
* Requires 1 byte of transmission
* @param i 0 to disable inversion; non-zero to enable inversion
* @return none
* @brief invert display
*/
void ST7735_InvertDisplay(int i) ;
/**
* Set up the axes, labels, and other variables to
* allow data to be plotted in a chart using the
* functions ST7735_PlotPoint() and
* ST7735_PlotIncrement().
* If yLabel2 is empty string, no yLabel2 is printed, and yLabel1 is centered
* - graphics routines
* - y coordinates 0 to 31 used for labels and messages
* - y coordinates 32 to 159 128 pixels high
* - x coordinates 0 to 127 128 pixels wide
*
* @param axisColor 16-bit color for axes, which can be produced by LCD_Color565()
* @param bgColor 16-bit color for plot background, which can be produced by LCD_Color565()
* @param xLabel pointer to a null terminated string for x-axis (~4 character space)
* @param yLabel1 pointer to a null terminated string for top of y-axis (~3-5 character space)
* @param label1Color 16-bit color for y-axis label1, which can be produced by LCD_Color565()
* @param yLabel2 pointer to a null terminated string for bottom of y-axis (~3 character space)
* @param label2Color 16-bit color for y-axis label2, which can be produced by LCD_Color565()
* @param ymax maximum value to be printed
* @param ymin minimum value to be printed
* @return none
* @note Assumes: ST7735_InitR() has been called
* @brief Initializes a plot
*/
void ST7735_Drawaxes(uint16_t axisColor, uint16_t bgColor, char *xLabel,
char *yLabel1, uint16_t label1Color, char *yLabel2, uint16_t label2Color,
int32_t ymax, int32_t ymin);
/**
* Clear the graphics buffer, set X coordinate to 0
* This routine clears the display
* @param ymin minimum plot value
* @param ymax maximum plot value
* @return none
* @brief Clear plot
*/
void ST7735_PlotClear(int32_t ymin, int32_t ymax);
/**
* Plot a point on the chart. To plot several points in the
* same column, call this function repeatedly before calling
* ST7735PlotIncrement(). The units of the data are the
* same as the ymax and ymin values specified in the
* initialization function.
* @param y value to be plotted (units not specified)
* @return none
* @note Assumes: ST7735_InitR() and ST7735_Drawaxes() have been called
* @brief Plot one point
*/
void ST7735_PlotPoint(int32_t y);
/**
* Plot a point on the chart. To plot several points in the
* same column, call this function repeatedly before calling
* ST7735_PlotIncrement(). The units of the data are the
* same as the ymax and ymin values specified in the
* initialization function.
* @param data1 value to be plotted (units not specified)
* @param color1 16-bit color for the point, which can be produced by ST7735_Color565()
* @return none
* @note Assumes: ST7735_InitR() and ST7735_Drawaxes() have been called
* @brief Plot one point with color
*/
void ST7735_PlotPoint2(int32_t data1, uint16_t color1);
/**
* Increment the plot between subsequent calls to
* ST7735_PlotPoint(). Automatically wrap and clear the
* column to be printed to.
* ST7735_PlotIncrement will erase the new line (clearing the display as it goes).
* ST7735_PlotNext does not erase the new line (plots over itself as it wraps).
* @param none
* @return none
* @note Assumes: ST7735_InitR() and ST7735_Drawaxes() have been called
* @brief Moves the plot cursor in time
*/
void ST7735PlotIncrement(void);
/**
* Used in the voltage versus time plot, plot line to new point
* It does output to display
* @param y value to be plotted
* @return none
* @note Assumes: ST7735_InitR() and ST7735_Drawaxes() have been called
* @brief Plot one line
*/
void ST7735_PlotLine(int32_t y);
// *************** ST7735_PlotPoints ********************
// Used in the voltage versus time plot, plot two points at y1, y2
// It does output to display
// Inputs: y1 is the y coordinate of the first point plotted
// y2 is the y coordinate of the second point plotted
// Outputs: none
/**
* Used in the voltage versus time plot, plot two points at y1, y2
* It does output to display. The units of the data are the
* same as the ymax and ymin values specified in the
* initialization function. Call ST7735_PlotIncrement() to move time.
* @param y1 is the y coordinate of the first point plotted
* @param y2 is the y coordinate of the second point plotted
* @return none
* @note Assumes: ST7735_InitR() and ST7735_Drawaxes() have been called
* @brief Plot two points
*/
void ST7735_PlotPoints(int32_t y1,int32_t y2);
/**
* Used in the voltage versus time bar plot, plot one bar at y
* It does output to display. The units of the data are the
* same as the ymax and ymin values specified in the
* initialization function. Call ST7735_PlotIncrement() to move time.
* @param y is the y coordinate of the bar plotted
* @return none
* @note Assumes: ST7735_InitR() and ST7735_Drawaxes() have been called
* @brief Plot one bar
*/
void ST7735_PlotBar(int32_t y);
/**
* Used in the amplitude versus frequency plot, plot one bar at y
* It does output to display. 0 to 0.625V scaled on a log plot from min to max.
* Call ST7735_PlotIncrement() to move x axis.
* @param y is the y ADC value of the bar plotted
* @return none
* @note Assumes: ST7735_InitR() and ST7735_Drawaxes() have been called
* @brief Plot one bar
*/
void ST7735_PlotdBfs(int32_t y);
/**
* Used in all the plots to step the X coordinate one pixel.
* ST7735_PlotIncrement will erase the new line (clearing the display as it goes).
* ST7735_PlotNext does not erase the new line (plots over itself as it wraps).
* X steps from 0 to 127, then back to 0 again
* It does not output to display
* @param none
* @return none
* @brief Move x-axis parameter
*/
void ST7735_PlotNext(void);
/**
* Used in all the plots to step the X coordinate one pixel
* X steps from 0 to 127, then back to 0 again
* It clears the vertical space into which the next pixel will be drawn
* @param none
* @return none
* @brief Move X coordinate
*/
void ST7735_PlotNextErase(void);
// Used in all the plots to write buffer to LCD
// Example 1 Voltage versus time
// ST7735_PlotClear(0,4095); // range from 0 to 4095
// ST7735_PlotPoint(data); ST7735_PlotNext(); // called 128 times
// Example 2a Voltage versus time (N data points/pixel, time scale)
// ST7735_PlotClear(0,4095); // range from 0 to 4095
// { for(j=0;j<N;j++){
// ST7735_PlotPoint(data[i++]); // called N times
// }
// ST7735_PlotNext();
// } // called 128 times
// Example 2b Voltage versus time (N data points/pixel, time scale)
// ST7735_PlotClear(0,4095); // range from 0 to 4095
// { for(j=0;j<N;j++){
// ST7735_PlotLine(data[i++]); // called N times
// }
// ST7735_PlotNext();
// } // called 128 times
// Example 3 Voltage versus frequency (512 points)
// perform FFT to get 512 magnitudes, mag[i] (0 to 4095)
// ST7735_PlotClear(0,1023); // clip large magnitudes
// {
// ST7735_PlotBar(mag[i++]); // called 4 times
// ST7735_PlotBar(mag[i++]);
// ST7735_PlotBar(mag[i++]);
// ST7735_PlotBar(mag[i++]);
// ST7735_PlotNext();
// } // called 128 times
// Example 4 Voltage versus frequency (512 points), dB scale
// perform FFT to get 512 magnitudes, mag[i] (0 to 4095)
// ST7735_PlotClear(0,511); // parameters ignored
// {
// ST7735_PlotdBfs(mag[i++]); // called 4 times
// ST7735_PlotdBfs(mag[i++]);
// ST7735_PlotdBfs(mag[i++]);
// ST7735_PlotdBfs(mag[i++]);
// ST7735_PlotNext();
// } // called 128 times
/**
* Output one character to the LCD
* Position determined by ST7735_SetCursor command
* Color set by ST7735_SetTextColor
* @param ch 8-bit ASCII character
* @return none
* @brief Output a character
*/
void ST7735_OutChar(char ch);
/**
* Output one character to the LCD
* Position determined by ST7735_SetCursor command
* Color set by ST7735_SetTextColor, background is transparent
* @param ch 8-bit ASCII character
* @return none
* @brief Output a character transparently
*/
void ST7735_OutCharTransparent(char ch);
/**
* Print a string of characters to the ST7735 LCD.
* Position determined by ST7735_SetCursor command
* Color set by ST7735_SetTextColor
* The string will not automatically wrap.
* @param ptr pointer to NULL-terminated ASCII string
* @return none
* @brief Output a string
*/
void ST7735_OutString(char *ptr);
/**
* Print a string of characters to the ST7735 LCD.
* Position determined by ST7735_SetCursor command
* Color set by ST7735_SetTextColor, background is transparent
* The string will not automatically wrap.
* @param ptr pointer to NULL-terminated ASCII string
* @return none
* @brief Output a string transparently
*/
void ST7735_OutStringTransparent(char *ptr);
/**
* Sets the color in which the characters will be printed
* Background color is fixed at black
* @param color 16-bit packed color
* @return none
* @brief sets the text color
*/
void ST7735_SetTextColor(uint16_t color);
/**
* Initialize the ST7735 for printf
* @param none
* @return none
* @brief use ST7735 LCD to output from printf
*/
void ST7735_InitPrintf(void);
/**
* Outputs signed fixed point number to LCD.
* The format signed 32-bit with resolution 0.01.
* The range -99.99 to +99.99
<table>
<caption id="ST7735_sDecOut2">ST7735_sDecOut2 </caption>
<tr><th>Parameter <th>LCD display
<tr><td> 12345 <td>" **.**"
<tr><td> 2345 <td>" 23.45"
<tr><td> -8100 <td><td>"-81.00"
<tr><td> -102 <td>" -1.02"
<tr><td> 31 <td>" 0.31"
<tr><td>-12345 <td>"-**.**"
</table>
* @param n signed 32-bit integer part of fixed-point number
* @return none
* @brief fixed point output resolution 0.01
* @note send exactly 6 characters to the LCD
*/
void ST7735_sDecOut2(int32_t n);
/**
* unsigned 32-bit binary fixed-point with a resolution of 1/64.
* The full-scale range is from 0 to 999.99.
* If the integer part is larger than 63999, it signifies an error.
* The ST7735_uBinOut6 function takes an unsigned 32-bit integer part
* of the binary fixed-point number and outputs the fixed-point value on the LCD
<table>
<caption id="ST7735_uBinOut6">ST7735_uBinOut6 </caption>
<tr><th>Parameter <th>LCD display
<tr><td> 0 <td>" 0.00"
<tr><td> 1 <td>" 0.01"
<tr><td> 16 <td>" 0.25"
<tr><td> 25 <td>" 0.39"
<tr><td> 125 <td>" 1.95"
<tr><td> 128 <td>" 2.00"
<tr><td> 1250 <td>" 19.53"
<tr><td> 7500 <td>"117.19"
<tr><td>63999 <td>"999.99"
<tr><td>64000 <td>"***.**"
</table>
* @param n unsigned 32-bit integer part of binary fixed-point number
* @return none
* @brief fixed point output resolution 1/64
* @note send exactly 6 characters to the LCD
*/
void ST7735_uBinOut6(uint32_t n);
/**
* Specify the X and Y axes for an x-y scatter plot
* Draw the title and clear the plot area
* @param title ASCII string to label the plot, null-termination
* @param minX smallest X data value allowed, resolution= 0.001
* @param maxX largest X data value allowed, resolution= 0.001
* @param minY smallest Y data value allowed, resolution= 0.001
* @param maxY largest Y data value allowed, resolution= 0.001
* @return none
* @note assumes minX < maxX, and miny < maxY
* @brief initialize XY plot
*/
void ST7735_XYplotInit(char *title, int32_t minX, int32_t maxX, int32_t minY, int32_t maxY);
/**
* Plot an array of (x,y) data, neglect any points outside the minX maxY minY maxY bounds
* @param num number of data points in the two arrays
* @param bufX array of 32-bit fixed-point data, resolution= 0.001
* @param bufY array of 32-bit fixed-point data, resolution= 0.001
* @return none
* @note assumes ST7735_XYplotInit has been previously called
* @brief XY plot
*/
void ST7735_XYplot(uint32_t num, int32_t bufX[], int32_t bufY[]);
/**
* Draws one line on the ST7735 color LCD<br>
* - (x1,y1) is the start point
* - (x2,y2) is the end point
* - x1 x2 must be less than 128, 0 is on the left, 126 is near the right
* - y1 y2 must be less than 160, 159 is near the wires, 0 is the side opposite the wires
*
* @param x1 horizonal position
* @param x2 horizonal position
* @param y1 vertical position
* @param y2 vertical position
* @param color 16-bit color, which can be produced by ST7735_Color565()
* @return none
* @brief Draws line
*/
void ST7735_Line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2,
uint16_t color);
/**
* Used in all the plots to change the X coordinate to new location
* X exists in the range from 0 to 127,
* Input values less than 0 get changed to 0,
* Input values greater than 127 get changed to 127
* It does not output to display
* @param newX is the new value that the global X will be
* @return none
* @brief set X-position
*/
void ST7735_SetX(int32_t newX);
#endif
/** @}*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,814 @@
/*!
* @defgroup ST7735
* @brief ST7735R_SDC LCD
<table>
<caption id="AdafruitLCDpins5">Adafruit ST7735R pins </caption>
<tr><th>Pin <th>Connection <th>Description
<tr><td>10<td>+3.3<td>Backlight
<tr><td>9 <td>PB7 <td>SPI1 MISO (used for SDC)
<tr><td>8 <td>PB9 <td>SPI1 SCLK clock out
<tr><td>7 <td>PB8 <td>SPI1 PICO data out
<tr><td>6 <td>PB6 <td>GPIO CS0=TFT_CS
<tr><td>5 <td>PB0 <td>CARD_CS (used for SDC)
<tr><td>4 <td>PB16<td>Data/Command(GPIO), high for data, low for command
<tr><td>3 <td>PB15<td>RESET, low to reset, (GPIO)
<tr><td>2 <td>+3.3<td>VCC
<tr><td>1 <td>Gnd <td>ground
</table>
<table>
<caption id="HiLetgopins5">HiLetgo ST7735 TFT and SDC pins </caption>
<tr><th>signal<th>Pin<th>Connection
<tr><td>LED- <td>16<td>TFT, to ground
<tr><td>LED+ <td>15<td>TFT, to +3.3 V
<tr><td>SD_CS <td>14<td>SDC, to PB0 chip select
<tr><td>MOSI <td>13<td>SDC, to PB8 MOSI
<tr><td>MISO <td>12<td>SDC, to PB7 MISO
<tr><td>SCK <td>11<td>SDC, to PB9 serial clock
<tr><td>CS <td>10<td>TFT, to PB6 SPI1 CS0
<tr><td>SCL <td> 9<td>TFT, to PB9 SPI1 SCLK
<tr><td>SDA <td> 8<td>TFT, to PB8 MOSI SPI1 PICO
<tr><td>A0 <td> 7<td>TFT, to PB16 Data/Command, high for data, low for command
<tr><td>RESET <td> 6<td>TFT, to PB15 reset (GPIO), low to reset
<tr><td>NC <td>3,4,5<td>not connected
<tr><td>VCC <td> 2<td>to +3.3 V
<tr><td>GND <td> 1<td>to ground
</table>
* @{*/
/**
* @file ST7735_SDC.h
* @brief 160 by 128 pixel LCD with SDC
* @details Software driver functions for ST7735R display<br>
* This is a library for the Adafruit 1.8" SPI display.<br>
* This library works with the Adafruit 1.8" TFT Breakout w/SD card<br>
* ----> http://www.adafruit.com/products/358<br>
* as well as Adafruit raw 1.8" TFT display<br>
* ----> http://www.adafruit.com/products/618<br>
* Check out the links above for our tutorials and wiring diagrams<br>
* These displays use SPI to communicate, 4 or 5 pins are required to
* interface (RST is optional)
* Adafruit invests time and resources providing this open source code,
* please support Adafruit and open-source hardware by purchasing
* products from Adafruit!
* Written by Limor Fried/Ladyada for Adafruit Industries.
* MIT license, all text above must be included in any redistribution
* @version ECE445M RTOS
* @author Daniel Valvano and Jonathan Valvano
* @copyright Copyright 2026 by Jonathan W. Valvano, valvano@mail.utexas.edu,
* @warning AS-IS
* @note For more information see http://users.ece.utexas.edu/~valvano/
* @date December 27, 2025
* interface <br>
* <br><br>ST7735 160 by 128 pixel LCD<br>
<table>
<caption id="AdafruitLCDpins6">Adafruit ST7735R pins </caption>
<tr><th>Pin <th>Connection <th>Description
<tr><td>10<td>+3.3<td>Backlight
<tr><td>9 <td>PB7 <td>SPI1 MISO (used for SDC)
<tr><td>8 <td>PB9 <td>SPI1 SCLK clock out
<tr><td>7 <td>PB8 <td>SPI1 PICO data out
<tr><td>6 <td>PB6 <td>GPIO CS0=TFT_CS
<tr><td>5 <td>PB0 <td>CARD_CS (used for SDC)
<tr><td>4 <td>PB16<td>Data/Command(GPIO), high for data, low for command
<tr><td>3 <td>PB15<td>RESET, low to reset, (GPIO)
<tr><td>2 <td>+3.3<td>VCC
<tr><td>1 <td>Gnd <td>ground
</table>
<table>
<caption id="HiLetgopins6">HiLetgo ST7735 TFT and SDC pins </caption>
<tr><th>signal<th>Pin<th>Connection
<tr><td>LED- <td>16<td>TFT, to ground
<tr><td>LED+ <td>15<td>TFT, to +3.3 V
<tr><td>SD_CS <td>14<td>SDC, to PB0 chip select
<tr><td>MOSI <td>13<td>SDC, to PB8 MOSI
<tr><td>MISO <td>12<td>SDC, to PB7 MISO
<tr><td>SCK <td>11<td>SDC, to PB9 serial clock
<tr><td>CS <td>10<td>TFT, to PB6 SPI1 CS0
<tr><td>SCL <td> 9<td>TFT, to PB9 SPI1 SCLK
<tr><td>SDA <td> 8<td>TFT, to PB8 MOSI SPI1 PICO
<tr><td>A0 <td> 7<td>TFT, to PB16 Data/Command, high for data, low for command
<tr><td>RESET <td> 6<td>TFT, to PB15 reset (GPIO), low to reset
<tr><td>NC <td>3,4,5<td>not connected
<tr><td>VCC <td> 2<td>to +3.3 V
<tr><td>GND <td> 1<td>to ground
</table>
<br>
******************************************************************************/
#ifndef _ST7735H_SDC_
#define _ST7735H_SDC_
#include <stdint.h>
/**
* \brief some flags for ST7735_InitR()
*/
enum initRFlags{
none,
INITR_GREENTAB,
INITR_REDTAB, // Adafruit
INITR_BLACKTAB // Hiletgo
};
/**
* \brief 128 pixels wide
*/
#define ST7735_TFTWIDTH 128
/**
* \brief 160 pixels tall
*/
#define ST7735_TFTHEIGHT 160
/**
* \brief The following constants are possible colors for the LCD in RGB format
*/
#define ST7735_BLACK 0x0000
#define ST7735_BLUE 0xF800
#define ST7735_RED 0x001F
#define ST7735_GREEN 0x07E0
#define ST7735_CYAN 0xFFE0
#define ST7735_MAGENTA 0xF81F
#define ST7735_YELLOW 0x07FF
#define ST7735_WHITE 0xFFFF
#define ST7735_LIGHTGREY ST7735_Color565(228,228,228)
#define ST7735_DARKGREY ST7735_Color565(32,32,32)
#define ST7735_ORANGE ST7735_Color565(255,102,0)
#define ST7735_PURPLE ST7735_Color565(106,13,173)
/**
* Initialize ST7735B color 128x160-pixel TFT LCD
* @param none
* @return none
* @brief Initialize ST7735B LCD
* @note assumes GPIOA and GPIOB are reset and powered previously
*/
void ST7735_InitB(void);
/**
* Initialize ST7735R color 128x160-pixel TFT LCD <br>
* - Use INITR_REDTAB for Adafruit
* - Use INITR_BLACKTAB for Hiletgo
* @param option one of none,INITR_GREENTAB,INITR_REDTAB,INITR_BLACKTAB
* @return none
* @brief Initialize ST7735R LCD
* @note assumes GPIOA and GPIOB are reset and powered previously
*/
void ST7735_InitR(enum initRFlags option);
/**
* Color the pixel at the given coordinates with the given color.<br>
* Requires 13 bytes of transmission<br>
* x must be less than 128<br>
* x 0 is on the left, 126 is near the right <br>
* y must be less than 160
* y 159 is near the wires, 0 is the side opposite the wires
* @param x horizontal position of the pixel, columns from the left edge
* @param y vertical position of the pixel, rows from the top edge
* @param color 16-bit color, which can be produced by LCD_Color565()
* @return none
* @brief Color one pixel
*/
void ST7735_DrawPixel(int16_t x, int16_t y, uint16_t color);
/**
* Draw a vertical line at the given coordinates with the given height and color.<br>
* A vertical line is parallel to the longer side of the rectangular display<br>
* Requires (11 + 2*h) bytes of transmission (assuming image fully on screen)
* @param x horizontal position of the start of the line, columns from the left edge
* @param y vertical position of the start of the line, rows from the top edge
* @param h vertical height of the line
* @param color 16-bit color, which can be produced by LCD_Color565()
* @return none
* @brief Draw a vertical line
*/
void ST7735_DrawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
/**
* Draw a horizontal line at the given coordinates with the given width and color.
* A horizontal line is parallel to the shorter side of the rectangular display<br>
* Requires (11 + 2*w) bytes of transmission (assuming image fully on screen)
* @param x horizontal position of the start of the line, columns from the left edge
* @param y vertical position of the start of the line, rows from the top edge
* @param w horizontal width of the line
* @param color 16-bit color, which can be produced by LCD_Color565()
* @return none
* @brief Draw a horizontal line
*/
void ST7735_DrawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
/**
* Fill the screen with the given color.<br>
* Requires 40,971 bytes of transmission
* @param color 16-bit color, which can be produced by ST7735_Color565()
* @return none
* @brief Fill the screen
*/
void ST7735_FillScreen(uint16_t color);
/**
* Draw a filled rectangle at the given coordinates with the given width, height, and color.<br>
* Requires (11 + 2*w*h) bytes of transmission (assuming image fully on screen)
* @param x horizontal position of the top left corner of the rectangle, columns from the left edge
* @param y vertical position of the top left corner of the rectangle, rows from the top edge
* @param w horizontal width of the rectangle
* @param h vertical height of the rectangle
* @param color 16-bit color, which can be produced by ST7735_Color565()
* @return none
* @brief Draw a filled rectangle
*/
void ST7735_FillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
/**
* Draw a small circle (diameter of 6 pixels) at the given coordinates with the given color.<br>
* Requires (11*6+24*2)=114 bytes of transmission (assuming image fully on screen)
* @param x horizontal position of the top left corner of the circle, columns from the left edge
* @param y vertical position of the top left corner of the circle, rows from the top edge
* @param color 16-bit color, which can be produced by ST7735_Color565()
* @return none
* @brief Draw a small circle
*/
void ST7735_DrawSmallCircle(int16_t x, int16_t y, uint16_t color);
/**
* Draw a circle (diameter of 10 pixels) at the given coordinates with the given color.<br>
* Requires (11*10+68*2)=178 bytes of transmission (assuming image on screen)
* @param x horizontal position of the top left corner of the circle, columns from the left edge
* @param y vertical position of the top left corner of the circle, rows from the top edge
* @param color 16-bit color, which can be produced by ST7735_Color565()
* @return none
* @brief Draw a circle
*/
void ST7735_DrawCircle(int16_t x, int16_t y, uint16_t color);
/**
* Pass 8-bit (each) R,G,B and get back 16-bit packed color.
* @param r red value
* @param g green value
* @param b blue value
* @return 16-bit color
* @brief RGB to color creation
*/
uint16_t ST7735_Color565(uint8_t r, uint8_t g, uint8_t b);
/**
* Swaps the red and blue values of the given 16-bit packed color;
* green is unchanged.
* @param x 16-bit color in format B, G, R
* @return 16-bit color in format R, G, B
* @brief Swaps red and blue
*/
uint16_t ST7735_SwapColor(uint16_t x) ;
/**
* Displays a 16-bit color BMP image. A bitmap file that is created
* by a PC image processing program has a header and may be padded
* with dummy columns so the data have four byte alignment. This
* function assumes that all of that has been stripped out, and the
* array image[] has one 16-bit halfword for each pixel to be
* displayed on the screen (encoded in reverse order, which is
* standard for bitmap files). An array can be created in this
* format from a 24-bit-per-pixel .bmp file using the associated
* <b>BmpConvert16.exe</b> converter program.
* (x,y) is the screen location of the lower left corner of BMP image<br>
* Requires (11 + 2*w*h) bytes of transmission (assuming image fully on screen)
* @param x horizontal position of the bottom left corner of the image, columns from the left edge
* @param y vertical position of the bottom left corner of the image, rows from the top edge
* @param image pointer to a 16-bit color BMP image
* @param w number of pixels wide
* @param h number of pixels tall
* @note Must be less than or equal to 128 pixels wide by 160 pixels high
* @return none
* @brief Displays a BMP image
*/
void ST7735_DrawBitmap(int16_t x, int16_t y, const uint16_t *image, int16_t w, int16_t h);
/**
* Simple character draw function. This is the same function from
* Adafruit_GFX.c but adapted for this processor. However, each call
* to ST7735_DrawPixel() calls setAddrWindow(), which needs to send
* many extra data and commands. If the background color is the same
* as the text color, no background will be printed, and text can be
* drawn right over existing images without covering them with a box.<br>
* Requires (11 + 2*size*size)*6*8 bytes of transmission (image fully on screen; textcolor != bgColor)
* @param x horizontal position of the top left corner of the character, columns from the left edge
* @param y vertical position of the top left corner of the character, rows from the top edge
* @param c character to be printed
* @param textColor 16-bit color of the character
* @param bgColor 16-bit color of the background
* @param size number of pixels per character pixel (e.g. size==2 prints each pixel of font as 2x2 square)
* @return none
* @brief Draw a character
*/
void ST7735_DrawCharS(int16_t x, int16_t y, char c, int16_t textColor, int16_t bgColor, uint8_t size);
/**
* Advanced character draw function. This is similar to the function
* from Adafruit_GFX.c but adapted for this processor. However, this
* function only uses one call to setAddrWindow(), which allows it to
* run at least twice as fast.<br>
* Requires (11 + size*size*6*8) bytes of transmission (assuming image fully on screen)
* @param x horizontal position of the top left corner of the character, columns from the left edge
* @param y vertical position of the top left corner of the character, rows from the top edge
* @param c character to be printed
* @param textColor 16-bit color of the character
* @param bgColor 16-bit color of the background
* @param size number of pixels per character pixel (e.g. size==2 prints each pixel of font as 2x2 square)
* @return none
* @brief Draw a character
*/
void ST7735_DrawChar(int16_t x, int16_t y, char c, int16_t textColor, int16_t bgColor, uint8_t size);
/**
* String draw function.
* 16 rows (0 to 15) and 21 characters (0 to 20)<br>
* Requires (11 + size*size*6*8) bytes of transmission for each character
* @param x columns from the left edge (0 to 20)
* @param y rows from the top edge (0 to 12)
* @param pt pointer to a null terminated string to be printed
* @param textColor 16-bit color of the characters
* @return number of characters printed
* @note bgColor is Black and size is 1
* @brief Draw a string
*/
uint32_t ST7735_DrawString(uint16_t x, uint16_t y, char *pt, int16_t textColor);;
/**
* Move the cursor to the desired X- and Y-position. The
* next character of the next unsigned decimal will be
* printed here. X=0 is the leftmost column. Y=0 is the top row.
* The cursor is used by the "Out" functions, but not the "Draw" functions
* @param newX new X-position of the cursor (0<=newX<=20)
* @param newY new Y-position of the cursor (0<=newY<=15)
* @return none
* @brief Move the cursor
*/
void ST7735_SetCursor(uint32_t newX, uint32_t newY);
/**
* Output a 32-bit number in unsigned decimal format.
* Position determined by ST7735_SetCursor command.
* Color set by ST7735_SetTextColor.
* @param n 32-bit number to be transferred
* @return none
* @note Variable format 1-10 digits with no space before or after
* @brief Output an unsigned decimal
*/
void ST7735_OutUDec(uint32_t n);
/**
* Output a 32-bit number in unsigned 4-digit decimal format
* with no space before or after.
* Color set by ST7735_SetTextColor.
* Position determined by ST7735_SetCursor command
* @param n 32-bit number to be transferred
* @return none
* @note Fixed format 4 digits with no space before or after
* @brief Output a 4-digit unsigned decimal
*/
void ST7735_OutUDec4(uint32_t n);
/**
* Output a 32-bit number in unsigned 5-digit decimal format
* with no space before or after.
* Color set by ST7735_SetTextColor.
* Position determined by ST7735_SetCursor command
* @param n 32-bit number to be transferred
* @return none
* @note Fixed format 5 digits with no space before or after
* @brief Output a 5-digit unsigned decimal
*/
void ST7735_OutUDec5(uint32_t n);
/**
* Output a 32-bit number in unsigned 3-digit fixed point, 0.1 resolution
* numbers 0 to 999 printed as " 0.0" to "99.9"
* Position determined by ST7735_SetCursor command
* @param n 32-bit number to be transferred
* @param textColor 16-bit color of the numbers
* @return none
* @note Fixed format 4 characters with no space before or after
* @brief Output a 2-digit fixed-point decimal
*/
void ST7735_OutUFix2_1(uint32_t n, int16_t textColor);
/**
* Output a 32-bit number in unsigned 2-digit hexadecimal format
* numbers 0 to 255 printed as "00," to "FF,"
* Position determined by ST7735_SetCursor command
* @param n 32-bit number to be transferred
* @param textColor 16-bit color of the numbers
* @return none
* @note Fixed format 3 characters with comma after
* @brief Output a 2-digit hexadecimal number
*/
void ST7735_OutUHex2(uint32_t n, int16_t textColor);
/**
* Change the image rotation.
* Requires 2 bytes of transmission
* @param m new rotation value (0 to 3)
* @return none
* @brief Change rotation
*/
void ST7735_SetRotation(uint8_t m) ;
/**
* Send the command to invert all of the colors.
* Requires 1 byte of transmission
* @param i 0 to disable inversion; non-zero to enable inversion
* @return none
* @brief invert display
*/
void ST7735_InvertDisplay(int i) ;
/**
* Set up the axes, labels, and other variables to
* allow data to be plotted in a chart using the
* functions ST7735_PlotPoint() and
* ST7735_PlotIncrement().
* If yLabel2 is empty string, no yLabel2 is printed, and yLabel1 is centered
* - graphics routines
* - y coordinates 0 to 31 used for labels and messages
* - y coordinates 32 to 159 128 pixels high
* - x coordinates 0 to 127 128 pixels wide
*
* @param axisColor 16-bit color for axes, which can be produced by LCD_Color565()
* @param bgColor 16-bit color for plot background, which can be produced by LCD_Color565()
* @param xLabel pointer to a null terminated string for x-axis (~4 character space)
* @param yLabel1 pointer to a null terminated string for top of y-axis (~3-5 character space)
* @param label1Color 16-bit color for y-axis label1, which can be produced by LCD_Color565()
* @param yLabel2 pointer to a null terminated string for bottom of y-axis (~3 character space)
* @param label2Color 16-bit color for y-axis label2, which can be produced by LCD_Color565()
* @param ymax maximum value to be printed
* @param ymin minimum value to be printed
* @return none
* @note Assumes: ST7735_InitR() has been called
* @brief Initializes a plot
*/
void ST7735_Drawaxes(uint16_t axisColor, uint16_t bgColor, char *xLabel,
char *yLabel1, uint16_t label1Color, char *yLabel2, uint16_t label2Color,
int32_t ymax, int32_t ymin);
/**
* Clear the graphics buffer, set X coordinate to 0
* This routine clears the display
* @param ymin minimum plot value
* @param ymax maximum plot value
* @return none
* @brief Clear plot
*/
void ST7735_PlotClear(int32_t ymin, int32_t ymax);
/**
* Plot a point on the chart. To plot several points in the
* same column, call this function repeatedly before calling
* ST7735PlotIncrement(). The units of the data are the
* same as the ymax and ymin values specified in the
* initialization function.
* @param y value to be plotted (units not specified)
* @return none
* @note Assumes: ST7735_InitR() and ST7735_Drawaxes() have been called
* @brief Plot one point
*/
void ST7735_PlotPoint(int32_t y);
/**
* Plot a point on the chart. To plot several points in the
* same column, call this function repeatedly before calling
* ST7735_PlotIncrement(). The units of the data are the
* same as the ymax and ymin values specified in the
* initialization function.
* @param data1 value to be plotted (units not specified)
* @param color1 16-bit color for the point, which can be produced by ST7735_Color565()
* @return none
* @note Assumes: ST7735_InitR() and ST7735_Drawaxes() have been called
* @brief Plot one point with color
*/
void ST7735_PlotPoint2(int32_t data1, uint16_t color1);
/**
* Increment the plot between subsequent calls to
* ST7735_PlotPoint(). Automatically wrap and clear the
* column to be printed to.
* ST7735_PlotIncrement will erase the new line (clearing the display as it goes).
* ST7735_PlotNext does not erase the new line (plots over itself as it wraps).
* @param none
* @return none
* @note Assumes: ST7735_InitR() and ST7735_Drawaxes() have been called
* @brief Moves the plot cursor in time
*/
void ST7735PlotIncrement(void);
/**
* Used in the voltage versus time plot, plot line to new point
* It does output to display
* @param y value to be plotted
* @return none
* @note Assumes: ST7735_InitR() and ST7735_Drawaxes() have been called
* @brief Plot one line
*/
void ST7735_PlotLine(int32_t y);
// *************** ST7735_PlotPoints ********************
// Used in the voltage versus time plot, plot two points at y1, y2
// It does output to display
// Inputs: y1 is the y coordinate of the first point plotted
// y2 is the y coordinate of the second point plotted
// Outputs: none
/**
* Used in the voltage versus time plot, plot two points at y1, y2
* It does output to display. The units of the data are the
* same as the ymax and ymin values specified in the
* initialization function. Call ST7735_PlotIncrement() to move time.
* @param y1 is the y coordinate of the first point plotted
* @param y2 is the y coordinate of the second point plotted
* @return none
* @note Assumes: ST7735_InitR() and ST7735_Drawaxes() have been called
* @brief Plot two points
*/
void ST7735_PlotPoints(int32_t y1,int32_t y2);
/**
* Used in the voltage versus time bar plot, plot one bar at y
* It does output to display. The units of the data are the
* same as the ymax and ymin values specified in the
* initialization function. Call ST7735_PlotIncrement() to move time.
* @param y is the y coordinate of the bar plotted
* @return none
* @note Assumes: ST7735_InitR() and ST7735_Drawaxes() have been called
* @brief Plot one bar
*/
void ST7735_PlotBar(int32_t y);
/**
* Used in the amplitude versus frequency plot, plot one bar at y
* It does output to display. 0 to 0.625V scaled on a log plot from min to max.
* Call ST7735_PlotIncrement() to move x axis.
* @param y is the y ADC value of the bar plotted
* @return none
* @note Assumes: ST7735_InitR() and ST7735_Drawaxes() have been called
* @brief Plot one bar
*/
void ST7735_PlotdBfs(int32_t y);
/**
* Used in all the plots to step the X coordinate one pixel.
* ST7735_PlotIncrement will erase the new line (clearing the display as it goes).
* ST7735_PlotNext does not erase the new line (plots over itself as it wraps).
* X steps from 0 to 127, then back to 0 again
* It does not output to display
* @param none
* @return none
* @brief Move x-axis parameter
*/
void ST7735_PlotNext(void);
/**
* Used in all the plots to step the X coordinate one pixel
* X steps from 0 to 127, then back to 0 again
* It clears the vertical space into which the next pixel will be drawn
* @param none
* @return none
* @brief Move X coordinate
*/
void ST7735_PlotNextErase(void);
// Used in all the plots to write buffer to LCD
// Example 1 Voltage versus time
// ST7735_PlotClear(0,4095); // range from 0 to 4095
// ST7735_PlotPoint(data); ST7735_PlotNext(); // called 128 times
// Example 2a Voltage versus time (N data points/pixel, time scale)
// ST7735_PlotClear(0,4095); // range from 0 to 4095
// { for(j=0;j<N;j++){
// ST7735_PlotPoint(data[i++]); // called N times
// }
// ST7735_PlotNext();
// } // called 128 times
// Example 2b Voltage versus time (N data points/pixel, time scale)
// ST7735_PlotClear(0,4095); // range from 0 to 4095
// { for(j=0;j<N;j++){
// ST7735_PlotLine(data[i++]); // called N times
// }
// ST7735_PlotNext();
// } // called 128 times
// Example 3 Voltage versus frequency (512 points)
// perform FFT to get 512 magnitudes, mag[i] (0 to 4095)
// ST7735_PlotClear(0,1023); // clip large magnitudes
// {
// ST7735_PlotBar(mag[i++]); // called 4 times
// ST7735_PlotBar(mag[i++]);
// ST7735_PlotBar(mag[i++]);
// ST7735_PlotBar(mag[i++]);
// ST7735_PlotNext();
// } // called 128 times
// Example 4 Voltage versus frequency (512 points), dB scale
// perform FFT to get 512 magnitudes, mag[i] (0 to 4095)
// ST7735_PlotClear(0,511); // parameters ignored
// {
// ST7735_PlotdBfs(mag[i++]); // called 4 times
// ST7735_PlotdBfs(mag[i++]);
// ST7735_PlotdBfs(mag[i++]);
// ST7735_PlotdBfs(mag[i++]);
// ST7735_PlotNext();
// } // called 128 times
/**
* Output one character to the LCD
* Position determined by ST7735_SetCursor command
* Color set by ST7735_SetTextColor
* @param ch 8-bit ASCII character
* @return none
* @brief Output a character
*/
void ST7735_OutChar(char ch);
/**
* Print a string of characters to the ST7735 LCD.
* Position determined by ST7735_SetCursor command
* Color set by ST7735_SetTextColor
* The string will not automatically wrap.
* @param ptr pointer to NULL-terminated ASCII string
* @return none
* @brief Output a string
*/
void ST7735_OutString(char *ptr);
/**
* Sets the color in which the characters will be printed
* Background color is fixed at black
* @param color 16-bit packed color
* @return none
* @brief sets the text color
*/
void ST7735_SetTextColor(uint16_t color);
/**
* Initialize the ST7735 for printf
* @param none
* @return none
* @brief use ST7735 LCD to output from printf
*/
void ST7735_InitPrintf(void);
/**
* Outputs signed fixed point number to LCD.
* The format signed 32-bit with resolution 0.01.
* The range -99.99 to +99.99
<table>
<caption id="ST7735_sDecOut2">ST7735_sDecOut2 </caption>
<tr><th>Parameter <th>LCD display
<tr><td> 12345 <td>" **.**"
<tr><td> 2345 <td>" 23.45"
<tr><td> -8100 <td><td>"-81.00"
<tr><td> -102 <td>" -1.02"
<tr><td> 31 <td>" 0.31"
<tr><td>-12345 <td>"-**.**"
</table>
* @param n signed 32-bit integer part of fixed-point number
* @return none
* @brief fixed point output resolution 0.01
* @note send exactly 6 characters to the LCD
*/
void ST7735_sDecOut2(int32_t n);
/**
* unsigned 32-bit binary fixed-point with a resolution of 1/64.
* The full-scale range is from 0 to 999.99.
* If the integer part is larger than 63999, it signifies an error.
* The ST7735_uBinOut6 function takes an unsigned 32-bit integer part
* of the binary fixed-point number and outputs the fixed-point value on the LCD
<table>
<caption id="ST7735_uBinOut6">ST7735_uBinOut6 </caption>
<tr><th>Parameter <th>LCD display
<tr><td> 0 <td>" 0.00"
<tr><td> 1 <td>" 0.01"
<tr><td> 16 <td>" 0.25"
<tr><td> 25 <td>" 0.39"
<tr><td> 125 <td>" 1.95"
<tr><td> 128 <td>" 2.00"
<tr><td> 1250 <td>" 19.53"
<tr><td> 7500 <td>"117.19"
<tr><td>63999 <td>"999.99"
<tr><td>64000 <td>"***.**"
</table>
* @param n unsigned 32-bit integer part of binary fixed-point number
* @return none
* @brief fixed point output resolution 1/64
* @note send exactly 6 characters to the LCD
*/
void ST7735_uBinOut6(uint32_t n);
/**
* Specify the X and Y axes for an x-y scatter plot
* Draw the title and clear the plot area
* @param title ASCII string to label the plot, null-termination
* @param minX smallest X data value allowed, resolution= 0.001
* @param maxX largest X data value allowed, resolution= 0.001
* @param minY smallest Y data value allowed, resolution= 0.001
* @param maxY largest Y data value allowed, resolution= 0.001
* @return none
* @note assumes minX < maxX, and miny < maxY
* @brief initialize XY plot
*/
void ST7735_XYplotInit(char *title, int32_t minX, int32_t maxX, int32_t minY, int32_t maxY);
/**
* Plot an array of (x,y) data, neglect any points outside the minX maxY minY maxY bounds
* @param num number of data points in the two arrays
* @param bufX array of 32-bit fixed-point data, resolution= 0.001
* @param bufY array of 32-bit fixed-point data, resolution= 0.001
* @return none
* @note assumes ST7735_XYplotInit has been previously called
* @brief XY plot
*/
void ST7735_XYplot(uint32_t num, int32_t bufX[], int32_t bufY[]);
/**
* Draws one line on the ST7735 color LCD<br>
* - (x1,y1) is the start point
* - (x2,y2) is the end point
* - x1 x2 must be less than 128, 0 is on the left, 126 is near the right
* - y1 y2 must be less than 160, 159 is near the wires, 0 is the side opposite the wires
*
* @param x1 horizonal position
* @param x2 horizonal position
* @param y1 vertical position
* @param y2 vertical position
* @param color 16-bit color, which can be produced by ST7735_Color565()
* @return none
* @brief Draws line
*/
void ST7735_Line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2,
uint16_t color);
/**
* Used in all the plots to change the X coordinate to new location
* X exists in the range from 0 to 127,
* Input values less than 0 get changed to 0,
* Input values greater than 127 get changed to 127
* It does not output to display
* @param newX is the new value that the global X will be
* @return none
* @brief set X-position
*/
void ST7735_SetX(int32_t newX);
//------------ST7735_Message------------
// String draw and number output.
// Input: device 0 is on top, 1 is on bottom
// line row from top, 0 to 7 for each device
// pt pointer to a null terminated string to be printed
// value signed integer to be printed
void ST7735_Message(uint32_t d, uint32_t l, char *pt, int32_t value);
#endif
/** @}*/

View File

@@ -0,0 +1,81 @@
/* This example accompanies the book
Embedded Systems: Real-Time Operating Systems for the Arm Cortex, Volume 3,
ISBN: 978-1466468863, Jonathan Valvano, copyright (c) 2026
Copyright 2026 by Jonathan W. Valvano, valvano@mail.utexas.edu
You may use, edit, run or distribute this file
as long as the above copyright notice remains
THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED
OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
VALVANO SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
For more information about my classes, my research, and my books, see
http://users.ece.utexas.edu/~valvano/
*/
#define MAX 15000/100
char Times[MAX]; // filled with spaces
char bestTimes[MAX];
int bestj,bestk,bestl;
int ScheduleFinder(int ta, int tb, int tc, int td){
int i,j,k,l; int jitter=0;
int thei,thejitter;
// find schedule for task b with min jitter
jitter = 100000;
for(j=0; j<tb; j++){ // slide factor task B
for(k=0; k<tc; k++){ // slide factor task C
for(l=0; l<td; l++){ // slide factor task D
for(i=0; i<MAX; i++) Times[i] = ' '; // space means time not used
for(i=0; i<MAX; i=i+ta) Times[i] = 'a'; // schedule task a
thejitter = 0;
for(i=j; i<MAX; i=i+tb){
if(Times[i]== ' '){
Times[i] = 'b'; // schedule B no jitter
} else{
thei = i; // search for place to schedule
do{
thei++;
thejitter++;
}
while((Times[thei]!= ' ')&&(thei<MAX));
if(thei<MAX) Times[thei] = 'B'; // schedule B with jitter
}
}
for(i=k; i<MAX; i=i+tc){
if(Times[i]== ' '){
Times[i] = 'c'; // schedule C no jitter
} else{
thei = i; // search for place to schedule
do{
thei++;
thejitter++;
}
while((Times[thei]!= ' ')&&(thei<MAX));
if(thei<MAX) Times[thei] = 'C'; // schedule C with jitter
}
}
for(i=l; i<MAX; i=i+td){
if(Times[i]== ' '){
Times[i] = 'd'; // schedule D no jitter
} else{
thei = i; // search for place to schedule
do{
thei++;
thejitter++;
}
while((Times[thei]!= ' ')&&(thei<MAX));
if(thei<MAX) Times[thei] = 'D'; // schedule D with jitter
}
}
if(thejitter<jitter){
bestj = j; bestk = k; bestl = l;
jitter = thejitter;
for(i=0; i<MAX; i++) bestTimes[i] = Times[i]; // best schedule
}
}
}
}
return jitter;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 530 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

415
RTOS_Labs_common/TFLuna1.c Normal file
View File

@@ -0,0 +1,415 @@
/* TFLuna1.c
* Jonathan Valvano
* Date: 10/21/2025
TF Luna TOF distance sensor
*/
#include <ti/devices/msp/msp.h>
#include "../RTOS_Labs_common/TFLuna1.h"
#include "../inc/Clock.h"
#include "../inc/LaunchPad.h"
// There are four possible sets of UART1 pins that can be used
// Select the TFLUNA1_RX/TFLUNA1_TX for the UART1 pins to use
// overhead of TFLuna will be 17us/measurement at 100 measurements/sec
// SJ-PM-TF-Luna+A01 on RSLK2
// Pin
// 1 Red 5V
// 2 Serial TxD: PA8 is UART1 Tx (MSPM0 to TFLuna1)
// 3 Serial RxD: PA9 is UART1 Rx (TFLuna1 to MSPM0) to use PA9, set jumper J14
// 4 black ground
//#define TFLUNA1_RX PA9INDEX
//#define TFLUNA1_TX PA8INDEX
// SJ-PM-TF-Luna+A01 on RTOS sensor board
// Pin
// 1 Red 5V
// 2 Serial TxD: PA17 is UART1 Tx (MSPM0 to TFLuna1)
// 3 Serial RxD: PA18 is UART1 Rx (TFLuna1 to MSPM0)
// 4 black ground
#define TFLUNA1_RX PA9INDEX
//#define TFLUNA1_RX PA18INDEX
#define TFLUNA1_TX PA17INDEX
// SJ-PM-TF-Luna+A01
// Pin
// 1 Red 5V
// 2 Serial TxD: PB4 is UART1 Tx (MSPM0 to TFLuna1)
// 3 Serial RxD: PB5 is UART1 Rx (TFLuna1 to MSPM0)
// 4 black ground
//#define TFLUNA1_RX PB5INDEX
//#define TFLUNA1_TX PB4INDEX
// SJ-PM-TF-Luna+A01
// Pin
// 1 Red 5V
// 2 Serial TxD: PB6 is UART1 Tx (MSPM0 to TFLuna1)
// 3 Serial RxD: PB7 is UART1 Rx (TFLuna1 to MSPM0)
// 4 black ground
//#define TFLUNA1_RX PB7INDEX
//#define TFLUNA1_TX PB6INDEX
uint32_t LostData1;
#define FIFO1_SIZE 16 // usable size is 15
// prototypes for private functions
void Tx1Fifo_Init(void);
uint32_t Tx1Fifo_Put(uint8_t data);
uint32_t Tx1Fifo_Get(uint8_t *datapt);
void Rx1Fifo_Init(void);
uint32_t Rx1Fifo_Put(uint8_t data);
uint32_t Rx1Fifo_Get(uint8_t *datapt);
int TFLuna1Index;
// 0 for looking for two 59s
// 2-8 filling the TFLunaDataMessage with 9-byte message
void (*TFLuna1Function)(uint32_t); // data in mm
uint8_t TFLuna1LastByte;
uint32_t TFLuna1Distance; // in mm
uint8_t TFLuna1DataMessage[12]; // 9 byte fixed size message
int BadCheckSum1; // errors
// defined in TFLunaCommon
extern const uint8_t ObtainFirmware[4];
// expected response is 0x5A,0x07,0x01,a,b,c,SU version c.b.a
extern const uint8_t System_Reset[4];
// expected response is 0x5A,0x05,0x02,0x00, 0x60 for success
// expected response is 0x5A,0x05,0x02,0x01, 0x61 for failed
extern const uint8_t Frame_Rate[6];
extern const uint8_t Trigger[4];
extern const uint8_t Format_Standard_cm[5];
extern const uint8_t Format_Pixhawk[5];
extern const uint8_t Format_Standard_mm[5];
extern const uint8_t Output_Enable[5];
extern const uint8_t Output_Disable[5];
extern const uint8_t SaveSettings[4];
// power Domain PD0
// for 80MHz bus clock, UART clock is ULPCLK 40MHz
//------------TFLuna_Init------------
// Initialize the UART1 for 115,200 baud rate (assuming 80 MHz clock),
// 8 bit word length, no parity bits, one stop bit, FIFOs enabled
// Input: function 0 for debug, user function for real time
// Output: none
void TFLuna1_Init(void (*function)(uint32_t)){
// do not reset or activate PortA, already done in LaunchPad_Init
// RSTCLR to GPIOA and UART1 peripherals
// bits 31-24 unlock key 0xB1
// bit 1 is Clear reset sticky bit
// bit 0 is reset gpio port
// GPIOA->GPRCM.RSTCTL = (uint32_t)0xB1000003; // called previously
UART1->GPRCM.RSTCTL = 0xB1000003;
// Enable power to GPIOA and UART1 peripherals
// PWREN
// bits 31-24 unlock key 0x26
// bit 0 is Enable Power
// GPIOA->GPRCM.PWREN = (uint32_t)0x26000001; // called previously
UART1->GPRCM.PWREN = 0x26000001;
Clock_Delay(24); // time for uart to power up
// the following code selects which pins to use
IOMUX->SECCFG.PINCM[TFLUNA1_RX] = 0x00040082;
//bit 18 INENA input enable
//bit 7 PC connected
//bits 5-0=2 for UART1_Rx
// configure alternate UART1 transmit function
IOMUX->SECCFG.PINCM[TFLUNA1_TX] = 0x00000082;
//bit 7 PC connected
//bits 5-0=2 for UART1_Tx
UART1->CLKSEL = 0x08; // bus clock
UART1->CLKDIV = 0x00; // no divide
UART1->CTL0 &= ~0x01; // disable UART1
UART1->CTL0 = 0x00020018;
// bit 17 FEN=1 enable FIFO
// bits 16-15 HSE=00 16x oversampling
// bit 14 CTSEN=0 no CTS hardware
// bit 13 RTSEN=0 no RTS hardware
// bit 12 RTS=0 not RTS
// bits 10-8 MODE=000 normal
// bits 6-4 TXE=001 enable TxD
// bit 3 RXE=1 enable TxD
// bit 2 LBE=0 no loop back
// bit 0 ENABLE 0 is disable, 1 to enable
if(Clock_Freq() == 40000000){
// 20000000/16 = 1,250,000 Hz
// Baud = 115200
// 1,250,000/115200 = 10.8506944444
// divider = 10+54/64 = 10.84375
UART1->IBRD = 10;
UART1->FBRD = 54; // baud =1,250,000/10.84375 = 115,273.77
}else if (Clock_Freq() == 32000000){
// 32000000/16 = 2,000,000
// Baud = 115200
// 2,000,000/115200 = 17.3611111
// divider = 21+23/64 = 17.359375
UART1->IBRD = 17;
UART1->FBRD = 23; // 115,211.52
}else if (Clock_Freq() == 80000000){
// 40000000/16 = 2,500,000 Hz
// Baud = 115200
// 2,500,000/115200 = 21.701388
// divider = 21+45/64 = 21.703125
UART1->IBRD = 21;
UART1->FBRD = 45; // baud =2,500,000/21.703125 = 115,191
}else return;
TFLuna1Function = function;
TFLuna1Index = 0; // looking for two 59s
TFLuna1LastByte = 0;
TFLuna1DataMessage[0] = 0x59;
TFLuna1DataMessage[1] = 0x59;
BadCheckSum1 = 0;
Tx1Fifo_Init();
Rx1Fifo_Init();
LostData1 = 0;
UART1->LCRH = 0x00000030;
// bits 5-4 WLEN=11 8 bits
// bit 3 STP2=0 1 stop
// bit 2 EPS=0 parity select
// bit 1 PEN=0 no parity
// bit 0 BRK=0 no break
UART1->CPU_INT.IMASK = 0x0C01;
// bit 11 TXINT yes
// bit 10 RXINT yes
// bit 0 Receive timeout, yes
UART1->IFLS = 0x0422;
// bits 11-8 RXTOSEL receiver timeout select 4 (0xF highest)
// bits 6-4 RXIFLSEL 2 is greater than or equal to half
// bits 2-0 TXIFLSEL 2 is less than or equal to half
NVIC->ICPR[0] = 1<<13; // UART1 is IRQ 13
NVIC->ISER[0] = 1<<13;
NVIC->IP[3] = (NVIC->IP[3]&(~0x0000FF00))|(1<<14); // set priority (bits 15,14) IRQ 13
UART1->CTL0 |= 0x01; // enable UART1
}
// copy from hardware RX FIFO to software RX FIFO
// stop when hardware RX FIFO is empty or software RX FIFO is full
void static copyHardwareToSoftware1(void){
uint8_t letter;
if(TFLuna1Function==0){ // raw data mode
while(((UART1->STAT&0x04) == 0)&&(TFLuna1_InStatus() < (FIFO1_SIZE - 1))){
letter = UART1->RXDATA;
Rx1Fifo_Put(letter);
}
}else{
while((UART1->STAT&0x04)==0){
letter = UART1->RXDATA;
if(TFLuna1Index == 0){
if((letter == 0x59)&&(TFLuna1LastByte == 0x59)){
TFLuna1Index = 2; // looking for message
}
TFLuna1LastByte = letter;
}else{
TFLuna1DataMessage[TFLuna1Index] = letter;
TFLuna1Index++;
if(TFLuna1Index == 9){
TFLuna1LastByte = 0; // get ready for next message
TFLuna1Index = 0;
uint8_t check=0;
for(int i=0;i<8;i++){
check += TFLuna1DataMessage[i];
}
if(check == TFLuna1DataMessage[8]){
TFLuna1Distance = TFLuna1DataMessage[3]*256+TFLuna1DataMessage[2];
// call back
(*TFLuna1Function)(TFLuna1Distance); // pass distance back to higher level
}else{
BadCheckSum1++; // error
}
}
}
}
}
}
//------------TFLuna1_InChar------------
// Wait for new serial port input
// Input: none
// Output: ASCII code for key typed
uint8_t TFLuna1_InChar(void){uint32_t status;
uint8_t letter;
do{
status = Rx1Fifo_Get(&letter);
}while(status==0);
return(letter);
}
// copy from software TX FIFO to hardware TX FIFO
// stop when software TX FIFO is empty or hardware TX FIFO is full
void static copySoftwareToHardware1(void){
uint8_t letter;
while(((UART1->STAT&0x80) == 0) && (TFLuna1_OutStatus() > 0)){
Tx1Fifo_Get(&letter);
UART1->TXDATA = letter;
}
}
//------------UART1_OutChar------------
// Output 8-bit to serial port
// Input: letter is an 8-bit ASCII character to be transferred
// Output: none
void TFLuna1_OutChar(uint8_t data){
while(Tx1Fifo_Put(data) == 0){};
UART1->CPU_INT.IMASK &= ~0x0800; // disarm TX FIFO interrupt
copySoftwareToHardware1();
UART1->CPU_INT.IMASK |= 0x0800; // rearm TX FIFO interrupt
}
//------------TFLuna_OutString------------
// Output String (NULL termination)
// Input: pointer to a NULL-terminated string to be transferred
// Output: none
void TFLuna1_OutString(uint8_t *pt){
while(*pt){
TFLuna1_OutChar(*pt);
pt++;
}
}
//------------TFLuna1_SendMessage------------
// Output message, msg[1] is length
// Input: pointer to message to be transferred
// Output: none
void TFLuna1_SendMessage(const uint8_t msg[]){
for(int i=0; i<msg[1]; i++){
TFLuna1_OutChar(msg[i]);
}
}
void TFLuna1_Format_Standard_mm(void){
TFLuna1_SendMessage(Format_Standard_mm);
}
void TFLuna1_Format_Standard_cm(void){
TFLuna1_SendMessage(Format_Standard_cm);
}
void TFLuna1_Format_Pixhawk(void){
TFLuna1_SendMessage(Format_Pixhawk);
}
void TFLuna1_Output_Enable(void){
TFLuna1_SendMessage(Output_Enable);
}
void TFLuna1_Output_Disable(void){
TFLuna1_SendMessage(Output_Disable);
}
void TFLuna1_Frame_Rate(void){
TFLuna1_SendMessage(Frame_Rate);
}
void TFLuna1_SaveSettings(void){
TFLuna1_SendMessage(SaveSettings);
}
void TFLuna1_System_Reset(void){
TFLuna1_SendMessage(System_Reset);
}
void UART1_IRQHandler(void){ uint32_t status;
status = UART1->CPU_INT.IIDX; // reading clears bit in RIS
if(status == 0x01){ // 0x01 receive timeout
copyHardwareToSoftware1();
}else if(status == 0x0B){ // 0x0B receive
copyHardwareToSoftware1();
}else if(status == 0x0C){ // 0x0C transmit
copySoftwareToHardware1();
if(TFLuna1_OutStatus() == 0){ // software TX FIFO is empty
UART1->CPU_INT.IMASK &= ~0x0800; // disable TX FIFO interrupt
}
}
}
// Declare state variables for FiFo
// size, buffer, put and get indexes
int32_t static Tx1PutI; // Index to put new
int32_t static Tx1GetI; // Index of oldest
uint8_t static Tx1Fifo[FIFO1_SIZE];
// *********** Tx1Fifo_Init**********
// Initializes a software Tx1FIFO of a
// fixed size and sets up indexes for
// put and get operations
void Tx1Fifo_Init(void){
Tx1PutI = Tx1GetI = 0;
}
// *********** Tx1Fifo_Put**********
// Adds an element to the Tx1FIFO
// Input: data is character to be inserted
// Output: 1 for success, data properly saved
// 0 for failure, TxFIFO is full
uint32_t Tx1Fifo_Put(uint8_t data){
if(((Tx1PutI+1)&(FIFO1_SIZE-1)) == Tx1GetI){
return 0;
}
Tx1Fifo[Tx1PutI] = data;
Tx1PutI = (Tx1PutI+1)&(FIFO1_SIZE-1);
return 1; // success
}
// *********** Tx1Fifo_Get**********
// Gets an element from the Tx1FIFO
// Input: pointer to empty 8-bit variable
// Output: If the Tx1FIFO is empty return 0
// If the Tx1FIFO has data, remove it, and put in *datapt, return 1
uint32_t Tx1Fifo_Get(uint8_t *datapt){
if(Tx1GetI == Tx1PutI){
return 0;
}
*datapt = Tx1Fifo[Tx1GetI];
Tx1GetI = (Tx1GetI+1)&(FIFO1_SIZE-1);
return 1; // success
}
//------------TFLuna1_OutStatus------------
// Returns how much data available for reading from Tx1 FIFO
// Input: none
// Output: number of elements in receive FIFO
uint32_t TFLuna1_OutStatus(void){
return ((Tx1PutI - Tx1GetI)&(FIFO1_SIZE-1));
}
int32_t static Rx1PutI; // Index to put new
int32_t static Rx1GetI; // Index of oldest
uint8_t static Rx1Fifo[FIFO1_SIZE];
// *********** Rx1Fifo_Init**********
// Initializes a software RxFIFO of a
// fixed size and sets up indexes for
// put and get operations
void Rx1Fifo_Init(void){
Rx1PutI = Rx1GetI = 0;
}
// *********** Rx1Fifo_Put**********
// Adds an element to the Rx1FIFO
// Input: data is character to be inserted
// Output: 1 for success, data properly saved
// 0 for failure, RxFIFO is full
uint32_t Rx1Fifo_Put(uint8_t data){
if(((Rx1PutI+1)&(FIFO1_SIZE-1)) == Rx1GetI){
return 0;
}
Rx1Fifo[Rx1PutI] = data;
Rx1PutI = (Rx1PutI+1)&(FIFO1_SIZE-1);
return 1;
}
// *********** Rx1Fifo_Get**********
// Gets an element from the Rx1FIFO
// Input: pointer to empty 8-bit variable
// Output: If the Rx1FIFO is empty return 0
// If the Rx1FIFO has data, remove it, and put in *datapt, return 1
uint32_t Rx1Fifo_Get(uint8_t *datapt){
if(Rx1GetI == Rx1PutI){
return 0;
}
*datapt = Rx1Fifo[Rx1GetI];
Rx1GetI = (Rx1GetI+1)&(FIFO1_SIZE-1);
return 1;
}
//------------TFLuna1_InStatus------------
// Returns how much data available for reading from Rx1 FIFO
// Input: none
// Output: number of elements in receive FIFO
uint32_t TFLuna1_InStatus(void){
return ((Rx1PutI - Rx1GetI)&(FIFO1_SIZE-1));
}

184
RTOS_Labs_common/TFLuna1.h Normal file
View File

@@ -0,0 +1,184 @@
/*!
* @defgroup TFLuna
* @brief Asynchronous serial communication to TFLuna1
<table>
<caption id="TFluna1pins0">SJ-PM-TF-Luna+A01 interface</caption>
<tr><th>TFLuna <th>Pin <th>Description
<tr><td>1 <td>Red 5V
<tr><td>2 <td>Serial TxD: PA8/PA17/PB4/PB6 is UART1 Tx (MSPM0 to TFLuna1)
<tr><td>3 <td>Serial RxD: PA9/PA18/PB5/PB7 is UART1 Rx (TFLuna1 to MSPM0)
<tr><td>4 <td>black ground
</table>
* @{*/
/**
* @file TFLuna1.h
* @brief Initialize TFLuna1, interrupt synchronization
* @details TFLuna1 initialization. UART1 115200 bps baud,
* 1 start, 8 data bits, 1 stop, no parity.<br>
* @version RTOS
* @author Jonathan Valvano
* @copyright Valvano 2025
* @warning AS-IS
* @note For more information see http://users.ece.utexas.edu/~valvano/
* @date Oct 23, 2025
<table>
<caption id="TFluna1pins1">SJ-PM-TF-Luna+A01 interface</caption>
<tr><th>TFLuna <th>Pin <th>Description
<tr><td>1 <td>Red 5V
<tr><td>2 <td>Serial TxD: PA8/PA17/PB4/PB6 is UART1 Tx (MSPM0 to TFLuna1)
<tr><td>3 <td>Serial RxD: PA9/PA18/PB5/PB7 is UART1 Rx (TFLuna1 to MSPM0)
<tr><td>4 <td>black ground
</table>
******************************************************************************/
#ifndef __TFLuna1_H__
#define __TFLuna1_H__
/**
* initialize UART1 for 115200 bps baud rate.<br>
* to use PA9, set jumper J14<br>
* interrupt synchronization
* @param function pointer to a callback function
* @return none
* @brief Initialize TFLuna1
*/
void TFLuna1_Init(void (*function)(uint32_t));
/**
* Configure TFLuna for measuring distance in mm<br>
* @param none
* @return none
* @brief mm units
*/
void TFLuna1_Format_Standard_mm(void);
/**
* Configure TFLuna for measuring distance in cm<br>
* @param none
* @return none
* @brief cm units
*/
void TFLuna1_Format_Standard_cm(void);
/**
* Configure TFLuna for Pixhawk<br>
* @param none
* @return none
* @brief Pixhawk
* @warning see datasheet, never tested
*/
void TFLuna1_Format_Pixhawk(void);
/**
* Configure TFLuna sampling rate<br>
* rate defined in #define TFLunaRate
* @param none
* @return none
* @brief sampling rate
* @warning only 100 Hz was tested
*/
void TFLuna1_Frame_Rate(void);
/**
* execute TFLuna_SaveSettings to activate changes to Format and Frame_Rate
* @param none
* @return none
* @brief save format and rate
*/
void TFLuna1_SaveSettings(void);
/**
* execute TFLuna_System_Reset to start measurements
* @param none
* @return none
* @brief start measurements
*/
void TFLuna1_System_Reset(void);
/**
* Enable TFLuna output<br>
* @param none
* @return none
* @brief enable
* @warning I didn't use these because output enabled was default
*/
void TFLuna1_Output_Enable(void);
/**
* Disable TFLuna output<br>
* @param none
* @return none
* @brief disable
* @warning I didn't use these because output enabled was default
*/
void TFLuna1_Output_Disable(void);
// the following functions only needed for low-level debugging
/**
* Output message to serial port TFLuna<br>
* Uses interrupt synchronization<br>
* msg[0] is 0x5A for command type<br>
* msg[1] is length=n<br>
* msg[2] is command<br>
* msg[3]-msg[n-2] is optional payload<br>
* msg[n-1] is 8-bit checksum<br>
* E.g., 0x5A,0x05,0x05,0x06,0x6A sets format to mm<br>
* This function waits if the transmit software FIFO is full
* @param msg pointer to a message to be transferred
* @return none
* @brief output message to TFLuna
*/
void TFLuna1_SendMessage(const uint8_t msg[]);
/**
* Wait for new serial port input<br>
* Uses interrupt synchronization<br>
* This function waits if the receive software FIFO is empty
* @param Input: none
* @return 8-bit code from TF Luna
*/
uint8_t TFLuna1_InChar(void);
/**
* Returns how much data available for reading
* @param none
* @return number of elements in receive software FIFO
*/
uint32_t TFLuna1_InStatus(void);
/**
* Returns how many bytes are in the transmission software FIFO
* @param none
* @return number of elements in transmission software FIFO
*/
uint32_t TFLuna1_OutStatus(void);
/**
* Output string to serial port TFLuna<br>
* Uses interrupt synchronization<br>
* This function waits if the transmit software FIFO is full
* @param pt pointer to a NULL-terminated string to be transferred
* @return none
* @brief output string to TFLuna
*/
void TFLuna1_OutString(uint8_t *pt);
/**
* Output 8-bit to serial port TFLuna<br>
* Uses interrupt synchronization<br>
* This function waits if the transmit software FIFO is full
* @param data is an 8-bit ASCII character to be transferred
* @return none
* @brief output character to TFLuna
*/
void TFLuna1_OutChar(uint8_t data);
#endif // __TFLuna1_H__
/** @}*/

414
RTOS_Labs_common/TFLuna2.c Normal file
View File

@@ -0,0 +1,414 @@
/* TFLuna2.c
* Jonathan Valvano
* Date: 12/12/2025
TF Luna TOF distance sensor
*/
#include <ti/devices/msp/msp.h>
#include "../RTOS_Labs_common/TFLuna2.h"
#include "../inc/Clock.h"
#include "../inc/LaunchPad.h"
// There are four possible sets of UART2 pins that can be used
// Select the TFLUNA2_RX/TFLUNA2_TX for the UART2 pins to use
// overhead of TFLuna will be 17us/measurement at 100 measurements/sec
// SJ-PM-TF-Luna+A01 on RTOS sensor board
// Pin
// 1 Red 5V
// 2 Serial TxD: PB17 is UART2 Tx (MSPM0 to TFLuna2)
// 3 Serial RxD: PB18 is UART2 Rx (TFLuna2 to MSPM0)
// 4 black ground
#define TFLUNA2_RX PB18INDEX
#define TFLUNA2_TX PB17INDEX
// SJ-PM-TF-Luna+A01
// Pin
// 1 Red 5V
// 2 Serial TxD: PA21 is UART2 Tx (MSPM0 to TFLuna2)
// 3 Serial RxD: PA22 is UART2 Rx (TFLuna2 to MSPM0)
// 4 black ground
//#define TFLUNA2_RX PA22INDEX
//#define TFLUNA2_TX PA21INDEX
// SJ-PM-TF-Luna+A01
// Pin
// 1 Red 5V
// 2 Serial TxD: PA23 is UART2 Tx (MSPM0 to TFLuna2)
// 3 Serial RxD: PA24 is UART2 Rx (TFLuna2 to MSPM0)
// 4 black ground
//#define TFLUNA2_RX PA24INDEX
//#define TFLUNA2_TX PA23INDEX
// SJ-PM-TF-Luna+A01
// Pin
// 1 Red 5V
// 2 Serial TxD: PB15 is UART2 Tx (MSPM0 to TFLuna2)
// 3 Serial RxD: PB16 is UART2 Rx (TFLuna2 to MSPM0)
// 4 black ground
//#define TFLUNA2_RX PB16INDEX
//#define TFLUNA2_TX PB15INDEX
uint32_t LostData2;
#define FIFO2_SIZE 16 // usable size is 15
// prototypes for private functions
void Tx2Fifo_Init(void);
uint32_t Tx2Fifo_Put(uint8_t data);
uint32_t Tx2Fifo_Get(uint8_t *datapt);
void Rx2Fifo_Init(void);
uint32_t Rx2Fifo_Put(uint8_t data);
uint32_t Rx2Fifo_Get(uint8_t *datapt);
int TFLuna2Index;
// 0 for looking for two 59s
// 2-8 filling the TFLunaDataMessage with 9-byte message
void (*TFLuna2Function)(uint32_t); // data in mm
uint8_t TFLuna2LastByte;
uint32_t TFLuna2Distance; // in mm
uint8_t TFLuna2DataMessage[12]; // 9 byte fixed size message
int BadCheckSum2; // errors
// defined in TFLunaCommon
extern const uint8_t ObtainFirmware[4];
// expected response is 0x5A,0x07,0x01,a,b,c,SU version c.b.a
extern const uint8_t System_Reset[4];
// expected response is 0x5A,0x05,0x02,0x00, 0x60 for success
// expected response is 0x5A,0x05,0x02,0x01, 0x61 for failed
extern const uint8_t Frame_Rate[6];
extern const uint8_t Trigger[4];
extern const uint8_t Format_Standard_cm[5];
extern const uint8_t Format_Pixhawk[5];
extern const uint8_t Format_Standard_mm[5];
extern const uint8_t Output_Enable[5];
extern const uint8_t Output_Disable[5];
extern const uint8_t SaveSettings[4];
// power Domain PD0
// for 80MHz bus clock, UART clock is ULPCLK 40MHz
//------------TFLuna_Init------------
// Initialize the UART2 for 115,200 baud rate (assuming 80 MHz clock),
// 8 bit word length, no parity bits, one stop bit, FIFOs enabled
// Input: function 0 for debug, user function for real time
// Output: none
void TFLuna2_Init(void (*function)(uint32_t)){
// do not reset or activate PortA, already done in LaunchPad_Init
// RSTCLR to GPIOA and UART2 peripherals
// bits 31-24 unlock key 0xB1
// bit 1 is Clear reset sticky bit
// bit 0 is reset gpio port
// GPIOA->GPRCM.RSTCTL = (uint32_t)0xB1000003; // called previously
UART2->GPRCM.RSTCTL = 0xB1000003;
// Enable power to GPIOA and UART2 peripherals
// PWREN
// bits 31-24 unlock key 0x26
// bit 0 is Enable Power
// GPIOA->GPRCM.PWREN = (uint32_t)0x26000001; // called previously
UART2->GPRCM.PWREN = 0x26000001;
Clock_Delay(24); // time for uart to power up
// the following code selects which pins to use
IOMUX->SECCFG.PINCM[TFLUNA2_RX] = 0x00040082;
//bit 18 INENA input enable
//bit 7 PC connected
//bits 5-0=2 for UART2_Rx
// configure alternate UART2 transmit function
IOMUX->SECCFG.PINCM[TFLUNA2_TX] = 0x00000082;
//bit 7 PC connected
//bits 5-0=2 for UART2_Tx
UART2->CLKSEL = 0x08; // bus clock
UART2->CLKDIV = 0x00; // no divide
UART2->CTL0 &= ~0x01; // disable UART2
UART2->CTL0 = 0x00020018;
// bit 17 FEN=1 enable FIFO
// bits 16-15 HSE=00 16x oversampling
// bit 14 CTSEN=0 no CTS hardware
// bit 13 RTSEN=0 no RTS hardware
// bit 12 RTS=0 not RTS
// bits 10-8 MODE=000 normal
// bits 6-4 TXE=001 enable TxD
// bit 3 RXE=1 enable TxD
// bit 2 LBE=0 no loop back
// bit 0 ENABLE 0 is disable, 1 to enable
if(Clock_Freq() == 40000000){
// 20000000/16 = 1,250,000 Hz
// Baud = 115200
// 1,250,000/115200 = 10.8506944444
// divider = 10+54/64 = 10.84375
UART2->IBRD = 10;
UART2->FBRD = 54; // baud =1,250,000/10.84375 = 115,273.77
}else if (Clock_Freq() == 32000000){
// 32000000/16 = 2,000,000
// Baud = 115200
// 2,000,000/115200 = 17.3611111
// divider = 21+23/64 = 17.359375
UART2->IBRD = 17;
UART2->FBRD = 23; // 115,211.52
}else if (Clock_Freq() == 80000000){
// 40000000/16 = 2,500,000 Hz
// Baud = 115200
// 2,500,000/115200 = 21.701388
// divider = 21+45/64 = 21.703125
UART2->IBRD = 21;
UART2->FBRD = 45; // baud =2,500,000/21.703125 = 115,191
}else return;
TFLuna2Function = function;
TFLuna2Index = 0; // looking for two 59s
TFLuna2LastByte = 0;
TFLuna2DataMessage[0] = 0x59;
TFLuna2DataMessage[1] = 0x59;
BadCheckSum2 = 0;
Tx2Fifo_Init();
Rx2Fifo_Init();
LostData2 = 0;
UART2->LCRH = 0x00000030;
// bits 5-4 WLEN=11 8 bits
// bit 3 STP2=0 1 stop
// bit 2 EPS=0 parity select
// bit 1 PEN=0 no parity
// bit 0 BRK=0 no break
UART2->CPU_INT.IMASK = 0x0C01;
// bit 11 TXINT yes
// bit 10 RXINT yes
// bit 0 Receive timeout, yes
UART2->IFLS = 0x0422;
// bits 11-8 RXTOSEL receiver timeout select 4 (0xF highest)
// bits 6-4 RXIFLSEL 2 is greater than or equal to half
// bits 2-0 TXIFLSEL 2 is less than or equal to half
NVIC->ICPR[0] = 1<<14; // UART2 is IRQ 13
NVIC->ISER[0] = 1<<14;
NVIC->IP[3] = (NVIC->IP[3]&(~0x00FF0000))|(1<<22); // set priority (bits 23-22) IRQ 14
UART2->CTL0 |= 0x01; // enable UART2
}
// copy from hardware RX FIFO to software RX FIFO
// stop when hardware RX FIFO is empty or software RX FIFO is full
void static copyHardwareToSoftware2(void){
uint8_t letter;
if(TFLuna2Function==0){ // raw data mode
while(((UART2->STAT&0x04) == 0)&&(TFLuna2_InStatus() < (FIFO2_SIZE - 1))){
letter = UART2->RXDATA;
Rx2Fifo_Put(letter);
}
}else{
while((UART2->STAT&0x04)==0){
letter = UART2->RXDATA;
if(TFLuna2Index == 0){
if((letter == 0x59)&&(TFLuna2LastByte == 0x59)){
TFLuna2Index = 2; // looking for message
}
TFLuna2LastByte = letter;
}else{
TFLuna2DataMessage[TFLuna2Index] = letter;
TFLuna2Index++;
if(TFLuna2Index == 9){
TFLuna2LastByte = 0; // get ready for next message
TFLuna2Index = 0;
uint8_t check=0;
for(int i=0;i<8;i++){
check += TFLuna2DataMessage[i];
}
if(check == TFLuna2DataMessage[8]){
TFLuna2Distance = TFLuna2DataMessage[3]*256+TFLuna2DataMessage[2];
// call back
(*TFLuna2Function)(TFLuna2Distance); // pass distance back to higher level
}else{
BadCheckSum2++; // error
}
}
}
}
}
}
//------------TFLuna2_InChar------------
// Wait for new serial port input
// Input: none
// Output: ASCII code for key typed
uint8_t TFLuna2_InChar(void){uint32_t status;
uint8_t letter;
do{
status = Rx2Fifo_Get(&letter);
}while(status==0);
return(letter);
}
// copy from software TX FIFO to hardware TX FIFO
// stop when software TX FIFO is empty or hardware TX FIFO is full
void static copySoftwareToHardware2(void){
uint8_t letter;
while(((UART2->STAT&0x80) == 0) && (TFLuna2_OutStatus() > 0)){
Tx2Fifo_Get(&letter);
UART2->TXDATA = letter;
}
}
//------------UART2_OutChar------------
// Output 8-bit to serial port
// Input: letter is an 8-bit ASCII character to be transferred
// Output: none
void TFLuna2_OutChar(uint8_t data){
while(Tx2Fifo_Put(data) == 0){};
UART2->CPU_INT.IMASK &= ~0x0800; // disarm TX FIFO interrupt
copySoftwareToHardware2();
UART2->CPU_INT.IMASK |= 0x0800; // rearm TX FIFO interrupt
}
//------------TFLuna_OutString------------
// Output String (NULL termination)
// Input: pointer to a NULL-terminated string to be transferred
// Output: none
void TFLuna2_OutString(uint8_t *pt){
while(*pt){
TFLuna2_OutChar(*pt);
pt++;
}
}
//------------TFLuna2_SendMessage------------
// Output message, msg[1] is length
// Input: pointer to message to be transferred
// Output: none
void TFLuna2_SendMessage(const uint8_t msg[]){
for(int i=0; i<msg[1]; i++){
TFLuna2_OutChar(msg[i]);
}
}
void TFLuna2_Format_Standard_mm(void){
TFLuna2_SendMessage(Format_Standard_mm);
}
void TFLuna2_Format_Standard_cm(void){
TFLuna2_SendMessage(Format_Standard_cm);
}
void TFLuna2_Format_Pixhawk(void){
TFLuna2_SendMessage(Format_Pixhawk);
}
void TFLuna2_Output_Enable(void){
TFLuna2_SendMessage(Output_Enable);
}
void TFLuna2_Output_Disable(void){
TFLuna2_SendMessage(Output_Disable);
}
void TFLuna2_Frame_Rate(void){
TFLuna2_SendMessage(Frame_Rate);
}
void TFLuna2_SaveSettings(void){
TFLuna2_SendMessage(SaveSettings);
}
void TFLuna2_System_Reset(void){
TFLuna2_SendMessage(System_Reset);
}
void UART2_IRQHandler(void){ uint32_t status;
status = UART2->CPU_INT.IIDX; // reading clears bit in RIS
if(status == 0x01){ // 0x01 receive timeout
copyHardwareToSoftware2();
}else if(status == 0x0B){ // 0x0B receive
copyHardwareToSoftware2();
}else if(status == 0x0C){ // 0x0C transmit
copySoftwareToHardware2();
if(TFLuna2_OutStatus() == 0){ // software TX FIFO is empty
UART2->CPU_INT.IMASK &= ~0x0800; // disable TX FIFO interrupt
}
}
}
// Declare state variables for FiFo
// size, buffer, put and get indexes
int32_t static Tx2PutI; // Index to put new
int32_t static Tx2GetI; // Index of oldest
uint8_t static Tx2Fifo[FIFO2_SIZE];
// *********** Tx2Fifo_Init**********
// Initializes a software Tx2FIFO of a
// fixed size and sets up indexes for
// put and get operations
void Tx2Fifo_Init(void){
Tx2PutI = Tx2GetI = 0;
}
// *********** Tx2Fifo_Put**********
// Adds an element to the Tx2FIFO
// Input: data is character to be inserted
// Output: 1 for success, data properly saved
// 0 for failure, TxFIFO is full
uint32_t Tx2Fifo_Put(uint8_t data){
if(((Tx2PutI+1)&(FIFO2_SIZE-1)) == Tx2GetI){
return 0;
}
Tx2Fifo[Tx2PutI] = data;
Tx2PutI = (Tx2PutI+1)&(FIFO2_SIZE-1);
return 1; // success
}
// *********** Tx2Fifo_Get**********
// Gets an element from the Tx2FIFO
// Input: pointer to empty 8-bit variable
// Output: If the Tx2FIFO is empty return 0
// If the Tx2FIFO has data, remove it, and put in *datapt, return 1
uint32_t Tx2Fifo_Get(uint8_t *datapt){
if(Tx2GetI == Tx2PutI){
return 0;
}
*datapt = Tx2Fifo[Tx2GetI];
Tx2GetI = (Tx2GetI+1)&(FIFO2_SIZE-1);
return 1; // success
}
//------------TFLuna2_OutStatus------------
// Returns how much data available for reading from Tx2 FIFO
// Input: none
// Output: number of elements in receive FIFO
uint32_t TFLuna2_OutStatus(void){
return ((Tx2PutI - Tx2GetI)&(FIFO2_SIZE-1));
}
int32_t static Rx2PutI; // Index to put new
int32_t static Rx2GetI; // Index of oldest
uint8_t static Rx2Fifo[FIFO2_SIZE];
// *********** Rx2Fifo_Init**********
// Initializes a software RxFIFO of a
// fixed size and sets up indexes for
// put and get operations
void Rx2Fifo_Init(void){
Rx2PutI = Rx2GetI = 0;
}
// *********** Rx2Fifo_Put**********
// Adds an element to the Rx2FIFO
// Input: data is character to be inserted
// Output: 1 for success, data properly saved
// 0 for failure, RxFIFO is full
uint32_t Rx2Fifo_Put(uint8_t data){
if(((Rx2PutI+1)&(FIFO2_SIZE-1)) == Rx2GetI){
return 0;
}
Rx2Fifo[Rx2PutI] = data;
Rx2PutI = (Rx2PutI+1)&(FIFO2_SIZE-1);
return 1;
}
// *********** Rx2Fifo_Get**********
// Gets an element from the Rx2FIFO
// Input: pointer to empty 8-bit variable
// Output: If the Rx2FIFO is empty return 0
// If the Rx2FIFO has data, remove it, and put in *datapt, return 1
uint32_t Rx2Fifo_Get(uint8_t *datapt){
if(Rx2GetI == Rx2PutI){
return 0;
}
*datapt = Rx2Fifo[Rx2GetI];
Rx2GetI = (Rx2GetI+1)&(FIFO2_SIZE-1);
return 1;
}
//------------TFLuna2_InStatus------------
// Returns how much data available for reading from Rx2 FIFO
// Input: none
// Output: number of elements in receive FIFO
uint32_t TFLuna2_InStatus(void){
return ((Rx2PutI - Rx2GetI)&(FIFO2_SIZE-1));
}

183
RTOS_Labs_common/TFLuna2.h Normal file
View File

@@ -0,0 +1,183 @@
/*!
* @defgroup TFLuna
* @brief Asynchronous serial communication to TFLuna2
<table>
<caption id="TFLuna2pins0">SJ-PM-TF-Luna+A01 interface</caption>
<tr><th>TFLuna <th>Pin <th>Description
<tr><td>1 <td>Red 5V
<tr><td>2 <td>Serial TxD: PA21/PA23/PB15/PB17 is UART2 Tx (MSPM0 to TFLuna2)
<tr><td>3 <td>Serial RxD: PA22/PA24/PB16/PB18 is UART2 Rx (TFLuna2 to MSPM0)
<tr><td>4 <td>black ground
</table>
* @{*/
/**
* @file TFLuna2.h
* @brief Initialize TFLuna2, interrupt synchronization
* @details TFLuna2 initialization. UART2 115200 bps baud,
* 1 start, 8 data bits, 1 stop, no parity.<br>
* @version RTOS
* @author Jonathan Valvano
* @copyright Valvano 2025
* @warning AS-IS
* @note For more information see http://users.ece.utexas.edu/~valvano/
* @date Oct 23, 2025
<table>
<caption id="TFLuna2pins1">SJ-PM-TF-Luna+A01 interface</caption>
<tr><th>TFLuna <th>Pin <th>Description
<tr><td>1 <td>Red 5V
<tr><td>2 <td>Serial TxD: PA21/PA23/PB15/PB17 is UART2 Tx (MSPM0 to TFLuna2)
<tr><td>3 <td>Serial RxD: PA22/PA24/PB16/PB18 is UART2 Rx (TFLuna2 to MSPM0)
<tr><td>4 <td>black ground
</table>
******************************************************************************/
#ifndef __TFLuna2_H__
#define __TFLuna2_H__
/**
* initialize UART2 for 115200 bps baud rate.<br>
* to use PA9, set jumper J14<br>
* interrupt synchronization
* @param function pointer to a callback function
* @return none
* @brief Initialize TFLuna2
*/
void TFLuna2_Init(void (*function)(uint32_t));
/**
* Configure TFLuna2 for measuring distance in mm<br>
* @param none
* @return none
* @brief mm units
*/
void TFLuna2_Format_Standard_mm(void);
/**
* Configure TFLuna2 for measuring distance in cm<br>
* @param none
* @return none
* @brief cm units
*/
void TFLuna2_Format_Standard_cm(void);
/**
* Configure TFLuna2 for Pixhawk<br>
* @param none
* @return none
* @brief Pixhawk
* @warning see datasheet, never tested
*/
void TFLuna2_Format_Pixhawk(void);
/**
* Configure TFLuna2 sampling rate<br>
* rate defined in #define TFLunaRate
* @param none
* @return none
* @brief sampling rate
* @warning only 100 Hz was tested
*/
void TFLuna2_Frame_Rate(void);
/**
* execute TFLuna2_SaveSettings to activate changes to Format and Frame_Rate
* @param none
* @return none
* @brief save format and rate
*/
void TFLuna2_SaveSettings(void);
/**
* execute TFLuna2_System_Reset to start measurements
* @param none
* @return none
* @brief start measurements
*/
void TFLuna2_System_Reset(void);
/**
* Enable TFLuna output<br>
* @param none
* @return none
* @brief enable
* @warning I didn't use these because output enabled was default
*/
void TFLuna2_Output_Enable(void);
/**
* Disable TFLuna output<br>
* @param none
* @return none
* @brief disable
* @warning I didn't use these because output enabled was default
*/
void TFLuna2_Output_Disable(void);
// the following functions only needed for low-level debugging
/**
* Output message to serial port TFLuna2<br>
* Uses interrupt synchronization<br>
* msg[0] is 0x5A for command type<br>
* msg[1] is length=n<br>
* msg[2] is command<br>
* msg[3]-msg[n-2] is optional payload<br>
* msg[n-1] is 8-bit checksum<br>
* E.g., 0x5A,0x05,0x05,0x06,0x6A sets format to mm<br>
* This function waits if the transmit software FIFO is full
* @param msg pointer to a message to be transferred
* @return none
* @brief output message to TFLuna2
*/
void TFLuna2_SendMessage(const uint8_t msg[]);
/**
* Wait for new serial port input<br>
* Uses interrupt synchronization<br>
* This function waits if the receive software FIFO is empty
* @param Input: none
* @return 8-bit code from TFLuna2
*/
uint8_t TFLuna2_InChar(void);
/**
* Returns how much data available for reading
* @param none
* @return number of elements in receive software FIFO
*/
uint32_t TFLuna2_InStatus(void);
/**
* Returns how many bytes are in the transmission software FIFO
* @param none
* @return number of elements in transmission software FIFO
*/
uint32_t TFLuna2_OutStatus(void);
/**
* Output string to serial port TFLuna2<br>
* Uses interrupt synchronization<br>
* This function waits if the transmit software FIFO is full
* @param pt pointer to a NULL-terminated string to be transferred
* @return none
* @brief output string to TFLuna2
*/
void TFLuna2_OutString(uint8_t *pt);
/**
* Output 8-bit to serial port TFLuna2<br>
* Uses interrupt synchronization<br>
* This function waits if the transmit software FIFO is full
* @param data is an 8-bit ASCII character to be transferred
* @return none
* @brief output character to TFLuna2
*/
void TFLuna2_OutChar(uint8_t data);
#endif // __TFLuna2_H__
/** @}*/

411
RTOS_Labs_common/TFLuna3.c Normal file
View File

@@ -0,0 +1,411 @@
/* TFLuna3.c
* Jonathan Valvano
* Date: 10/23/2025
TF Luna TOF distance sensor
*/
#include <ti/devices/msp/msp.h>
#include "../RTOS_Labs_common/TFLuna3.h"
#include "../inc/Clock.h"
#include "../inc/LaunchPad.h"
// There are four possible sets of UART3 pins that can be used
// Select the TFLuna3_RX/TFLuna3_TX for the UART3 pins to use
// overhead of TFLuna will be 17us/measurement at 100 measurements/sec
// UART3 is shared between LD19 and TFLuna3 (can have either but not both)
// SJ-PM-TF-Luna+A01 on RTOS sensor board
// Pin
// 1 Red 5V
// 2 Serial TxD: PB12 is UART3 Tx (MSPM0 to TFLuna3)
// 3 Serial RxD: PB13 is UART3 Rx (TFLuna3 to MSPM0)
// 4 black ground
#define TFLUNA3_RX PB13INDEX
#define TFLUNA3_TX PB12INDEX
// Can use PA13 and PA14, but different PINCH values
// SJ-PM-TF-Luna+A01
// Pin
// 1 Red 5V
// 2 Serial TxD: PA26 is UART3 Tx (MSPM0 to TFLuna3)
// 3 Serial RxD: PA25 is UART3 Rx (TFLuna3 to MSPM0)
// 4 black ground
//#define TFLUNA3_RX PA25INDEX
//#define TFLUNA3_TX PA26INDEX
// SJ-PM-TF-Luna+A01
// Pin
// 1 Red 5V
// 2 Serial TxD: PB2 is UART3 Tx (MSPM0 to TFLuna3)
// 3 Serial RxD: PB3 is UART3 Rx (TFLuna3 to MSPM0)
// 4 black ground
//#define TFLUNA3_RX PB3INDEX
//#define TFLUNA3_TX PB2INDEX
uint32_t LostData3;
#define FIFO3_SIZE 16 // usable size is 15
// prototypes for private functions
void Tx3Fifo_Init(void);
uint32_t Tx3Fifo_Put(uint8_t data);
uint32_t Tx3Fifo_Get(uint8_t *datapt);
void Rx3Fifo_Init(void);
uint32_t Rx3Fifo_Put(uint8_t data);
uint32_t Rx3Fifo_Get(uint8_t *datapt);
int TFLuna3Index;
// 0 for looking for two 59s
// 2-8 filling the TFLunaDataMessage with 9-byte message
void (*TFLuna3Function)(uint32_t); // data in mm
uint8_t TFLuna3LastByte;
uint32_t TFLuna3Distance; // in mm
uint8_t TFLuna3DataMessage[12]; // 9 byte fixed size message
int BadCheckSum3; // errors
// defined in TFLunaCommon
extern const uint8_t ObtainFirmware[4];
// expected response is 0x5A,0x07,0x01,a,b,c,SU version c.b.a
extern const uint8_t System_Reset[4];
// expected response is 0x5A,0x05,0x02,0x00, 0x60 for success
// expected response is 0x5A,0x05,0x02,0x01, 0x61 for failed
extern const uint8_t Frame_Rate[6];
extern const uint8_t Trigger[4];
extern const uint8_t Format_Standard_cm[5];
extern const uint8_t Format_Pixhawk[5];
extern const uint8_t Format_Standard_mm[5];
extern const uint8_t Output_Enable[5];
extern const uint8_t Output_Disable[5];
extern const uint8_t SaveSettings[4];
// power Domain PD1
// for 32MHz bus clock, UART clock is 32MHz
// for 40MHz bus clock, UART clock is MCLK 40MHz
// for 80MHz bus clock, UART clock is MCLK 80MHz
//------------TFLuna_Init------------
// Initialize the UART3 for 115,200 baud rate (assuming 80 MHz clock),
// 8 bit word length, no parity bits, one stop bit, FIFOs enabled
// Input: function 0 for debug, user function for real time
// Output: none
void TFLuna3_Init(void (*function)(uint32_t)){
// do not reset or activate PortA, already done in LaunchPad_Init
// RSTCLR to GPIOA and UART3 peripherals
// bits 31-24 unlock key 0xB1
// bit 1 is Clear reset sticky bit
// bit 0 is reset gpio port
// GPIOA->GPRCM.RSTCTL = (uint32_t)0xB1000003; // called previously
UART3->GPRCM.RSTCTL = 0xB1000003;
// Enable power to GPIOA and UART3 peripherals
// PWREN
// bits 31-24 unlock key 0x26
// bit 0 is Enable Power
// GPIOA->GPRCM.PWREN = (uint32_t)0x26000001; // called previously
UART3->GPRCM.PWREN = 0x26000001;
Clock_Delay(24); // time for uart to power up
// the following code selects which pins to use
IOMUX->SECCFG.PINCM[TFLUNA3_RX] = 0x00040082;
//bit 18 INENA input enable
//bit 7 PC connected
//bits 5-0=2 for UART3_Rx
// configure alternate UART3 transmit function
IOMUX->SECCFG.PINCM[TFLUNA3_TX] = 0x00000082;
//bit 7 PC connected
//bits 5-0=2 for UART3_Tx
UART3->CLKSEL = 0x08; // bus clock
UART3->CLKDIV = 0x00; // no divide
UART3->CTL0 &= ~0x01; // disable UART3
UART3->CTL0 = 0x00020018;
// bit 17 FEN=1 enable FIFO
// bits 16-15 HSE=00 16x oversampling
// bit 14 CTSEN=0 no CTS hardware
// bit 13 RTSEN=0 no RTS hardware
// bit 12 RTS=0 not RTS
// bits 10-8 MODE=000 normal
// bits 6-4 TXE=001 enable TxD
// bit 3 RXE=1 enable TxD
// bit 2 LBE=0 no loop back
// bit 0 ENABLE 0 is disable, 1 to enable
if(Clock_Freq() == 40000000){
// 40000000/16 = 2,500,000 Hz
// Baud = 115200
// 2,500,000/115200 = 21.701388888
// divider = 21+45/64 = 21.703125
UART3->IBRD = 21;
UART3->FBRD = 45; // baud =2,500,000/10.84375 = 115,273.77
}else if (Clock_Freq() == 32000000){
// 32000000/16 = 2,000,000
// Baud = 115200
// 2,000,000/115200 = 17.3611111
// divider = 21+23/64 = 17.359375
UART3->IBRD = 17;
UART3->FBRD = 23; // 115,211.52
}else if (Clock_Freq() == 80000000){
// 80000000/16 = 5,000,000 Hz
// Baud = 115200
// 5,000,000/115200 = 43.4027777
// divider = 43+26/64 = 43.40625
UART3->IBRD = 43;
UART3->FBRD = 26; // baud =5,000,000/43.40625 = 115,190.78
}else return;
TFLuna3Function = function;
TFLuna3Index = 0; // looking for two 59s
TFLuna3LastByte = 0;
TFLuna3DataMessage[0] = 0x59;
TFLuna3DataMessage[1] = 0x59;
BadCheckSum3 = 0;
Tx3Fifo_Init();
Rx3Fifo_Init();
LostData3 = 0;
UART3->LCRH = 0x00000030;
// bits 5-4 WLEN=11 8 bits
// bit 3 STP2=0 1 stop
// bit 2 EPS=0 parity select
// bit 1 PEN=0 no parity
// bit 0 BRK=0 no break
UART3->CPU_INT.IMASK = 0x0C01;
// bit 11 TXINT
// bit 10 RXINT yes
// bit 0 Receive timeout, yes
UART3->IFLS = 0x0422;
// bits 11-8 RXTOSEL receiver timeout select 4 (0xF highest)
// bits 6-4 RXIFLSEL 2 is greater than or equal to half
// bits 2-0 TXIFLSEL 2 is less than or equal to half
NVIC->ICPR[0] = 1<<3; // UART3 is IRQ 3
NVIC->ISER[0] = 1<<3;
NVIC->IP[0] = (NVIC->IP[0]&(~0xFF000000))|(1<<30); // set priority (bits 31-30) IRQ 3
UART3->CTL0 |= 0x01; // enable UART3
}
// copy from hardware RX FIFO to software RX FIFO
// stop when hardware RX FIFO is empty or software RX FIFO is full
void static copyHardwareToSoftware3(void){
uint8_t letter;
if(TFLuna3Function==0){ // raw data mode
while(((UART3->STAT&0x04) == 0)&&(TFLuna3_InStatus() < (FIFO3_SIZE - 1))){
letter = UART3->RXDATA;
Rx3Fifo_Put(letter);
}
}else{
while((UART3->STAT&0x04)==0){
letter = UART3->RXDATA;
if(TFLuna3Index == 0){
if((letter == 0x59)&&(TFLuna3LastByte == 0x59)){
TFLuna3Index = 2; // looking for message
}
TFLuna3LastByte = letter;
}else{
TFLuna3DataMessage[TFLuna3Index] = letter;
TFLuna3Index++;
if(TFLuna3Index == 9){
TFLuna3LastByte = 0; // get ready for next message
TFLuna3Index = 0;
uint8_t check=0;
for(int i=0;i<8;i++){
check += TFLuna3DataMessage[i];
}
if(check == TFLuna3DataMessage[8]){
TFLuna3Distance = TFLuna3DataMessage[3]*256+TFLuna3DataMessage[2];
// call back
(*TFLuna3Function)(TFLuna3Distance); // pass distance back to higher level
}else{
BadCheckSum3++; // error
}
}
}
}
}
}
//------------TFLuna3_InChar------------
// Wait for new serial port input
// Input: none
// Output: ASCII code for key typed
uint8_t TFLuna3_InChar(void){uint32_t status;
uint8_t letter;
do{
status = Rx3Fifo_Get(&letter);
}while(status==0);
return(letter);
}
// copy from software TX FIFO to hardware TX FIFO
// stop when software TX FIFO is empty or hardware TX FIFO is full
void static copySoftwareToHardware3(void){
uint8_t letter;
while(((UART3->STAT&0x80) == 0) && (TFLuna3_OutStatus() > 0)){
Tx3Fifo_Get(&letter);
UART3->TXDATA = letter;
}
}
//------------UART3_OutChar------------
// Output 8-bit to serial port
// Input: letter is an 8-bit ASCII character to be transferred
// Output: none
void TFLuna3_OutChar(uint8_t data){
while(Tx3Fifo_Put(data) == 0){};
UART3->CPU_INT.IMASK &= ~0x0800; // disarm TX FIFO interrupt
copySoftwareToHardware3();
UART3->CPU_INT.IMASK |= 0x0800; // rearm TX FIFO interrupt
}
//------------TFLuna_OutString------------
// Output String (NULL termination)
// Input: pointer to a NULL-terminated string to be transferred
// Output: none
void TFLuna3_OutString(uint8_t *pt){
while(*pt){
TFLuna3_OutChar(*pt);
pt++;
}
}
//------------TFLuna3_SendMessage------------
// Output message, msg[1] is length
// Input: pointer to message to be transferred
// Output: none
void TFLuna3_SendMessage(const uint8_t msg[]){
for(int i=0; i<msg[1]; i++){
TFLuna3_OutChar(msg[i]);
}
}
void TFLuna3_Format_Standard_mm(void){
TFLuna3_SendMessage(Format_Standard_mm);
}
void TFLuna3_Format_Standard_cm(void){
TFLuna3_SendMessage(Format_Standard_cm);
}
void TFLuna3_Format_Pixhawk(void){
TFLuna3_SendMessage(Format_Pixhawk);
}
void TFLuna3_Output_Enable(void){
TFLuna3_SendMessage(Output_Enable);
}
void TFLuna3_Output_Disable(void){
TFLuna3_SendMessage(Output_Disable);
}
void TFLuna3_Frame_Rate(void){
TFLuna3_SendMessage(Frame_Rate);
}
void TFLuna3_SaveSettings(void){
TFLuna3_SendMessage(SaveSettings);
}
void TFLuna3_System_Reset(void){
TFLuna3_SendMessage(System_Reset);
}
void UART3_IRQHandler(void){ uint32_t status;
status = UART3->CPU_INT.IIDX; // reading clears bit in RIS
if(status == 0x01){ // 0x01 receive timeout
copyHardwareToSoftware3();
}else if(status == 0x0B){ // 0x0B receive
copyHardwareToSoftware3();
}else if(status == 0x0C){ // 0x0C transmit
copySoftwareToHardware3();
if(TFLuna3_OutStatus() == 0){ // software TX FIFO is empty
UART3->CPU_INT.IMASK &= ~0x0800; // disable TX FIFO interrupt
}
}
}
// Declare state variables for FiFo
// size, buffer, put and get indexes
int32_t static Tx3PutI; // Index to put new
int32_t static Tx3GetI; // Index of oldest
uint8_t static Tx3Fifo[FIFO3_SIZE];
// *********** Tx3Fifo_Init**********
// Initializes a software Tx3FIFO of a
// fixed size and sets up indexes for
// put and get operations
void Tx3Fifo_Init(void){
Tx3PutI = Tx3GetI = 0;
}
// *********** Tx3Fifo_Put**********
// Adds an element to the Tx3FIFO
// Input: data is character to be inserted
// Output: 1 for success, data properly saved
// 0 for failure, TxFIFO is full
uint32_t Tx3Fifo_Put(uint8_t data){
if(((Tx3PutI+1)&(FIFO3_SIZE-1)) == Tx3GetI){
return 0;
}
Tx3Fifo[Tx3PutI] = data;
Tx3PutI = (Tx3PutI+1)&(FIFO3_SIZE-1);
return 1; // success
}
// *********** Tx3Fifo_Get**********
// Gets an element from the Tx3FIFO
// Input: pointer to empty 8-bit variable
// Output: If the Tx3FIFO is empty return 0
// If the Tx3FIFO has data, remove it, and put in *datapt, return 1
uint32_t Tx3Fifo_Get(uint8_t *datapt){
if(Tx3GetI == Tx3PutI){
return 0;
}
*datapt = Tx3Fifo[Tx3GetI];
Tx3GetI = (Tx3GetI+1)&(FIFO3_SIZE-1);
return 1; // success
}
//------------TFLuna3_OutStatus------------
// Returns how much data available for reading from Tx3 FIFO
// Input: none
// Output: number of elements in receive FIFO
uint32_t TFLuna3_OutStatus(void){
return ((Tx3PutI - Tx3GetI)&(FIFO3_SIZE-1));
}
int32_t static Rx3PutI; // Index to put new
int32_t static Rx3GetI; // Index of oldest
uint8_t static Rx3Fifo[FIFO3_SIZE];
// *********** Rx3Fifo_Init**********
// Initializes a software RxFIFO of a
// fixed size and sets up indexes for
// put and get operations
void Rx3Fifo_Init(void){
Rx3PutI = Rx3GetI = 0;
}
// *********** Rx3Fifo_Put**********
// Adds an element to the Rx3FIFO
// Input: data is character to be inserted
// Output: 1 for success, data properly saved
// 0 for failure, RxFIFO is full
uint32_t Rx3Fifo_Put(uint8_t data){
if(((Rx3PutI+1)&(FIFO3_SIZE-1)) == Rx3GetI){
return 0;
}
Rx3Fifo[Rx3PutI] = data;
Rx3PutI = (Rx3PutI+1)&(FIFO3_SIZE-1);
return 1;
}
// *********** Rx3Fifo_Get**********
// Gets an element from the Rx3FIFO
// Input: pointer to empty 8-bit variable
// Output: If the Rx3FIFO is empty return 0
// If the Rx3FIFO has data, remove it, and put in *datapt, return 1
uint32_t Rx3Fifo_Get(uint8_t *datapt){
if(Rx3GetI == Rx3PutI){
return 0;
}
*datapt = Rx3Fifo[Rx3GetI];
Rx3GetI = (Rx3GetI+1)&(FIFO3_SIZE-1);
return 1;
}
//------------TFLuna3_InStatus------------
// Returns how much data available for reading from Rx3 FIFO
// Input: none
// Output: number of elements in receive FIFO
uint32_t TFLuna3_InStatus(void){
return ((Rx3PutI - Rx3GetI)&(FIFO3_SIZE-1));
}

184
RTOS_Labs_common/TFLuna3.h Normal file
View File

@@ -0,0 +1,184 @@
/*!
* @defgroup TFLuna
* @brief Asynchronous serial communication to TFLuna3
<table>
<caption id="TFLuna3pins0">SJ-PM-TF-Luna+A01 interface</caption>
<tr><th>TFLuna <th>Pin <th>Description
<tr><td>1 <td>Red 5V
<tr><td>2 <td>Serial TxD: PA14/PA26/PB2/PB12 is UART3 Tx (MSPM0 to TFLuna3)
<tr><td>3 <td>Serial RxD: PA13/PA25/PB3/PB13 is UART3 Rx (TFLuna3 to MSPM0)
<tr><td>4 <td>black ground
</table>
* @{*/
/**
* @file TFLuna3.h
* @brief Initialize TFLuna3, interrupt synchronization
* @details TFLuna3 initialization. UART3 115200 bps baud,
* 1 start, 8 data bits, 1 stop, no parity.<br>
* @version RTOS
* @author Jonathan Valvano
* @copyright Valvano 2025
* @warning AS-IS
* @note For more information see http://users.ece.utexas.edu/~valvano/
* @date Oct 23, 2025
<table>
<caption id="TFLuna3pins1">SJ-PM-TF-Luna+A01 interface</caption>
<tr><th>TFLuna <th>Pin <th>Description
<tr><td>1 <td>Red 5V
<tr><td>2 <td>Serial TxD: PA21/PA23/PB15/PB17 is UART3 Tx (MSPM0 to TFLuna3)
<tr><td>3 <td>Serial RxD: PA22/PA24/PB16/PB18 is UART3 Rx (TFLuna3 to MSPM0)
<tr><td>4 <td>black ground
</table>
******************************************************************************/
#ifndef __TFLuna3_H__
#define __TFLuna3_H__
/**
* initialize UART3 for 115200 bps baud rate.<br>
* to use PA9, set jumper J14<br>
* interrupt synchronization
* @param function pointer to a callback function
* @return none
* @brief Initialize TFLuna3
*/
void TFLuna3_Init(void (*function)(uint32_t));
/**
* Configure TFLuna3 for measuring distance in mm<br>
* @param none
* @return none
* @brief mm units
*/
void TFLuna3_Format_Standard_mm(void);
/**
* Configure TFLuna3 for measuring distance in cm<br>
* @param none
* @return none
* @brief cm units
*/
void TFLuna3_Format_Standard_cm(void);
/**
* Configure TFLuna3 for Pixhawk<br>
* @param none
* @return none
* @brief Pixhawk
* @warning see datasheet, never tested
*/
void TFLuna3_Format_Pixhawk(void);
/**
* Configure TFLuna3 sampling rate<br>
* rate defined in #define TFLunaRate
* @param none
* @return none
* @brief sampling rate
* @warning only 100 Hz was tested
*/
void TFLuna3_Frame_Rate(void);
/**
* execute TFLuna3_SaveSettings to activate changes to Format and Frame_Rate
* @param none
* @return none
* @brief save format and rate
*/
void TFLuna3_SaveSettings(void);
/**
* execute TFLuna3_System_Reset to start measurements
* @param none
* @return none
* @brief start measurements
*/
void TFLuna3_System_Reset(void);
/**
* Enable TFLuna3 output<br>
* @param none
* @return none
* @brief enable
* @warning I didn't use these because output enabled was default
*/
void TFLuna3_Output_Enable(void);
/**
* Disable TFLuna3 output<br>
* @param none
* @return none
* @brief disable
* @warning I didn't use these because output enabled was default
*/
void TFLuna3_Output_Disable(void);
// the following functions only needed for low-level debugging
/**
* Output message to serial port TFLuna<br>
* Uses interrupt synchronization<br>
* msg[0] is 0x5A for command type<br>
* msg[1] is length=n<br>
* msg[2] is command<br>
* msg[3]-msg[n-2] is optional payload<br>
* msg[n-1] is 8-bit checksum<br>
* E.g., 0x5A,0x05,0x05,0x06,0x6A sets format to mm<br>
* This function waits if the transmit software FIFO is full
* @param msg pointer to a message to be transferred
* @return none
* @brief output message to TFLuna3
*/
void TFLuna3_SendMessage(const uint8_t msg[]);
/**
* Wait for new serial port input<br>
* Uses interrupt synchronization<br>
* This function waits if the receive software FIFO is empty
* @param Input: none
* @return 8-bit code from TFLuna3
*/
uint8_t TFLuna3_InChar(void);
/**
* Returns how much data available for reading
* @param none
* @return number of elements in receive software FIFO
*/
uint32_t TFLuna3_InStatus(void);
/**
* Returns how many bytes are in the transmission software FIFO
* @param none
* @return number of elements in transmission software FIFO
*/
uint32_t TFLuna3_OutStatus(void);
/**
* Output string to serial port TFLuna3<br>
* Uses interrupt synchronization<br>
* This function waits if the transmit software FIFO is full
* @param pt pointer to a NULL-terminated string to be transferred
* @return none
* @brief output string to TFLuna3
*/
void TFLuna3_OutString(uint8_t *pt);
/**
* Output 8-bit to serial port TFLuna3<br>
* Uses interrupt synchronization<br>
* This function waits if the transmit software FIFO is full
* @param data is an 8-bit ASCII character to be transferred
* @return none
* @brief output character to TFLuna3
*/
void TFLuna3_OutChar(uint8_t data);
#endif // __TFLuna3_H__
/** @}*/

View File

@@ -0,0 +1,26 @@
/* TFLunaCommon.c
* Jonathan Valvano
* Date: 10/23/2025
Initialization commands for TF Luna TOF distance sensor
*/
#include <stdint.h>
/**
* Sampling rate in Hz
* @warning only 100 tested
*/
#define TFLunaRate 100
const uint8_t ObtainFirmware[4]={0x5A,0x04,0x01,0x5F};
// expected response is 0x5A,0x07,0x01,a,b,c,SU version c.b.a
const uint8_t System_Reset[4]={0x5A,0x04,0x02,0x60};
// expected response is 0x5A,0x05,0x02,0x00, 0x60 for success
// expected response is 0x5A,0x05,0x02,0x01, 0x61 for failed
#define SU1 ((0x54+0x06+0x3+TFLunaRate)&0xFF)
const uint8_t Frame_Rate[6]={0x5A,0x06,0x03,TFLunaRate,0,SU1};
const uint8_t Trigger[4]={0x5A,0x04,0x04,0x62};
const uint8_t Format_Standard_cm[5]={0x5A,0x05,0x05,0x01,0x66};
const uint8_t Format_Pixhawk[5]={0x5A,0x05,0x05,0x02,0x67};
const uint8_t Format_Standard_mm[5]={0x5A,0x05,0x05,0x06,0x6A};
const uint8_t Output_Enable[5]={0x5A,0x05,0x07,0x00,0x66};
const uint8_t Output_Disable[5]={0x5A,0x05,0x07,0x01,0x67};
const uint8_t SaveSettings[4]={0x5A,0x04,0x11,0x6F};

117
RTOS_Labs_common/UART2.c Normal file
View File

@@ -0,0 +1,117 @@
/* UART2.c
* Your name
* Data:
* PA22 UART2 Rx from other microcontroller PA8 UART1 Tx<br>
*/
#include <ti/devices/msp/msp.h>
#include "../RTOS_Labs_common/UART2.h"
#include "../inc/Clock.h"
#include "../inc/LaunchPad.h"
uint32_t LostData2;
// power Domain PD0
// for 80MHz bus clock, UART clock is ULPCLK 40MHz
// initialize UART2 for 2000 baud rate
// no transmit, interrupt on receive timeout
void UART2_Init(void){
// do not reset or activate PortA, already done in LaunchPad_Init
// RSTCLR to GPIOA and UART2 peripherals
// bits 31-24 unlock key 0xB1
// bit 1 is Clear reset sticky bit
// bit 0 is reset gpio port
// GPIOA->GPRCM.RSTCTL = (uint32_t)0xB1000003; // called previously
UART2->GPRCM.RSTCTL = 0xB1000003;
// Enable power to GPIOA and UART1 peripherals
// PWREN
// bits 31-24 unlock key 0x26
// bit 0 is Enable Power
// GPIOA->GPRCM.PWREN = (uint32_t)0x26000001; // called previously
UART2->GPRCM.PWREN = 0x26000001;
Clock_Delay(24); // time for uart to power up
// configure PA22 PA8 as alternate UART2 function
IOMUX->SECCFG.PINCM[PA22INDEX] = 0x00040082;
//bit 18 INENA input enable
//bit 7 PC connected
//bits 5-0=2 for UART1_Rx
Fifo1_Init();
LostData2 = 0;
UART2->CLKSEL = 0x08; // bus clock
UART2->CLKDIV = 0x00; // no divide
UART2->CTL0 &= ~0x01; // disable UART1
UART2->CTL0 = 0x00020018;
// bit 17 FEN=1 enable FIFO
// bits 16-15 HSE=00 16x oversampling
// bit 14 CTSEN=0 no CTS hardware
// bit 13 RTSEN=0 no RTS hardware
// bit 12 RTS=0 not RTS
// bits 10-8 MODE=000 normal
// bits 6-4 TXE=001 enable TxD
// bit 3 RXE=1 enable TxD
// bit 2 LBE=0 no loop back
// bit 0 ENABLE 0 is disable, 1 to enable
if(Clock_Freq() == 40000000){
// 20000000/16 = 1,250,000 Hz
// Baud = 2000
// 1,250,000/2000 = 625
// divider = 625
UART2->IBRD = 625;
UART2->FBRD = 0; // baud =1,250,000/625 = 2000
}else if (Clock_Freq() == 32000000){
// 32000000/16 = 2,000,000
// Baud = 2000
// 2,000,000/2000 = 1000
// divider = 1000
UART2->IBRD = 1000;
UART2->FBRD = 0;
}else if (Clock_Freq() == 80000000){
// 40000000/16 = 2,500,000 Hz
// Baud = 2000
// 2,500,000/2000 = 1250
// divider = 1250
UART2->IBRD = 1250;
UART2->FBRD = 0; // baud =2,500,000/1250 = 2000
}else return;
UART2->LCRH = 0x00000030;
// bits 5-4 WLEN=11 8 bits
// bit 3 STP2=0 1 stop
// bit 2 EPS=0 parity select
// bit 1 PEN=0 no parity
// bit 0 BRK=0 no break
UART2->CPU_INT.IMASK = 0x0001;
// bit 11 TXINT=0 no transmit interrupt
// bit 10 RXINT=0 no receive interrupt
// bit 0 Receive timeout
UART2->IFLS = 0x0422;
// bits 11-8 RXTOSEL receiver timeout select 4 (0xF highest)
// bits 6-4 RXIFLSEL 2 is greater than or equal to half
// bits 2-0 TXIFLSEL 2 is less than or equal to half
NVIC->ICPR[0] = 1<<14; // UART2 is IRQ 14
NVIC->ISER[0] = 1<<14;
NVIC->IP[3] = (NVIC->IP[3]&(~0x00FF0000))|(2<<22); // set priority (bits 23-22) IRQ 14
UART2->CTL0 |= 0x01; // enable UART2
}
//------------UART2_InChar------------
// Get new serial port receive data from FIFO1
// Input: none
// Output: Return 0 if the FIFO1 is empty
// Return nonzero data from the FIFO1 if available
char UART2_InChar(void){
return Fifo1_Get();
}
void UART2_IRQHandler(void){ uint32_t status; char letter;
status = UART2->CPU_INT.IIDX; // reading clears bit in RIS
if(status == 0x01){ // 0x01 receive timeout
GPIOB->DOUTTGL31_0 = BLUE; // toggle PB22 (minimally intrusive debugging)
GPIOB->DOUTTGL31_0 = BLUE; // toggle PB22 (minimally intrusive debugging)
while((UART2->STAT&0x04) == 0){
letter = UART2->RXDATA;
if(Fifo1_Put(letter)==0){
LostData2++;
}
}
GPIOB->DOUTTGL31_0 = BLUE; // toggle PB22 (minimally intrusive debugging)
}
}

56
RTOS_Labs_common/UART2.h Normal file
View File

@@ -0,0 +1,56 @@
/*!
* @defgroup UART
* @brief Asynchronous serial communication
<table>
<caption id="UARTpins6">UART pins on the MSPM0G3507</caption>
<tr><th>Pin <th>Description
<tr><td>PA22 <td>UART1 Rx from other microcontroller PA8 Tx
</table>
* @{*/
/**
* @file UART2.h
* @brief Initialize UART2, no transmit, interrupt on receive timeout
* @details UART2 initialization. 2000 bps baud,
* 1 start, 8 data bits, 1 stop, no parity.<br>
* @version ECE319K v1.0
* @author your name
* @copyright lab 8 solution, do not post,
* @warning AS-IS
* @note For more information see http://users.ece.utexas.edu/~valvano/
* @date October 29, 2023
<table>
<caption id="UARTpins7">UART pins on the MSPM0G3507</caption>
<tr><th>Pin <th>Description
<tr><td>PA22 <td>UART2 Rx from other microcontroller PA8 Tx
<tr><td>J14 <td>Connect SW1 to PA9 (not PB23)
</table>
******************************************************************************/
#ifndef __UART2_H__
#define __UART2_H__
/**
* initialize UART2 for 2000 bps baud rate.<br>
* PA22 UART2 Rx from other microcontroller PA8 Tx<br>
* no transmit, interrupt on receive timeout
* @param none
* @return none
* @brief Initialize UART1
*/
void UART2_Init(void);
/**
* Get new serial port receive data from FIFO1<br>
* Return 0 if the FIFO1 is empty<br>
* Return nonzero data from the FIFO1 if available
* @param none
* @return char ASCII code from other computer
* @brief get data from FIFO1
*/
char UART2_InChar(void);
#endif // __UART2_H__
/** @}*/

View File

@@ -0,0 +1,44 @@
// WifiSettings.h
// Dung Nguyen
// Wally Guzman
// Baudrate for UART connection to ESP8266
//#define BAUDRATE 74880 // The ESP first boots with 74880 baud and then switches
#define BAUDRATE 115200
// Return values
#define NORESPONSE (-1)
#define BADVALUE (-1)
#define SUCCESS 1
#define FAILURE 0
enum Menu_Status {RX=0, TX, CONNECTED};
// Station or soft access point mode
// 0 means regular station, 1 means act as soft AP
#define SOFTAP 0
// Access Point Parameters
#define SSID_NAME "utexas-iot"
#define PASSKEY "87943064235102835779"
//#define SSID_NAME "utexas-iot"
//#define PASSKEY ""
/* To get the password for the "utexas-iot" network:
From your PC/laptop:
Go to https://network.utexas.edu
Log in with your UT EID (via UTLogin)
Click the "Register Wireless Device" button
Enter your device's MAC address and a description, and click the "Register" button
Make note of the WPA2 key generated (will be unique for each device you register)
On your IoT device:
Select utexas-iot from the available wireless networks
Enter the WPA2 key provided to you in step 5 above
*/

624
RTOS_Labs_common/eDisk.c Normal file
View File

@@ -0,0 +1,624 @@
// eDisk.c
// Runs on MSPM0
// Use SPI1 to communicate with the SDC.
// Valvano
// December 27, 2025
// Solution to lab 4
/*
Copyright 2026 by Jonathan W. Valvano, valvano@mail.utexas.edu
You may use, edit, run or distribute this file
as long as the above copyright notice remains
THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED
OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
VALVANO SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
For more information about my classes, my research, and my books, see
http://users.ece.utexas.edu/~valvano/
*/
// hardware connections, ECE445M RTOS sensor board
// **********ST7735 TFT and SDC*******************
// ST7735
// Backlight (pin 10) to +3.3 V
// MISO (pin 9) to SPI1 POCI: PB7
// SCK (pin 8) to SPI1 SCLK: PB9
// MOSI (pin 7) to SPI1 PICO: PB8
// TFT_CS (pin 6) to GPIO: PB6
// CARD_CS (pin 5) to PB0 (GPIO)
// Data/Command (pin 4) to PB16 (GPIO), high for data, low for command
// RESET (pin 3) to PB15 (GPIO)
// VCC (pin 2) to +3.3 V
// Gnd (pin 1) to ground
// **********HiLetgo ST7735 TFT and SDC (SDC not tested)*******************
// ST7735
// LED- (pin 16) TFT, to ground
// LED+ (pin 15) TFT, to +3.3 V
// SD_CS (pin 14) SDC, to PB0 chip select
// MOSI (pin 13) SDC, to PB8 MOSI
// MISO (pin 12) SDC, to PB7 MISO
// SCK (pin 11) SDC, to serial clock
// CS (pin 10) TFT, to PB6 GPIO
// SCL (pin 9) TFT, to PB9 SPI1 SCLK
// SDA (pin 8) TFT, to PB8 MOSI SPI1 PICO
// A0 (pin 7) TFT, to PB16 Data/Command, high for data, low for command
// RESET (pin 6) TFT, to PB15 reset (GPIO), low to reset
// NC (pins 3,4,5)
// VCC (pin 2) to +3.3 V
// GND (pin 1) to ground
#include <stdint.h>
#include <ti/devices/msp/msp.h>
#include "../inc/LaunchPad.h"
#include "../inc/Clock.h"
#include "../inc/Timer.h"
#include "../RTOS_Labs_common/SPI.h"
#include "../RTOS_Labs_common/eDisk.h"
#include "../RTOS_Labs_common/OS.h"
// **** OS must run disk_timerproc(); at 1000Hz, every 1ms *****
// outputBitRate = (spiInputClock) / ((1 + SCR) * 2)
// 99 for 400,000 bps slow mode, used during initialization
// 4 for 8,000,000 bps fast mode, used during disk I/O
#define FCLK_SLOW() { SPI1->CLKCTL = 99; }
#define FCLK_FAST() { SPI1->CLKCTL = 80/16 -1; }// 8 MHz
//#define MMC_CD !(GPIOC_IDR & _BV(4)) /* Card detect (yes:true, no:false, default:true) */
#define MMC_CD 1 /* Card detect (yes:true, no:false, default:true) */
#define MMC_WP 0 /* Write protected (yes:true, no:false, default:false) */
/*--------------------------------------------------------------------------
Module Private Functions
---------------------------------------------------------------------------*/
/* MMC/SD command */
#define CMD0 (0) /* GO_IDLE_STATE */
#define CMD1 (1) /* SEND_OP_COND (MMC) */
#define ACMD41 (0x80+41) /* SEND_OP_COND (SDC) */
#define CMD8 (8) /* SEND_IF_COND */
#define CMD9 (9) /* SEND_CSD */
#define CMD10 (10) /* SEND_CID */
#define CMD12 (12) /* STOP_TRANSMISSION */
#define ACMD13 (0x80+13) /* SD_STATUS (SDC) */
#define CMD16 (16) /* SET_BLOCKLEN */
#define CMD17 (17) /* READ_SINGLE_BLOCK */
#define CMD18 (18) /* READ_MULTIPLE_BLOCK */
#define CMD23 (23) /* SET_BLOCK_COUNT (MMC) */
#define ACMD23 (0x80+23) /* SET_WR_BLK_ERASE_COUNT (SDC) */
#define CMD24 (24) /* WRITE_BLOCK */
#define CMD25 (25) /* WRITE_MULTIPLE_BLOCK */
#define CMD32 (32) /* ERASE_ER_BLK_START */
#define CMD33 (33) /* ERASE_ER_BLK_END */
#define CMD38 (38) /* ERASE */
#define CMD55 (55) /* APP_CMD */
#define CMD58 (58) /* READ_OCR */
static volatile DSTATUS Stat = STA_NOINIT; /* Physical drive status */
static volatile UINT Timer1, Timer2; /* 1kHz decrement timer stopped at zero (disk_timerproc()) */
static BYTE CardType; /* Card type flags */
/*-----------------------------------------------------------------------*/
/* SPI controls (Platform dependent) */
/*-----------------------------------------------------------------------*/
/* Initialize MMC interface */
static void init_spi(void){
SPI1_Init(); // initialize SPI
SDC_CS_HIGH(); /* Set CS# high */
// TimerG0_IntArm(1000,40,1); // initialize TimerG0 for 1 ms interrupts
Clock_Delay1ms(10);
// for (Timer1 = 10; Timer1; ) ; /* 10ms */
}
/* Exchange a byte */
// Inputs: byte to be sent to SPI
// Outputs: byte received from SPI
// assumes it has been selected with CS low
static BYTE xchg_spi(BYTE data){ BYTE volatile rcvdat;
// wait until SPI1 not busy/
while((SPI1->STAT&0x10) == 0x10){}; // spin SPI busy
SPI1->TXDATA = data;
while((SPI1->STAT&0x04) == 0x04){}; // spin SPI RxFifo empty
rcvdat = SPI1->RXDATA; // acknowledge response
// while((SSI0_SR_R&SSI_SR_BSY)==SSI_SR_BSY){};
// SSI0_DR_R = dat; // data out
// while((SSI0_SR_R&SSI_SR_RNE)==0){}; // wait until response
// rcvdat = SSI0_DR_R; // acknowledge response
return rcvdat;
}
/*-----------------------------------------------------------------------*/
/* Receive a byte from MMC via SPI (Platform dependent) */
/*-----------------------------------------------------------------------*/
// Inputs: none
// Outputs: byte received from SPI
// assumes it has been selected with CS low
static BYTE rcvr_spi(void){
// wait until SPI1 not busy/
while((SPI1->STAT&0x10) == 0x10){}; // spin SPI busy
SPI1->TXDATA = 0xFF; // data out, garbage
while((SPI1->STAT&0x04) == 0x04){}; // spin SPI RxFifo empty
return (BYTE)SPI1->RXDATA; // read received data
// while((SSI0_SR_R&SSI_SR_BSY)==SSI_SR_BSY){};
// SSI0_DR_R = 0xFF; // data out, garbage
// while((SSI0_SR_R&SSI_SR_RNE)==0){}; // wait until response
// return (BYTE)SSI0_DR_R; // read received data
}
/* Receive multiple byte */
// Input: buff Pointer to empty buffer into which data will be received
// btr Number of bytes to receive (even number)
// Output: none
static void rcvr_spi_multi(BYTE *buff, UINT btr){
while(btr){
*buff = rcvr_spi(); // return by reference
btr--; buff++;
}
}
#if _USE_WRITE
/* Send multiple byte */
// Input: buff Pointer to the data which will be sent
// btx Number of bytes to send (even number)
// Output: none
static void xmit_spi_multi(const BYTE *buff, UINT btx){
BYTE volatile rcvdat;
while(btx){
SPI1->TXDATA = *buff;
while((SPI1->STAT&0x04) == 0x04){}; // spin SPI RxFifo empty
rcvdat = SPI1->RXDATA; // acknowledge response
// SSI0_DR_R = *buff; // data out
// while((SSI0_SR_R&SSI_SR_RNE)==0){}; // wait until response
// rcvdat = SSI0_DR_R; // acknowledge response
btx--; buff++;
}
}
#endif
/*-----------------------------------------------------------------------*/
/* Wait for card ready */
/*-----------------------------------------------------------------------*/
// Input: time to wait in ms
// Output: 1:Ready, 0:Timeout
static int wait_ready(UINT wt){
BYTE d;
Timer2 = wt;
do {
d = xchg_spi(0xFF);
/* This loop takes a time. Insert rot_rdq() here for multitask environment. */
} while (d != 0xFF && Timer2); /* Wait for card goes ready or timeout */
return (d == 0xFF) ? 1 : 0;
}
/*-----------------------------------------------------------------------*/
/* Deselect card and release SPI */
/*-----------------------------------------------------------------------*/
static void deselect(void){
SDC_CS_HIGH(); /* CS = H */
xchg_spi(0xFF); /* Dummy clock (force DO hi-z for multiple slave SPI) */
}
/*-----------------------------------------------------------------------*/
/* Select card and wait for ready */
/*-----------------------------------------------------------------------*/
// Input: none
// Output: 1:OK, 0:Timeout in 500ms
int firstselectflag=0;
static int select(void){
TFT_CS_HIGH(); // make sure TFT is off
SDC_CS_LOW();
/*
if(firstselectflag == 0){
Clock_Delay(80000);
firstselectflag = 1;
}
*/
xchg_spi(0xFF); /* Dummy clock (force DO enabled) */
if(wait_ready(500)) {
return 1; /* OK */
}
deselect();
return 0; /* Timeout */
}
/*-----------------------------------------------------------------------*/
/* Receive a data packet from the MMC */
/*-----------------------------------------------------------------------*/
// Input: buff Pointer to empty buffer into which data will be received
// btr Number of bytes to receive (even number)
// Output: 1:OK, 0:Error on timeout
static int rcvr_datablock(BYTE *buff, UINT btr){
BYTE token;
Timer1 = 200;
do { /* Wait for DataStart token in timeout of 200ms */
token = xchg_spi(0xFF);
/* This loop will take a time. Insert rot_rdq() here for multitask envilonment. */
} while ((token == 0xFF) && Timer1);
if(token != 0xFE) return 0; /* Function fails if invalid DataStart token or timeout */
rcvr_spi_multi(buff, btr); /* Store trailing data to the buffer */
xchg_spi(0xFF); xchg_spi(0xFF); /* Discard CRC */
return 1; /* Function succeeded */
}
/*-----------------------------------------------------------------------*/
/* Send a data packet to the MMC */
/*-----------------------------------------------------------------------*/
#if _USE_WRITE
// Input: buff Pointer to 512 byte data which will be sent
// token Token
// Output: 1:OK, 0:Failed on timeout
static int xmit_datablock(const BYTE *buff, BYTE token){
BYTE resp;
if (!wait_ready(500)) return 0; /* Wait for card ready */
xchg_spi(token); /* Send token */
if (token != 0xFD) { /* Send data if token is other than StopTran */
xmit_spi_multi(buff, 512); /* Data */
xchg_spi(0xFF); xchg_spi(0xFF); /* Dummy CRC */
resp = xchg_spi(0xFF); /* Receive data resp */
if ((resp & 0x1F) != 0x05) /* Function fails if the data packet was not accepted */
return 0;
}
return 1;
}
#endif
/*-----------------------------------------------------------------------*/
/* Send a command packet to the MMC */
/*-----------------------------------------------------------------------*/
// Inputs: cmd Command index
// arg /* Argument
// Outputs: R1 resp (bit7==1:Failed to send)
static BYTE send_cmd(BYTE cmd, DWORD arg){
BYTE n, res;
if (cmd & 0x80) { /* Send a CMD55 prior to ACMD<n> */
cmd &= 0x7F;
res = send_cmd(CMD55, 0);
if (res > 1) return res;
}
/* Select the card and wait for ready except to stop multiple block read */
if (cmd != CMD12) {
deselect();
if (!select()) return 0xFF;
}
/* Send command packet */
xchg_spi(0x40 | cmd); /* Start + command index */
xchg_spi((BYTE)(arg >> 24)); /* Argument[31..24] */
xchg_spi((BYTE)(arg >> 16)); /* Argument[23..16] */
xchg_spi((BYTE)(arg >> 8)); /* Argument[15..8] */
xchg_spi((BYTE)arg); /* Argument[7..0] */
n = 0x01; /* Dummy CRC + Stop */
if (cmd == CMD0) n = 0x95; /* Valid CRC for CMD0(0) */
if (cmd == CMD8) n = 0x87; /* Valid CRC for CMD8(0x1AA) */
xchg_spi(n);
/* Receive command resp */
if (cmd == CMD12) xchg_spi(0xFF); /* Diacard following one byte when CMD12 */
n = 10; /* Wait for response (10 bytes max) */
do
res = xchg_spi(0xFF);
while ((res & 0x80) && --n);
return res; /* Return received response */
}
/*--------------------------------------------------------------------------
Public Functions
---------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------*/
/* Initialize disk drive */
/*-----------------------------------------------------------------------*/
// Inputs: Physical drive number, which must be 0
// Outputs: status (see DSTATUS)
DSTATUS eDisk_Init(BYTE drv){
BYTE n, cmd, ty, ocr[4];
if (drv) return STA_NOINIT; /* Supports only drive 0 */
init_spi(); /* Initialize SPI */
if (Stat & STA_NODISK) return Stat; /* Is card existing in the soket? */
FCLK_SLOW();
for (n = 10; n; n--) xchg_spi(0xFF); /* Send 80 dummy clocks */
ty = 0;
if (send_cmd(CMD0, 0) == 1) { /* Put the card SPI/Idle state */
Timer1 = 1000; /* Initialization timeout = 1 sec */
if (send_cmd(CMD8, 0x1AA) == 1) { /* SDv2? */
for (n = 0; n < 4; n++) ocr[n] = xchg_spi(0xFF); /* Get 32 bit return value of R7 resp */
if (ocr[2] == 0x01 && ocr[3] == 0xAA) { /* Is the card supports vcc of 2.7-3.6V? */
while (Timer1 && send_cmd(ACMD41, 1UL << 30)) ; /* Wait for end of initialization with ACMD41(HCS) */
if (Timer1 && send_cmd(CMD58, 0) == 0) { /* Check CCS bit in the OCR */
for (n = 0; n < 4; n++) ocr[n] = xchg_spi(0xFF);
ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; /* Card id SDv2 */
}
}
} else { /* Not SDv2 card */
if (send_cmd(ACMD41, 0) <= 1) { /* SDv1 or MMC? */
ty = CT_SD1; cmd = ACMD41; /* SDv1 (ACMD41(0)) */
} else {
ty = CT_MMC; cmd = CMD1; /* MMCv3 (CMD1(0)) */
}
while (Timer1 && send_cmd(cmd, 0)) ; /* Wait for end of initialization */
if (!Timer1 || send_cmd(CMD16, 512) != 0) /* Set block length: 512 */
ty = 0;
}
}
CardType = ty; /* Card type */
deselect();
if (ty) { /* OK */
FCLK_FAST(); /* Set fast clock */
Stat &= ~STA_NOINIT; /* Clear STA_NOINIT flag */
} else { /* Failed */
Stat = STA_NOINIT;
}
return Stat;
}
/*-----------------------------------------------------------------------*/
/* Get disk status */
/*-----------------------------------------------------------------------*/
// Inputs: Physical drive number, which must be 0
// Outputs: status (see DSTATUS)
DSTATUS eDisk_Status(BYTE drv){
if (drv) return STA_NOINIT; /* Supports only drive 0 */
return Stat; /* Return disk status */
}
/*-----------------------------------------------------------------------*/
/* Read sector(s) */
/*-----------------------------------------------------------------------*/
//Inputs: drv Physical drive number (0)
// buff Pointer to the data buffer to store read data
// sector Start sector number (LBA)
// count Number of sectors to read (1..128)
// Outputs: status (see DRESULT)
DRESULT eDisk_Read(BYTE drv, BYTE *buff, DWORD sector, UINT count){
if (drv || !count) return RES_PARERR; /* Check parameter */
if (Stat & STA_NOINIT) return RES_NOTRDY; /* Check if drive is ready */
if (!(CardType & CT_BLOCK)) sector *= 512; /* LBA ot BA conversion (byte addressing cards) */
if (count == 1) { /* Single sector read */
if ((send_cmd(CMD17, sector) == 0) /* READ_SINGLE_BLOCK */
&& rcvr_datablock(buff, 512))
count = 0;
}
else { /* Multiple sector read */
if (send_cmd(CMD18, sector) == 0) { /* READ_MULTIPLE_BLOCK */
do {
if (!rcvr_datablock(buff, 512)) break;
buff += 512;
} while (--count);
send_cmd(CMD12, 0); /* STOP_TRANSMISSION */
}
}
deselect();
return count ? RES_ERROR : RES_OK; /* Return result */
}
//*************** eDisk_ReadBlock ***********
// Read 1 block of 512 bytes from the SD card (write to RAM)
// Inputs: pointer to an empty RAM buffer
// sector number of SD card to read: 0,1,2,...
// Outputs: result
// RES_OK 0: Successful
// RES_ERROR 1: R/W Error
// RES_WRPRT 2: Write Protected
// RES_NOTRDY 3: Not Ready
// RES_PARERR 4: Invalid Parameter
DRESULT eDisk_ReadBlock(
BYTE *buff, /* Pointer to the data buffer to store read data */
DWORD sector){ /* Start sector number (LBA) */
return eDisk_Read(0,buff,sector,1);
}
/*-----------------------------------------------------------------------*/
/* Write sector(s) */
/*-----------------------------------------------------------------------*/
#if _USE_WRITE
//Inputs: drv Physical drive number (0)
// buff Pointer to the data buffer to write to disk
// sector Start sector number (LBA)
// count Number of sectors to write (1..128)
// Outputs: status (see DRESULT)
DRESULT eDisk_Write(BYTE drv, const BYTE *buff, DWORD sector, UINT count){
if (drv || !count) return RES_PARERR; /* Check parameter */
if (Stat & STA_NOINIT) return RES_NOTRDY; /* Check drive status */
if (Stat & STA_PROTECT) return RES_WRPRT; /* Check write protect */
if (!(CardType & CT_BLOCK)) sector *= 512; /* LBA ==> BA conversion (byte addressing cards) */
if (count == 1) { /* Single sector write */
if ((send_cmd(CMD24, sector) == 0) /* WRITE_BLOCK */
&& xmit_datablock(buff, 0xFE))
count = 0;
}
else { /* Multiple sector write */
if (CardType & CT_SDC) send_cmd(ACMD23, count); /* Predefine number of sectors */
if (send_cmd(CMD25, sector) == 0) { /* WRITE_MULTIPLE_BLOCK */
do {
if (!xmit_datablock(buff, 0xFC)) break;
buff += 512;
} while (--count);
if (!xmit_datablock(0, 0xFD)) /* STOP_TRAN token */
count = 1;
}
}
deselect();
return count ? RES_ERROR : RES_OK; /* Return result */
}
//*************** eDisk_WriteBlock ***********
// Write 1 block of 512 bytes of data to the SD card
// Inputs: pointer to RAM buffer with information
// sector number of SD card to write: 0,1,2,...
// Outputs: result
// RES_OK 0: Successful
// RES_ERROR 1: R/W Error
// RES_WRPRT 2: Write Protected
// RES_NOTRDY 3: Not Ready
// RES_PARERR 4: Invalid Parameter
DRESULT eDisk_WriteBlock (
const BYTE *buff, /* Pointer to the data to be written */
DWORD sector){ /* Start sector number (LBA) */
return eDisk_Write(0,buff,sector,1); // 1 block
}
#endif
/*-----------------------------------------------------------------------*/
/* Miscellaneous drive controls other than data read/write */
/*-----------------------------------------------------------------------*/
// Inputs: drv, Physical drive number (0)
// cmd, Control command code
// buff Pointer to the control data
// Outputs: status (see DRESULT)
#if _USE_IOCTL
DRESULT disk_ioctl(BYTE drv, BYTE cmd, void *buff){
DRESULT res;
BYTE n, csd[16];
DWORD *dp, st, ed, csize;
if (drv) return RES_PARERR; /* Check parameter */
if (Stat & STA_NOINIT) return RES_NOTRDY; /* Check if drive is ready */
res = RES_ERROR;
switch (cmd) {
case CTRL_SYNC : /* Wait for end of internal write process of the drive */
if (select()) res = RES_OK;
break;
case GET_SECTOR_COUNT : /* Get drive capacity in unit of sector (DWORD) */
if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) {
if ((csd[0] >> 6) == 1) { /* SDC ver 2.00 */
csize = csd[9] + ((WORD)csd[8] << 8) + ((DWORD)(csd[7] & 63) << 16) + 1;
*(DWORD*)buff = csize << 10;
} else { /* SDC ver 1.XX or MMC ver 3 */
n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
csize = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1;
*(DWORD*)buff = csize << (n - 9);
}
res = RES_OK;
}
break;
case GET_BLOCK_SIZE : /* Get erase block size in unit of sector (DWORD) */
if (CardType & CT_SD2) { /* SDC ver 2.00 */
if (send_cmd(ACMD13, 0) == 0) { /* Read SD status */
xchg_spi(0xFF);
if (rcvr_datablock(csd, 16)) { /* Read partial block */
for (n = 64 - 16; n; n--) xchg_spi(0xFF); /* Purge trailing data */
*(DWORD*)buff = 16UL << (csd[10] >> 4);
res = RES_OK;
}
}
} else { /* SDC ver 1.XX or MMC */
if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) { /* Read CSD */
if (CardType & CT_SD1) { /* SDC ver 1.XX */
*(DWORD*)buff = (((csd[10] & 63) << 1) + ((WORD)(csd[11] & 128) >> 7) + 1) << ((csd[13] >> 6) - 1);
} else { /* MMC */
*(DWORD*)buff = ((WORD)((csd[10] & 124) >> 2) + 1) * (((csd[11] & 3) << 3) + ((csd[11] & 224) >> 5) + 1);
}
res = RES_OK;
}
}
break;
case CTRL_TRIM : /* Erase a block of sectors (used when _USE_ERASE == 1) */
if (!(CardType & CT_SDC)) break; /* Check if the card is SDC */
if (disk_ioctl(drv, MMC_GET_CSD, csd)) break; /* Get CSD */
if (!(csd[0] >> 6) && !(csd[10] & 0x40)) break; /* Check if sector erase can be applied to the card */
dp = buff; st = dp[0]; ed = dp[1]; /* Load sector block */
if (!(CardType & CT_BLOCK)) {
st *= 512; ed *= 512;
}
if (send_cmd(CMD32, st) == 0 && send_cmd(CMD33, ed) == 0 && send_cmd(CMD38, 0) == 0 && wait_ready(30000)) /* Erase sector block */
res = RES_OK; /* FatFs does not check result of this command */
break;
default:
res = RES_PARERR;
}
deselect();
return res;
}
#endif
/*-----------------------------------------------------------------------*/
/* Device timer function */
/*-----------------------------------------------------------------------*/
/* This function must be called from timer interrupt routine in period
/ of 1 ms to generate card control timing.
/ OS will schedule this periodic task
*/
void disk_timerproc(void){
WORD n;
BYTE s;
n = Timer1; /* 1kHz decrement timer stopped at 0 */
if (n) Timer1 = --n;
n = Timer2;
if (n) Timer2 = --n;
s = Stat;
if (MMC_WP) /* Write protected */
s |= STA_PROTECT;
else /* Write enabled */
s &= ~STA_PROTECT;
if (MMC_CD) /* Card is in socket */
s &= ~STA_NODISK;
else /* Socket empty */
s |= (STA_NODISK | STA_NOINIT);
Stat = s;
}

278
RTOS_Labs_common/eDisk.h Normal file
View File

@@ -0,0 +1,278 @@
/**
* @file eDisk.h
* @brief low-level SDC driver
* @details This version of the driver has been
* configured to operate on the ST7735R, so that both
* the SDC card and the LCD use the same SSI port,
* but have different chip selects.
* @version V1.0
* @author Valvano
* @copyright Copyright 2026 by Jonathan W. Valvano, valvano@mail.utexas.edu,
* @warning AS-IS
* @note For more information see http://users.ece.utexas.edu/~valvano/
* @date December 29, 2026
******************************************************************************/
/*-----------------------------------------------------------------------
/ Low level disk interface modlue include file R0.04a (C)ChaN, 2007
/-----------------------------------------------------------------------
* Modified by Jonathan Valvano to simplify usage in Lab 4
*/
#ifndef _DISKIO
/**
* \brief set to 1 to enable disk write
*/
#define _USE_WRITE 1
/**
* \brief set to 1 to enable ioctl()
*/
#define _USE_IOCTL 1
typedef signed int INT;
typedef unsigned int UINT;
/* These types are assumed as 8-bit integer */
typedef signed char CHAR;
typedef unsigned char UCHAR;
typedef unsigned char BYTE;
/* These types are assumed as 16-bit integer */
typedef signed short SHORT;
typedef unsigned short USHORT;
typedef unsigned short WORD;
/* These types are assumed as 32-bit integer */
typedef signed long LONG;
typedef unsigned long ULONG;
typedef unsigned long DWORD;
/**
* \brief Boolean type
*/
typedef enum { FALSE = 0, TRUE } BOOL;
/**
* \brief Status of Disk Functions
*/
typedef BYTE DSTATUS;
/* Results of Disk Functions */
typedef enum {
RES_OK = 0, /* 0: Successful */
RES_ERROR, /* 1: R/W Error */
RES_WRPRT, /* 2: Write Protected */
RES_NOTRDY, /* 3: Not Ready */
RES_PARERR /* 4: Invalid Parameter */
} DRESULT;
/*---------------------------------------*/
/* Prototypes for disk control functions */
/**
* @details Turn on PLL.
* Since this program initializes the disk, it must run with
* the disk periodic task operating.
<table>
<caption id="init">Return parameter</caption>
<tr><th>Return <th>Value <th>Meaning
<tr><td>RES_OK <td>0x00 <td>Successful
<tr><td>STA_NOINIT <td>0x01 <td>Drive not initialized
<tr><td>STA_NODISK <td>0x02 <td>No medium in the drive
<tr><td>STA_PROTECT <td>0x04 <td>Write protected
</table>
* @param drive number (only drive 0 is supported)
* @return status (0 means OK)
* @brief Initialize the interface between microcontroller and the SD card.
*/
DSTATUS eDisk_Init(BYTE drive);
/**
* @details Checks the status of the secure digital care.
<table>
<caption id="status">Return parameter</caption>
<tr><th>Return <th>Value <th>Meaning
<tr><td>RES_OK <td>0x00 <td>Successful
<tr><td>STA_NOINIT <td>0x01 <td>Drive not initialized
<tr><td>STA_NODISK <td>0x02 <td>No medium in the drive
<tr><td>STA_PROTECT <td>0x04 <td>Write protected
</table>
* @param drive number (only drive 0 is supported)
* @return status (0 means OK)
* @brief Check the status of the SD card.
*/
DSTATUS eDisk_Status (BYTE drive);
/**
* @details Read data from the SD card (write to RAM)
<table>
<caption id="read">Return parameter</caption>
<tr><th>Return <th>Value <th>Meaning
<tr><td>RES_OK <td>0x00 <td>Successful
<tr><td>RES_ERROR <td>0x01 <td>R/W Error
<tr><td>RES_WRPRT <td>0x02 <td>Write Protected
<tr><td>RES_NOTRDY <td>0x03 <td>Not Ready
<tr><td>RES_PARERR <td>0x04 <td>Invalid Parameter
</table>
* @param drv (only drive 0 is supported)
* @param buff pointer to an empty RAM buffer
* @param sector sector number of SD card to read: 0,1,2,...
* @param count number of sectors to read
* @return result (0 means OK)
* @brief Read bytes from SD card.
*/
DRESULT eDisk_Read (
BYTE drv, // Physical drive number (0)
BYTE *buff, // Pointer to buffer to read data
DWORD sector, // Start sector number (LBA)
UINT count); // Sector count (1..255)
/**
* @details Read one block from the SD card (write to RAM)
<table>
<caption id="readBlock">Return parameter</caption>
<tr><th>Return <th>Value <th>Meaning
<tr><td>RES_OK <td>0x00 <td>Successful
<tr><td>RES_ERROR <td>0x01 <td>R/W Error
<tr><td>RES_WRPRT <td>0x02 <td>Write Protected
<tr><td>RES_NOTRDY <td>0x03 <td>Not Ready
<tr><td>RES_PARERR <td>0x04 <td>Invalid Parameter
</table>
* @param buff pointer to an empty RAM buffer
* @param sector sector number of SD card to read: 0,1,2,...
* @return result (0 means OK)
* @brief Read 512-byte block from SD card.
*/
DRESULT eDisk_ReadBlock (
BYTE *buff, /* Pointer to the data buffer to store read data */
DWORD sector); /* Start sector number (LBA) */
#if _READONLY == 0
/**
* @details write data to the SD card (read to RAM)
<table>
<caption id="write">Return parameter</caption>
<tr><th>Return <th>Value <th>Meaning
<tr><td>RES_OK <td>0x00 <td>Successful
<tr><td>RES_ERROR <td>0x01 <td>R/W Error
<tr><td>RES_WRPRT <td>0x02 <td>Write Protected
<tr><td>RES_NOTRDY <td>0x03 <td>Not Ready
<tr><td>RES_PARERR <td>0x04 <td>Invalid Parameter
</table>
* @param drv (only drive 0 is supported)
* @param buff pointer to RAM buffer with data
* @param sector sector number of SD card to write: 0,1,2,...
* @param count number of sectors to write
* @return result (0 means OK)
* @brief Write bytes to SD card.
*/
DRESULT eDisk_Write (
BYTE drv, // Physical drive number (0)
const BYTE *buff, // Pointer to the data to be written
DWORD sector, // Start sector number (LBA)
UINT count); // Sector count (1..255)
/**
* @details Write one block to the SD card (read to RAM)
<table>
<caption id="writeBlock">Return parameter</caption>
<tr><th>Return <th>Value <th>Meaning
<tr><td>RES_OK <td>0x00 <td>Successful
<tr><td>RES_ERROR <td>0x01 <td>R/W Error
<tr><td>RES_WRPRT <td>0x02 <td>Write Protected
<tr><td>RES_NOTRDY <td>0x03 <td>Not Ready
<tr><td>RES_PARERR <td>0x04 <td>Invalid Parameter
</table>
* @param buff pointer to RAM buffer with 512 bytes of data
* @param sector sector number of SD card to write: 0,1,2,...
* @return result (0 means OK)
* @brief Write 512-byte block from SD card.
*/
DRESULT eDisk_WriteBlock (
const BYTE *buff, /* Pointer to the data to be written */
DWORD sector); /* Start sector number (LBA) */
#endif
/**
* @details Enable SDC chip select, so it is an output
* @param none
* @return none
* @brief Configure SDC chip select
*/
void CS_Init(void);
/**
* @details This implements timeout functions
* @param none
* @return none
* @brief This should be called every 10 ms.
*/
void disk_timerproc(void);
/**
* @details General purpose function for all disk I/O
* @param drv (only drive 0 is supported)
* @param cmd disk command
* @param buff pointer to RAM input/output data
* @return result (0 means OK)
* @brief Disk input/output.
*/
DRESULT disk_ioctl (BYTE drv, BYTE cmd, void *buff);
/**
* \brief Disk Status Bits (DSTATUS)
*/
#define STA_NOINIT 0x01 /* Drive not initialized */
#define STA_NODISK 0x02 /* No medium in the drive */
#define STA_PROTECT 0x04 /* Write protected */
/* Command code for disk_ioctrl fucntion */
/* Generic command (Used by FatFs) */
#define CTRL_SYNC 0 /* Complete pending write process (needed at _FS_READONLY == 0) */
#define GET_SECTOR_COUNT 1 /* Get media size (needed at _USE_MKFS == 1) */
#define GET_SECTOR_SIZE 2 /* Get sector size (needed at _MAX_SS != _MIN_SS) */
#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at _USE_MKFS == 1) */
#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at _USE_TRIM == 1) */
/* Generic command (Not used by FatFs) */
#define CTRL_FORMAT 5 /* Create physical format on the media */
#define CTRL_POWER_IDLE 6 /* Put the device idle state */
#define CTRL_POWER_OFF 7 /* Put the device off state */
#define CTRL_LOCK 8 /* Lock media removal */
#define CTRL_UNLOCK 9 /* Unlock media removal */
#define CTRL_EJECT 10 /* Eject media */
/* MMC/SDC specific command (Not used by FatFs) */
#define MMC_GET_TYPE 50 /* Get card type */
#define MMC_GET_CSD 51 /* Get CSD */
#define MMC_GET_CID 52 /* Get CID */
#define MMC_GET_OCR 53 /* Get OCR */
#define MMC_GET_SDSTAT 54 /* Get SD status */
/* ATA/CF specific command (Not used by FatFs) */
#define ATA_GET_REV 60 /* Get F/W revision */
#define ATA_GET_MODEL 61 /* Get model name */
#define ATA_GET_SN 62 /* Get serial number */
/* MMC card type flags (MMC_GET_TYPE) */
#define CT_MMC 0x01 /* MMC ver 3 */
#define CT_SD1 0x02 /* SD ver 1 */
#define CT_SD2 0x04 /* SD ver 2 */
#define CT_SDC (CT_SD1|CT_SD2) /* SD */
#define CT_BLOCK 0x08 /* Block addressing */
#define _DISKIO
#endif

719
RTOS_Labs_common/eFile.c Normal file
View File

@@ -0,0 +1,719 @@
// filename ************** eFile.c *****************************
// High-level routines to implement a solid-state disk
// Students implement these functions in Lab 4
// Jonathan W. Valvano 12/27/25
// Solution to lab 4
#include <stdint.h>
#include <string.h>
#include "../RTOS_Labs_common/OS.h"
#include "../RTOS_Labs_common/eDisk.h"
#include "../RTOS_Labs_common/eFile.h"
#include <stdio.h>
#define SUCCESS 0
#define FAIL 1
int OpenFlag=0; // 0 means not initialized
#define MAXBLOCK 256 // largest block number,
#define DATASIZE 512 // 512
struct aBlock{
char data[DATASIZE]; // blockes are exactly 512 bytes
};
typedef struct aBlock BlockType;
#define NOT_OPEN 255
int WOpenFile; // directory index of file open for writing (0 to 30)
BlockType WCurrentBlock; // 512 bytes of RAM copy of block used during writing
unsigned long WBlockNum; // which block is stored in WCurrentBlock
int ROpenFile; // directory index of file open for reading (0 to 30)
BlockType RCurrentBlock; // 512 bytes
unsigned long RBlockNum; // which block is stored in RCurrentBlock
unsigned long RByteCnt; // which byte from block will be read next (0 to DATASIZE-1)
unsigned long RTotalByteCnt; // which byte from file will be read next (0 to file size - 1)
unsigned long BlankBlock[128]; // 512-byte block used temporarily
struct Entry{ // size = 16 bytes/file
char Name[8]; // file name, up to 7 characters
unsigned long First; // first block ((Size/DATASIZE)+1 = number of blocks)
unsigned long Size; // number of bytes (Size%DATASIZE = bytes in last block)
};
typedef struct Entry EntryType;
#define MAXFILES 30
struct aDirectory{
EntryType File[MAXFILES]; // up to 30 files
};
typedef struct aDirectory DirectoryType;
#define NONE {0,0,0,0,0,0,0,0} // blank name
const DirectoryType BlankDirectory = {
{ { NONE, 0, 0}, // first file
{ NONE, 0, 0}, { NONE, 0, 0}, { NONE, 0, 0}, { NONE, 0, 0}, { NONE, 0, 0},
{ NONE, 0, 0}, { NONE, 0, 0}, { NONE, 0, 0}, { NONE, 0, 0}, { NONE, 0, 0},
{ NONE, 0, 0}, { NONE, 0, 0}, { NONE, 0, 0}, { NONE, 0, 0}, { NONE, 0, 0},
{ NONE, 0, 0}, { NONE, 0, 0}, { NONE, 0, 0}, { NONE, 0, 0}, { NONE, 0, 0},
{ NONE, 0, 0}, { NONE, 0, 0}, { NONE, 0, 0}, { NONE, 0, 0}, { NONE, 0, 0},
{ NONE, 0, 0}, { NONE, 0, 0}, { NONE, 0, 0}, { NONE, 0, 0}}, // 30th file
};
#define BLOCKFREE 1 // Block not used in FAT
#define FAT_EOF 0 // End of file
struct MetadataBlock {
unsigned char FAT[MAXBLOCK]; // File allocation table - each entry points to next block in file
unsigned long FreeMap[MAXBLOCK/32]; // 256 bits, 1 for each block
unsigned char padding[DATASIZE - MAXBLOCK - (MAXBLOCK/32)*4]; // To make sure it takes up a whole block
};
typedef struct MetadataBlock MetadataType;
MetadataType Metadata;
#define NUMDIRECTORIES 2
DirectoryType Directories[NUMDIRECTORIES]; // RAM copies of directories
DirectoryType* CurrentDirectory; // pointer to curent directory loaded
unsigned long DCurrentEntry; // current directory entry
// Sets the given bit in the free map, indicating that block is now free
void FreeMapSetBit(unsigned char bit){
Metadata.FreeMap[bit/32] |= (1 << (bit%32));
}
// Clears the given bit in the free map, indicating that block is now taken
void FreeMapClearBit(unsigned char bit){
Metadata.FreeMap[bit/32] &= ~(1 << (bit%32));
}
// Returns the value of a given bit in the free map
unsigned char FreeMapGet(unsigned char bit){
return (Metadata.FreeMap[bit/32] >> (bit%32)) & 1;
}
// Allocate a free block, returns index of block allocated
unsigned char FreeMapAlloc(){
for (int i = 0; i < MAXBLOCK/32; i++){
if (Metadata.FreeMap[i]){
unsigned char bit = 0;
unsigned long word = Metadata.FreeMap[i];
while (!(word & 1)){
word >>= 1;
bit++;
}
unsigned char block = i*32 + bit;
FreeMapClearBit(block);
Metadata.FAT[block] = FAT_EOF;
return block;
}
}
return FAIL; // Block 1 cannot be allocated (directory 0), so this indicates fail because disk is full
}
//---------- eFile_Init-----------------
// Activate the file system, without formating
// Input: none
// Output: 0 if successful and 1 on failure (already initialized)
int eFile_Init(void){ // initialize file system
if(OpenFlag){
return SUCCESS; // already open
}
eDisk_Init(0); // initialize hardware, drive 0
OpenFlag = 1;
WOpenFile = NOT_OPEN; // not open WCurrentBlock is unused
ROpenFile = NOT_OPEN; // not open RCurrentBlock is unused
CurrentDirectory = NULL; // directory not loaded
return SUCCESS;
}
//---------- eFile_Format-----------------
// Erase all files, create blank directory, initialize free space manager
// Input: none
// Output: 0 if successful and 1 on failure (e.g., trouble writing to flash)
int eFile_Format(void){ // erase disk, add format
unsigned short block;
unsigned long old = OS_LockScheduler();
if(!OpenFlag){
OS_UnLockScheduler(old);
return FAIL; // not initialized
}
for (int i = 0; i < MAXBLOCK/32; i++){ // Format free bitmap
Metadata.FreeMap[i] = 0xFFFFFFFF; // Set all free
}
Metadata.FreeMap[0] &= ~7; // Reserve first 3 blocks
for (block = 0; block < MAXBLOCK; block++){ // Clear FAT (set all entries to 1)
Metadata.FAT[block] = BLOCKFREE;
}
if(eDisk_WriteBlock((const BYTE *)&Metadata,0)){ // format FAT + FreeMap
OS_UnLockScheduler(old);
return FAIL; // write block error
}
if(eDisk_WriteBlock((const BYTE *)&BlankDirectory,1)){ // format directory 0
OS_UnLockScheduler(old);
return FAIL; // write block error
}
if(eDisk_WriteBlock((const BYTE *)&BlankDirectory,2)){ // format directory 1
OS_UnLockScheduler(old);
return FAIL; // write block error
}
for(block=3; block<MAXBLOCK; block++){
if(eDisk_WriteBlock((const BYTE *)BlankBlock,block)){
OS_UnLockScheduler(old);
return FAIL; // write byte error
}
}
OS_UnLockScheduler(old);
CurrentDirectory = NULL; // directory not loaded
return SUCCESS; // OK
}
// bring directories from flash into RAM
// Output: 0 if successful and 1 on failure (e.g., trouble reading from flash)
int FetchMetadata(void){
if( eDisk_ReadBlock((BYTE *)&Metadata,0)){ // first block is FAT + FreeMap
return FAIL;
}
if( eDisk_ReadBlock((BYTE *)&Directories[0],1)){ // second block is directory 0
CurrentDirectory = NULL;
return FAIL;
}
if( eDisk_ReadBlock((BYTE *)&Directories[1],2)){ // third block is directory 1
CurrentDirectory = NULL;
return FAIL;
}
return SUCCESS;
}
// save RAM-copy of directories out to flash
// Output: 0 if successful and 1 on failure (e.g., trouble writing to flash)
int BackupMetadata(void){
if( eDisk_WriteBlock((BYTE *)&Metadata,0)){ // first block is FAT + FreeMap
return FAIL;
}
if( eDisk_WriteBlock((BYTE *)&Directories[0],1)){ // second block is directory 0
return FAIL;
}
if( eDisk_WriteBlock((BYTE *)&Directories[1],2)){ // third block is directory 1
return FAIL;
}
return SUCCESS;
}
//---------- eFile_Mount-----------------
// Mount the file system, without formating
// Input: none
// Output: 0 if successful and 1 on failure
int eFile_Mount(void){ // initialize file system
if(!OpenFlag){
return FAIL; // not initialized
}
if(FetchMetadata() == FAIL){
return FAIL; // problem fetching directory
}
CurrentDirectory = &Directories[0]; // default to directory 0
return SUCCESS;
}
//---------- eFile_SelectDirectory-----------------
// Select working directory
// Input: directory ID (0 or 1 since there are two)
// Output: 0 if successful and 1 on failure (e.g., trouble writing to flash)
int eFile_SelectDirectory(unsigned char dirID) {
if (dirID > 1){
return FAIL;
}
if (CurrentDirectory == NULL){
if (FetchMetadata() == FAIL){
return FAIL;
}
}
CurrentDirectory = &Directories[dirID];
return SUCCESS;
}
//---------- eFile_Create-----------------
// Create a new, empty file with one allocated block
// Input: file name is an ASCII string up to seven characters
// Output: 0 if successful and 1 on failure (e.g., trouble writing to flash)
int eFile_Create( const char name[]){ // create new file, make it empty
int i; unsigned char first;
if(!OpenFlag){
return FAIL; // not initialized
}
if(strlen(name)>7){
return FAIL; // name too long
}
if(CurrentDirectory == NULL){ // read if not already in memory
if(FetchMetadata() == FAIL){
return FAIL; // problem fetching directory
}
}
i = 0; // search for duplicate
while(i<MAXFILES){
if(strcmp(CurrentDirectory->File[i].Name, name)==0){
return FAIL; // file already exists
}
i++;
}
i = 0; // search for free directory entry spot
while((i<MAXFILES)&&(CurrentDirectory->File[i].Name[0])){
i++;
}
if(i==(MAXFILES)){
return FAIL; // full directory, up to 30 files
}
first = FreeMapAlloc();
if(first == FAIL){
return FAIL; // problem allocating its first block, e.g., disk full
}
strcpy(CurrentDirectory->File[i].Name,name);
CurrentDirectory->File[i].First = first;
CurrentDirectory->File[i].Size = 0; // empty file
return BackupMetadata(); // restore directory back to flash
}
//---------- eFile_WOpen-----------------
// Open the file, read into RAM last block
// Input: file name is an ASCII string up to seven characters
// Output: 0 if successful and 1 on failure (e.g., trouble writing to flash)
int eFile_WOpen( const char name[]){ // open a file for writing
int i;
if(!OpenFlag){
return FAIL; // not initialized
}
if(WOpenFile != NOT_OPEN){
return FAIL; // already open
}
if(CurrentDirectory == NULL){ // load if not previously loaded
if(FetchMetadata() == FAIL){
return FAIL; // problem fetching directory
}
}
i = 0; // search for matching filename, strcmp returns 0 if equal
while((i<MAXFILES) && (strcmp(CurrentDirectory->File[i].Name,name))){
i++;
}
if((i==MAXFILES)||(i==ROpenFile)){ // can't have the same file open for read and write
return FAIL; // file does not exist or already open for read
}
WOpenFile = i;
WBlockNum = CurrentDirectory->File[i].First;
while(Metadata.FAT[WBlockNum] != FAT_EOF){ // keep reading until find the last block
WBlockNum = Metadata.FAT[WBlockNum];
}
if(eDisk_ReadBlock((BYTE *)&WCurrentBlock,WBlockNum)){ // fetch data block
WOpenFile = NOT_OPEN;
return FAIL; // trouble reading a data block
}
return SUCCESS;
}
//---------- eFile_Write-----------------
// Save at end of the open file
// Input: data to be saved
// Output: 0 if successful and 1 on failure (e.g., trouble writing to flash)
int eFile_Write( const char data){unsigned long newBlock;
if(!OpenFlag){
return FAIL; // not initialized
}
if(WOpenFile == NOT_OPEN){
return FAIL; // not open
}
unsigned long currentSize = CurrentDirectory->File[WOpenFile].Size;
if(currentSize > 0 && currentSize % DATASIZE == 0){ // this block full?
newBlock = FreeMapAlloc();
if(newBlock == FAIL){
eDisk_WriteBlock((const BYTE *)&WCurrentBlock,WBlockNum); // save full block to disk
WOpenFile = NOT_OPEN; // disk full, close
BackupMetadata();
return FAIL; // problem allocating next block
}
Metadata.FAT[WBlockNum] = newBlock; // link previous to new one
if(eDisk_WriteBlock((const BYTE *)&WCurrentBlock,WBlockNum)){ // save full block to disk
WOpenFile = NOT_OPEN;
return FAIL; //trouble writing a data block
}
WBlockNum = newBlock; // new one becomes current
Metadata.FAT[WBlockNum] = FAT_EOF; // mark this block as the end
}
WCurrentBlock.data[currentSize % DATASIZE] = data; // save into RAM buffer
CurrentDirectory->File[WOpenFile].Size++;
return SUCCESS;
}
//---------- eFile_WriteString-----------------
// Save at end of the open file
// Input: pointer to string to be saved
// Output: 0 if successful and 1 on failure (e.g., trouble writing to flash)
int eFile_WriteString(const char *pt){ int max=512;
while(*pt){
if(eFile_Write(*pt)) return FAIL; //trouble writing
pt++;
max--;
if(max==0)return FAIL; //buffer overflow
}
return SUCCESS;
}
//-----------------------eFile_WriteUDec-----------------------
// Write a 32-bit number in unsigned decimal format
// Input: 32-bit number to be transferred
// Output: 0 if successful and 1 on failure (e.g., trouble writing to flash)
// Variable format 1-10 digits with space before and no space after
int eFile_WriteUDec(uint32_t n){
char eOutBuf[12];
eOutBuf[11] = 0;
int i=10;
do{
eOutBuf[i] = '0'+n%10;
n = n/10;
i--;
}while(n);
eOutBuf[i] = ' ';
return eFile_WriteString(&eOutBuf[i]);
}
//-----------------------eFile_WriteSDec-----------------------
// Write a 32-bit number in signed decimal format
// Input: 32-bit number to be transferred
// Output: 0 if successful and 1 on failure (e.g., trouble writing to flash)
// Variable format 1-10 digits with space before and no space after
int eFile_WriteSDec(int32_t num){
char eOutBuf[12];
int32_t n;
if(num<0){
n = -num;
} else{
n = num;
}
eOutBuf[11] = 0;
int i=10;
do{
eOutBuf[i] = '0'+n%10;
n = n/10;
i--;
}while(n);
if(num<0){
eOutBuf[i] = '-';
} else{
eOutBuf[i] = ' ';
}
eOutBuf[i-1] = ' ';
return eFile_WriteString(&eOutBuf[i-1]);
}
//-----------------------eFile_WriteSFix2-----------------------
// Write a 32-bit number in signed fixed point format
// signed 32-bit with resolution 0.01
// range -999.99 to +999.99
// Input: signed 32-bit integer part of fixed point number
// Output: 0 if successful and 1 on failure (e.g., trouble writing to flash)
// Examples
// 72345 to " 723.45"
// -22100 to "-221.00"
// -102 to " -1.02"
// 31 to " 0.31"
// -100000 to " ***.**"
int eFile_WriteSFix2(int32_t num){
char eOutBuf[8];
int32_t n;
if((num>99999)||(num<-99999)){
return eFile_WriteString(" ***.**");
}
if(num<0){
n = -num;
eOutBuf[0] = '-';
} else{
n = num;
eOutBuf[0] = ' ';
}
if(n>9999){
eOutBuf[1] = '0'+n/10000;
n = n%10000;
eOutBuf[2] = '0'+n/1000;
} else{
if(n>999){
if(num<0){
eOutBuf[0] = ' ';
eOutBuf[1] = '-';
} else {
eOutBuf[1] = ' ';
}
eOutBuf[2] = '0'+n/1000;
} else{
if(num<0){
eOutBuf[0] = ' ';
eOutBuf[1] = ' ';
eOutBuf[2] = '-';
} else {
eOutBuf[1] = ' ';
eOutBuf[2] = ' ';
}
}
}
n = n%1000;
eOutBuf[3] = '0'+n/100;
n = n%100;
eOutBuf[4] = '.';
eOutBuf[5] = '0'+n/10;
n = n%10;
eOutBuf[6] = '0'+n;
eOutBuf[7] = 0;
return eFile_WriteString(eOutBuf);
}
//-----------------------eFile_WriteUFix2-----------------------
// Write a 32-bit number in signed fixed point format
// unsigned 32-bit with resolution 0.01
// range 0.00 to 999.99
// Input: unsigned 32-bit integer part of fixed point number
// Output: 0 if successful and 1 on failure (e.g., trouble writing to flash)
// Examples
// 72345 to " 723.45"
// 22100 to " 221.00"
// 102 to " 1.02"
// 31 to " 0.31"
// 100000 to " ***.**"
int eFile_WriteUFix2(uint32_t num){
if(num>99999){
return eFile_WriteString(" ***.**");
}
return eFile_WriteSFix2((int32_t) num);
}
//---------- eFile_WClose-----------------
// Close the file, left disk in a state power can be removed
// Input: none
// Output: 0 if successful and 1 on failure (e.g., trouble writing to flash)
int eFile_WClose(void){ // close the file for writing
if(!OpenFlag){
return FAIL; // not initialized
}
if(WOpenFile==NOT_OPEN){
return FAIL; // not open
}
WOpenFile = NOT_OPEN; // Now closed for writing
if(eDisk_WriteBlock((const BYTE *)&WCurrentBlock,WBlockNum)){ // save full block to disk
return FAIL; // trouble writing a data block
}
return BackupMetadata(); // restore directory back to flash
}
//---------- eFile_ROpen-----------------
// Open the file, read first block into RAM
// Input: file name is an ASCII string up to seven characters
// Output: 0 if successful and 1 on failure (e.g., trouble read to flash)
int eFile_ROpen( const char name[]){ // open a file for reading
int i;
if(!OpenFlag){
return FAIL; // not initialized
}
if(ROpenFile != NOT_OPEN){
return FAIL; // already open
}
if(CurrentDirectory == NULL){ // load if not previously loaded
if(FetchMetadata() == FAIL){
return FAIL; // problem fetching directory
}
}
i = 0; // search for matching filename
while((i < MAXFILES) && (strcmp(CurrentDirectory->File[i].Name,name))){
i++;
}
if((i == MAXFILES)||(i == WOpenFile)){ // can't have the same file open for read and write
return FAIL; // file does not exist or is open for write
}
ROpenFile = i;
RBlockNum = CurrentDirectory->File[i].First;
if(eDisk_ReadBlock((BYTE *)&RCurrentBlock,RBlockNum)){ // fetch data block
ROpenFile = NOT_OPEN;
return 1; // trouble reading a data block
}
RByteCnt = 0; // start at the top of the block
RTotalByteCnt = 0; // start at beginning of file
return SUCCESS;
}
//---------- eFile_ReadNext-----------------
// Retreive data from open file
// Input: none
// Output: return by reference data
// 0 if successful and 1 on failure (e.g., end of file)
int eFile_ReadNext( char *pt){ // get next byte
if(!OpenFlag){
return FAIL; // not initialized
}
if(ROpenFile == NOT_OPEN){
return FAIL; // not open
}
unsigned long currentSize = CurrentDirectory->File[ROpenFile].Size;
if(RTotalByteCnt < currentSize && RByteCnt < DATASIZE){ // this block have data to read?
*pt = RCurrentBlock.data[RByteCnt];
RByteCnt++;
RTotalByteCnt++;
return SUCCESS; // We can keep reading from this block
}
if(Metadata.FAT[RBlockNum] == FAT_EOF){ // no more blocks
return FAIL; // end of file
}
RBlockNum = Metadata.FAT[RBlockNum]; // need to read next block
if(eDisk_ReadBlock((BYTE *)&RCurrentBlock,RBlockNum)){ // fetch data block
ROpenFile = NOT_OPEN;
return FAIL; // trouble reading a data block
}
RByteCnt = 0; // start at the top of the block
if(RTotalByteCnt < currentSize){ // this block have any data?
*pt = RCurrentBlock.data[0];
RByteCnt++;
RTotalByteCnt++;
return SUCCESS;
}
return FAIL; // end of file
}
//---------- eFileReadNextWord-----------------
// Retreive 32-bit little endian word from open file
// Input: none
// Output: return by reference data
// 0 if successful and 1 on failure (e.g., end of file)
uint32_t eFileReadNextWord(uint32_t *pt){char data; int status; *pt=0;
for(int i=0; i<32; i=i+8){
status = eFile_ReadNext(&data);
if(status==0){
(*pt) |= data<<i; // little endian
}
else return FAIL;
}
return SUCCESS;
}
//---------- eFile_RClose-----------------
// Close the reading file
// Input: none
// Output: 0 if successful and 1 on failure (e.g., wasn't open)
int eFile_RClose(void){ // close the file for writing
if(!OpenFlag){
return FAIL; // not initialized
}
if(ROpenFile==NOT_OPEN){
return FAIL; // not open
}
ROpenFile = NOT_OPEN; // Now closed for reading
return SUCCESS;
}
//---------- eFile_Delete-----------------
// Delete this file
// Input: file name is a single ASCII letter
// Output: 0 if successful and 1 on failure (e.g., trouble writing to flash)
int eFile_Delete( const char name[]){ // remove this file
int i; unsigned short blknum;
if(!OpenFlag){
return FAIL; // not initialized
}
if(WOpenFile!=NOT_OPEN){
return FAIL; // can't delete a file, if one open for writing
}
if(CurrentDirectory == NULL){ // load if not previously loaded
if(FetchMetadata() == FAIL){
return FAIL; // problem fetching directory
}
}
i = 0; // search for matching filename
while((i<MAXFILES) && (strcmp(CurrentDirectory->File[i].Name , name))){
i++;
}
if(i==MAXFILES){
return FAIL; // file doesn't exist
}
CurrentDirectory->File[i].Name[0] = 0; // delete directory entry
CurrentDirectory->File[i].Size = 0; // empty file
blknum = CurrentDirectory->File[i].First;
if(blknum != BLOCKFREE){
while(Metadata.FAT[blknum] != FAT_EOF){ // keep reading until find the last block
FreeMapSetBit(blknum); // indicate this block is free now
unsigned long new_blknum = Metadata.FAT[blknum];
Metadata.FAT[blknum] = BLOCKFREE; // indicate block is free in FAT
blknum = new_blknum; // Move to next block in file
}
FreeMapSetBit(blknum); // indicate this block is free now
Metadata.FAT[blknum] = BLOCKFREE;
}
return BackupMetadata(); // restore directory back to flash
}
//---------- eFile_DOpen-----------------
// Open a (sub)directory, read into RAM
// Input: directory name is an ASCII string up to seven characters
// (empty/NULL for root directory)
// Output: 0 if successful and 1 on failure (e.g., trouble reading from flash)
int eFile_DOpen( const char name[]){ // open directory
if(!OpenFlag){
return FAIL; // not initialized
}
if(CurrentDirectory == NULL){ // load if not previously loaded
if(FetchMetadata() == FAIL){
return FAIL; // problem fetching directory
}
}
DCurrentEntry = 0;
return SUCCESS;
}
//---------- eFile_DirNext-----------------
// Retreive directory entry from open directory
// Input: none
// Output: return file name and size by reference
// 0 if successful and 1 on failure (e.g., end of directory)
int eFile_DirNext( char *name[], unsigned long *size){ // get next entry
if(!OpenFlag){
return FAIL; // not initialized
}
if(CurrentDirectory == NULL){
return FAIL; // not opened
}
while(DCurrentEntry<MAXFILES){
if(CurrentDirectory->File[DCurrentEntry].Name[0]){ // file exists, if name is nonzero
*name = CurrentDirectory->File[DCurrentEntry].Name;
*size = CurrentDirectory->File[DCurrentEntry].Size;
DCurrentEntry++;
return SUCCESS;
}
DCurrentEntry++;
}
return FAIL;
}
//---------- eFile_DClose-----------------
// Close the directory
// Input: none
// Output: 0 if successful and 1 on failure (e.g., wasn't open)
int eFile_DClose(void){ // close the directory
return SUCCESS; // nothing to do here
}
//---------- eFile_Unmount-----------------
// Unmount and deactivate the file system
// Input: none
// Output: 0 if successful and 1 on failure (not currently mounted)
int eFile_Unmount(void){
if(OpenFlag){
if (WOpenFile != NOT_OPEN){
eDisk_WriteBlock((const BYTE *)&WCurrentBlock, WBlockNum);
}
if (BackupMetadata() == FAIL){
return FAIL;
}
OpenFlag = 0; // closed
WOpenFile = NOT_OPEN; // not open
ROpenFile = NOT_OPEN; // not open
CurrentDirectory = NULL; // directory not loaded
return SUCCESS;
}
return FAIL; // error, because not open
}

198
RTOS_Labs_common/eFile.h Normal file
View File

@@ -0,0 +1,198 @@
/**
* @file eFile.h
* @brief high-level file system
* @details This file system sits on top of eDisk.
* @version V1.0
* @author Valvano
* @copyright Copyright 2026 by Jonathan W. Valvano, valvano@mail.utexas.edu,
* @warning AS-IS
* @note For more information see http://users.ece.utexas.edu/~valvano/
* @date Dec 30, 2025
******************************************************************************/
/**
* @details This function must be called first, before calling any of the other eFile functions
* @param none
* @return 0 if successful and 1 on failure (already initialized)
* @brief Activate the file system, without formating
*/
int eFile_Init(void); // initialize file system
/**
* @details Erase all files, create blank directory, initialize free space manager
* @param none
* @return 0 if successful and 1 on failure (e.g., trouble writing to flash)
* @brief Format the disk
*/
int eFile_Format(void); // erase disk, add format
/**
* @details Mount disk and load file system metadata information
* @param none
* @return 0 if successful and 1 on failure (e.g., already mounted)
* @brief Mount the disk
*/
int eFile_Mount(void); // mount disk and file system
/**
* @details Select working directory for file management
* @param dirID ID is 0 or 1, since there are two directories
* @return 0 if successful and 1 on failure (e.g., already mounted)
* @brief Select working directory by ID
*/
int eFile_SelectDirectory(unsigned char dirID); // select directory
/**
* @details Create a new, empty file with one allocated block
* @param name file name is an ASCII string up to seven characters
* @return 0 if successful and 1 on failure (e.g., already exists)
* @brief Create a new file
*/
int eFile_Create(const char name[]); // create new file, make it empty
/**
* @details Open the file for writing, read into RAM last block
* @param name file name is an ASCII string up to seven characters
* @return 0 if successful and 1 on failure (e.g., trouble reading from flash)
* @brief Open an existing file for writing
*/
int eFile_WOpen(const char name[]); // open a file for writing
/**
* @details Save one byte at end of the open file
* @param data byte to be saved on the disk
* @return 0 if successful and 1 on failure (e.g., trouble writing to flash)
* @brief Write one byte
*/
int eFile_Write(const char data);
/**
* @details Save string at end of the open file
* @param pt pointer to string to be saved
* @return 0 if successful and 1 on failure (e.g., trouble writing to flash)
* @brief Write string
*/
int eFile_WriteString(const char *pt);
//-----------------------eFile_WriteUDec-----------------------
// Write a 32-bit number in unsigned decimal format
// Input: 32-bit number to be transferred
// Output: none
// Variable format 1-10 digits with no space before or after
int eFile_WriteUDec(uint32_t n);
//-----------------------eFile_WriteSDec-----------------------
// Write a 32-bit number in signed decimal format
// Input: 32-bit number to be transferred
// Output: 0 if successful and 1 on failure (e.g., trouble writing to flash)
// Variable format 1-10 digits with space before and no space after
int eFile_WriteSDec(int32_t num);
//-----------------------eFile_WriteSFix2-----------------------
// Write a 32-bit number in signed decimal format
// format signed 16-bit with resolution 0.01
// range -327.67 to +327.67
// Input: signed 16-bit integer part of fixed point number
// -32768 means invalid fixed-point number
// Output: 0 if successful and 1 on failure (e.g., trouble writing to flash)
// Examples
// 12345 to " 123.45"
// -22100 to "-221.00"
// -102 to " -1.02"
// 31 to " 0.31"
// -32768 to " ***.**"
int eFile_WriteSFix2(int32_t n);
//-----------------------eFile_WriteUFix2-----------------------
// Write a 32-bit number in signed fixed point format
// unsigned 32-bit with resolution 0.01
// range 0.00 to 999.99
// Input: unsigned 32-bit integer part of fixed point number
// Output: 0 if successful and 1 on failure (e.g., trouble writing to flash)
// Examples
// 72345 to " 723.45"
// 22100 to " 221.00"
// 102 to " 1.02"
// 31 to " 0.31"
// 100000 to " ***.**"
int eFile_WriteUFix2(uint32_t num);
/**
* @details Close the file, leave disk in a state power can be removed.
* This function will flush all RAM buffers to the disk.
* @param none
* @return 0 if successful and 1 on failure (e.g., trouble writing to flash)
* @brief Close the file that was being written
*/
int eFile_WClose(void); // close the file for writing
/**
* @details Open the file for reading, read first block into RAM
* @param name file name is an ASCII string up to seven characters
* @return 0 if successful and 1 on failure (e.g., trouble reading from flash)
* @brief Open an existing file for reading
*/
int eFile_ROpen(const char name[]); // open a file for reading
/**
* @details Read one byte from disk into RAM
* @param pt call by reference pointer to place to save data
* @return 0 if successful and 1 on failure (e.g., trouble reading from flash)
* @brief Retreive data from open file
*/
int eFile_ReadNext(char *pt); // get next byte
/**
* @details Read one 32-bit word from disk into RAM, little endian
* @param pt call by reference pointer to place to save data
* @return 0 if successful and 1 on failure (e.g., trouble reading from flash)
* @brief Retreive data from open file
*/
uint32_t eFileReadNextWord(uint32_t *pt); // get next word
/**
* @details Close the file, leave disk in a state power can be removed.
* @param none
* @return 0 if successful and 1 on failure (e.g., wasn't open)
* @brief Close the file that was being read
*/
int eFile_RClose(void); // close the file for writing
/**
* @details Delete the file with this name, recover blocks so they can be used by another file
* @param name file name is an ASCII string up to seven characters
* @return 0 if successful and 1 on failure (e.g., file doesn't exist)
* @brief delete this file
*/
int eFile_Delete(const char name[]); // remove this file
/**
* @details Open a (sub)directory, read into RAM
* @param directory name is an ASCII string up to seven characters
* if subdirectories are supported (optional, empty sring for root directory)
* @return 0 if successful and 1 on failure (e.g., trouble reading from flash)
*/
int eFile_DOpen(const char name[]);
/**
* @details Retreive directory entry from open directory
* @param pointers to return file name and size by reference
* @return 0 if successful and 1 on failure (e.g., end of directory)
*/
int eFile_DirNext(char *name[], unsigned long *size);
/**
* @details Close the directory
* @param none
* @return 0 if successful and 1 on failure (e.g., wasn't open)
*/
int eFile_DClose(void);
/**
* @details Unmount and deactivate the file system.
* @param none
* @return 0 if successful and 1 on failure (e.g., trouble writing to flash)
* @brief Unmount the disk
*/
int eFile_Unmount(void);

1344
RTOS_Labs_common/esp8266.c Normal file

File diff suppressed because it is too large Load Diff

211
RTOS_Labs_common/esp8266.h Normal file
View File

@@ -0,0 +1,211 @@
//*********************** ESP8266.h ***********************
// Program written by:
// - Steven Prickett steven.prickett@gmail.com
//
// Driver for ESP8266 module to act as a WiFi client or server
//
//*********************************************************
/* Modified by Jonathan Valvano, Sept 19, 2015
Modified by Andreas Gerstlauer, Apr 13, 2020
Converted to MSPM0 by Jonathan Valvano, Jan 19, 2026
Added MSPM0G3507 UART2 by Jonathan Valvano, Jan 26, 2026
*/
#ifndef ESP8266_H
#define ESP8266_H
#include <stdint.h>
#define ESP8266_ENCRYPT_MODE_OPEN 0
#define ESP8266_ENCRYPT_MODE_WEP 1
#define ESP8266_ENCRYPT_MODE_WPA_PSK 2
#define ESP8266_ENCRYPT_MODE_WPA2_PSK 3
#define ESP8266_ENCRYPT_MODE_WPA_WPA2_PSK 4
#define ESP8266_WIFI_MODE_CLIENT 1
#define ESP8266_WIFI_MODE_AP 2
#define ESP8266_WIFI_MODE_AP_AND_CLIENT 3
//-------------------ESP8266_Init --------------
// Initializes the module
// Inputs: RX and/or TX echo for debugging
// Outputs: 1 for success, 0 for failure (no ESP detected)
int ESP8266_Init(int rx_echo, int tx_echo);
//-------------------ESP8266_Connect --------------
// Bring interface up and connect to Wifi
// Inputs: enable debug output
// Outputs: 1 on success, 0 on failure
int ESP8266_Connect(int verbose);
//-------------------ESP8266_StartServer --------------
// Start server on specific port
// Inputs: port and server timeout
// Outputs: 1 on success, 0 on failure
int ESP8266_StartServer(uint16_t port, uint16_t timeout);
//-------------------ESP8266_StopServer --------------
// Stop server and set to single-client mode
// Inputs: none
// Outputs: 1 on success, 0 on failure
int ESP8266_StopServer(void);
//----------ESP8266_Reset------------
// Soft resets the esp8266 module
// Input: none
// Output: 1 if success, 0 if fail
int ESP8266_Reset(void);
//---------ESP8266_Restore-----
// Restore the ESP8266 module to default values
// Inputs: none
// Outputs: 1 if success, 0 if fail
int ESP8266_Restore(void);
//---------ESP8266_GetVersionNumber----------
// Get status
// Input: none
// Output: 1 if success, 0 if fail
int ESP8266_GetVersionNumber(void);
//---------ESP8266_GetMACAddress----------
// Get MAC address
// Input: none
// Output: 1 if success, 0 if fail
int ESP8266_GetMACAddress(void);
//---------ESP8266_SetWifiMode----------
// Configures the esp8266 to operate as a wifi client, access point, or both
// Input: mode accepts ESP8266_WIFI_MODE constants
// Output: 1 if success, 0 if fail
int ESP8266_SetWifiMode(uint8_t mode);
//---------ESP8266_SetConnectionMux----------
// Enables the esp8266 connection mux, required for starting tcp server
// Input: 0 (single) or 1 (multiple)
// Output: 1 if success, 0 if fail
int ESP8266_SetConnectionMux(uint8_t enabled);
//---------ESP8266_ListAccessPoints----------
// Lists available wifi access points
// Input: none
// Output: 1 if success, 0 if fail
int ESP8266_ListAccessPoints(void);
//----------ESP8266_JoinAccessPoint------------
// Joins a wifi access point using specified ssid and password
// Input: SSID and PASSWORD
// Output: 1 if success, 0 if fail
int ESP8266_JoinAccessPoint(const char* ssid, const char* password);
// ----------ESP8266_QuitAccessPoint-------------
// Disconnects from currently connected wifi access point
// Inputs: none
// Outputs: 1 if success, 0 if fail
int ESP8266_QuitAccessPoint(void);
//----------ESP8266_ConfigureAccessPoint------------
// Configures esp8266 wifi soft access point settings
// Use this function only when in AP mode (and not in client mode)
// Input: SSID, Password, channel, security
// Output: 1 if success, 0 if fail
int ESP8266_ConfigureAccessPoint(const char* ssid, const char* password, uint8_t channel, uint8_t encryptMode);
//---------ESP8266_GetIPAddress----------
// Get local IP address
// Input: none
// output: 1 if success, 0 if fail
int ESP8266_GetIPAddress(void);
//---------ESP8266_SetSSLClientConfiguration----------
// Set SSL client configuration
// Requires certificates to be flashed into the ESP firmware
// Inputs: enable/disable client/server certificate checks
// Output: 1 if success, 0 if fail
int ESP8266_SetSSLClientConfiguration(int verifyClient, int verifyServer);
//---------ESP8266_SetSSLBufferSize----------
// Set SSL buffer size
// Inputs: buffer size between 2048 and 4096
// Output: 1 if success, 0 if fail
int ESP8266_SetSSLBufferSize(uint16_t bufferSize);
//---------ESP8266_MakeTCPConnection----------
// Establish TCP or SSL connection
// The ESP only seems to have limited SSL support, does not work with all servers
// Inputs: IP address or web page as a string, port, and keepalive time (0 if none)
// Output: 1 if success, 0 if fail
int ESP8266_MakeTCPConnection(char *IPaddress, uint16_t port, uint16_t keepalive, int ssl);
//---------ESP8266_Send----------
// Send a packet to server
// Input: payload to send
// Output: 1 if success, 0 if fail
int ESP8266_Send(const char* fetch);
//---------ESP8266_SendBuffered----------
// Send a string to server using ESP TCP-send buffer
// Input: payload to send
// Output: 1 if success, 0 if fail
int ESP8266_SendBuffered(const char* fetch);
//---------ESP8266_SendBuferedStatus----------
// Check status of last buffered segment
// Input: none
// Output: 1 if success, 0 if fail
int ESP8266_SendBufferedStatus(void);
//---------ESP8266_Receive----------
// Receive a string from server
// Reads from data input until end of line or max length is reached
// Input: buffer and max length
// Output: 1 and null-terminated string if success, 0 if fail (disconnected)
int ESP8266_Receive(char* fetch, uint32_t max);
//---------ESP8266_CloseTCPConnection----------
// Close TCP connection
// Input: none
// Output: 1 if success, 0 if fail
int ESP8266_CloseTCPConnection(void);
//---------ESP8266_SetDataTransmissionMode----------
// Set data transmission passthrough mode
// Input: 0 not data mode, 1 data mode; return "Link is builded"
// Output: 1 if success, 0 if fail
int ESP8266_SetDataTransmissionMode(uint8_t mode);
//---------ESP8266_GetStatus----------
// Get network connection status
// Input: none
// Output: 1 if success, 0 if fail
int ESP8266_GetStatus(void);
// --------ESP8266_EnableServer------------------
// Enables tcp server on specified port
// Inputs: port number
// Outputs: 1 if success, 0 if fail
int ESP8266_EnableServer(uint16_t port);
// ----------ESP8266_SetServerTimeout--------------
// Set connection timeout for tcp server, 0-28800 seconds
// Inputs: timeout parameter
// Outputs: 1 if success, 0 if fail
int ESP8266_SetServerTimeout(uint16_t timeout);
// ----------ESP8266_WaitForConnection--------------
// Wait for incoming connection on server
// Inputs: none
// Outputs: 1 if success, 0 if fail
int ESP8266_WaitForConnection(void);
//---------ESP8266_DisableServer----------
// Disables tcp server
// Input: none
// Output: 1 if success, 0 if fail
int ESP8266_DisableServer(void);
// if rxecho is active, receive data is also streamed to ReceiveBuffer
void ESP8266_StartReceiveSearch(char *search);
char * ESP8266_GetReceiveBuffer(void);
#endif

212
RTOS_Labs_common/fixed.c Normal file

File diff suppressed because one or more lines are too long

78
RTOS_Labs_common/fixed.h Normal file
View File

@@ -0,0 +1,78 @@
/**
* @file fixed.h
* @brief trig functions using fixed point
* @details sin and cos functions. Input in decimal fixed point (units radians/1000) or
* binary fixed point (units 2*pi/16384).
* Output in either decimal fixed-point (units 1/10000) or binary fixed-point (units 1/65536)
* @version TI-RSLK MAX v1.1
* @author Daniel Valvano and Jonathan Valvano
* @copyright Copyright 2019 by Jonathan W. Valvano, valvano@mail.utexas.edu,
* @warning AS-IS
* @note For more information see http://users.ece.utexas.edu/~valvano/
* @date June 28, 2019
******************************************************************************/
#include <stdint.h>
/*!
* @defgroup Math
* @brief
* @{*/
/**
* decimal fixed-point sin****************
* @param theta -3142 to 3142, angle is in units radians/1000
* @return -10000 to +10000, fixed point resolution 1/10000
* @note input theta=1571 means 90 degrees (pi/2 radians)
* @brief decimal fixed-point sin
*/
int16_t fixed_sin(int32_t theta);
/**
* decimal fixed-point cos****************
* @param theta -3142 to 3142, angle is in units radians/1000
* @return -10000 to +10000, fixed point resolution 1/10000
* @note input theta=-1571 means 90 degrees (pi/2 radians)
* @brief decimal fixed-point cos
*/
int16_t fixed_cos(int32_t theta);
/**
* binary fixed-point sin****************
* @param theta -8192 to 8191, angle is in units 2*pi/16384 radians (-pi to +pi)
* @return -65536 to +65536, fixed point resolution 1/65536
* @note input theta=-4096 means -90 degrees (pi/2 radians)
* @brief binary fixed-point sin
*/
int32_t fixed_sin2(int32_t theta);
/**
* binaryfixed-point cos****************
* @param theta -8192 to 8191, angle is in units 2*pi/16384 radians (-pi to +pi)
* @return -65536 to +65536, fixed point resolution 1/65536
* @note input theta=4096 means 90 degrees (pi/2 radians)
* @brief binary fixed-point cos
*/
int32_t fixed_cos2(int32_t theta);
/**
* binary fixed-point sin****************<br>
* e.g., 359.3degrees (2pi radians) theta=539 sin540(539) = -763 (-0.0116)<br>
* e.g., 270 degrees (3pi/4 radians) theta=405 sin540(405) = -65536 (-1)<br>
* e.g., 180 degrees (pi radians) theta=270 sin540(270) = 0 (0)<br>
* e.g., 90 degrees (pi/2 radians) theta=135 sin540(135) = 65536 (+1)<br>
* e.g., 45 degrees (pi/4 radians) theta= 67 sin540(67) = 46071 (sqrt(2)/2)<br>
* e.g., 0 degrees (0 radians) theta= 0 sin540(0) = 0 (0)<br>
* @param theta 0 to 539, angle is in units 2*pi/540 = 0.011635528 radians (0 to 2pi)
* @return -65536 to +65536, fixed point resolution 1/65536
* @brief binary fixed-point sin
*/
int32_t sin540(int32_t theta);
/**
* binaryfixed-point cos****************
* @param theta 0 to 539, angle is in units 2*pi/540 = 0.011635528 radians (0 to 2pi)
* @return -65536 to +65536, fixed point resolution 1/65536
* @note input theta=4096 means 90 degrees (pi/2 radians)
* @brief binary fixed-point cos
*/
int32_t cos540(int32_t theta);

Binary file not shown.

102
RTOS_Labs_common/heap.c Normal file
View File

@@ -0,0 +1,102 @@
// filename *************************heap.c ************************
// starter
#include <stdint.h>
#include "../RTOS_Labs_common/heap.h"
long StartCritical(void);
void EndCritical(long);
#define OSCRITICAL_ENTER() { sr = StartCritical(); }
#define OSCRITICAL_EXIT() { EndCritical(sr); }
//******** Heap_Init ***************
// Initialize the Heap
// input: none
// output: always HEAP_OK
// notes: Initializes/resets the heap to a clean state where no memory
// is allocated.
int32_t Heap_Init(void){
return 0;
}
//******** Heap_Malloc ***************
// Allocate memory, data not initialized
// input:
// desiredBytes: desired number of bytes to allocate
// output: void* pointing to the allocated memory or will return NULL
// if there isn't sufficient space to satisfy allocation request
void* Heap_Malloc(int32_t desiredBytes){ int sr;
return 0; //NULL
}
//******** Heap_Calloc ***************
// Allocate memory, data are initialized to 0
// input:
// desiredBytes: desired number of bytes to allocate
// output: void* pointing to the allocated memory block or will return NULL
// if there isn't sufficient space to satisfy allocation request
//notes: the allocated memory block will be zeroed out
void* Heap_Calloc(int32_t desiredBytes){
return 0; //NULL
}
//******** Heap_Realloc ***************
// Reallocate buffer to a new size
//input:
// oldBlock: pointer to a block
// desiredBytes: a desired number of bytes for a new block
// where the contents of the old block will be copied to
// output: void* pointing to the new block or will return NULL
// if there is any reason the reallocation can't be completed
// notes: the given block will be unallocated after its contents
// are copied to the new block
void* Heap_Realloc(void* oldBlock, int32_t desiredBytes){
return 0; // NULL
}
//******** Heap_Free ***************
// return a block to the heap
// input: pointer to memory to unallocate
// output: HEAP_OK if everything is ok;
// HEAP_ERROR_POINTER_OUT_OF_RANGE if pointer points outside the heap;
// HEAP_ERROR_CORRUPTED_HEAP if heap has been corrupted or trying to
// unallocate memory that has already been unallocated;
int32_t Heap_Free(void* pointer){ int sr;
return 0;
}
//******** Heap_Test ***************
// Test the heap
// input: none
// output: validity of the heap - either HEAP_OK or HEAP_ERROR_HEAP_CORRUPTED
int32_t Heap_Test(void){
return 0;
}
//******** Heap_Stats ***************
// return the current status of the heap
// input: none
// output: a heap_stats_t that describes the current usage of the heap
int32_t Heap_Stats(heap_stats_t *stats){
return 0;
}

87
RTOS_Labs_common/heap.h Normal file
View File

@@ -0,0 +1,87 @@
/**
* @file heap.h
* @brief heap memory manager
* @details Dynamic memory management on a heap
* @version V1.0
* @author Valvano (originally by Jacob Egner)
* @copyright Copyright 2026 by Jonathan W. Valvano, valvano@mail.utexas.edu,
* @warning AS-IS
* @note For more information see http://users.ece.utexas.edu/~valvano/
* @date Jan 10, 2026
******************************************************************************/
#ifndef HEAP_H
#define HEAP_H
#include <stdint.h>
// struct for holding statistics on the state of the heap
typedef struct heap_stats {
uint32_t size; // heap size (in bytes)
uint32_t used; // number of bytes used/allocated
uint32_t free; // number of bytes available to allocate
} heap_stats_t;
/**
* @details Initialize the Heap
* @param none
* @return always 0 (success)
* @brief Initializes/resets the heap to a clean state where no memory
* is allocated.
*/
int32_t Heap_Init(void);
/**
* @details Allocate memory, data not initialized
* @param desiredBytes: desired number of bytes to allocate
* @return void* pointing to the allocated memory or will return NULL
* if there isn't sufficient space to satisfy allocation request
* @brief Allocate memory
*/
void* Heap_Malloc(int32_t desiredBytes);
/**
* @details Allocate memory, allocated memory is initialized to 0 (zeroed out)
* @param desiredBytes: desired number of bytes to allocate
* @return void* pointing to the allocated memory block or will return NULL
* if there isn't sufficient space to satisfy allocation request
* @brief Zero-allocate memory
*/
void* Heap_Calloc(int32_t desiredBytes);
/**
* @details Reallocate buffer to a new size. The given block may be
* unallocated and its contents copied to a new block
* @param oldBlock: pointer to a block
* @param desiredBytes: a desired number of bytes for a new block
* @return void* pointing to the new block or will return NULL
* if there is any reason the reallocation can't be completed
* @brief Grow/shrink memory
*/
void* Heap_Realloc(void* oldBlock, int32_t desiredBytes);
/**
* @details Return a block to the heap
* @param pointer to memory to unallocate
* @return 0 if everything is ok, non-zero in case of error (e.g. invalid pointer
* or trying to unallocate memory that has already been unallocated)
* @brief Free memory
*/
int32_t Heap_Free(void* pointer);
/**
* @details Return the current usage status of the heap
* @param reference to a heap_stats_t that returns the current usage of the heap
* @return 0 in case of success, non-zeror in case of error (e.g. corrupted heap)
* @brief Get heap usage
*/
int32_t Heap_Stats(heap_stats_t *stats);
#endif //#ifndef HEAP_H

257
RTOS_Labs_common/osasm.s Normal file
View File

@@ -0,0 +1,257 @@
/* ***************************************************************************
; osasm.s: low-level OS commands, written in assembly
; derived from uCOS-II
;****************************************************************************
;OS Lab2/3/4/5/6
;Students will implement these functions as part of ECE445M Lab
;Starter to labs 1,2,3,4
; Jonathan Valvano
; December 21, 2025
;
;Copyright 2025 by Jonathan W. Valvano, valvano@mail.utexas.edu
; You may use, edit, run or distribute this file
; as long as the above copyright notice remains
; THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED
; OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
; MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
; VALVANO SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
; OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
; For more information about my classes, my research, and my books, see
; http://users.ece.utexas.edu/~valvano/
;
*/
.data
.align 2
// Declare global variables here if needed
// with the .space assembly directive
.text
.thumb
.align 2
.global RunPt // currently running thread
.global NextThreadPt // next thread to run, set by schedule
.global Scheduler
.global SysTick_Handler
.global TimeMs
.global OS_MsTime
.global OS_ClearMsTime
.global Scheduler
.global ReadyLists
.global RunPts
.global StartOS
.global ContextSwitch2
.global PendSV_Handler
.global OSDisableInterrupts
.global OSEnableInterrupts
.global SVC_Handler
.global StartCritical
.global EndCritical
.include "../inc/msp.s" // For profiling
.macro TogglePB22
LDR R0, =GPIOB_DOUTTGL31_0
LDR R1, =(1 << 22)
STR R1, [R0]
.endm
.equ POINTERSIZE, 4 // bytes
.equ TCB_NEXT, 4 // offset within struct
OSDisableInterrupts:
CPSID I
BX LR
OSEnableInterrupts:
CPSIE I
BX LR
// *********** StartCritical ************************
//make a copy of previous I bit, disable interrupts
// inputs : none
// outputs : R0=previous I bit
StartCritical:
MRS R0, PRIMASK // save old status
CPSID I // mask all (except faults)
BX LR
// *********** EndCritical ************************
// using the copy of previous I bit, restore I bit to previous value
// inputs : R0=previous I bit
// outputs : none
EndCritical:
MSR PRIMASK, R0
BX LR
// ******** OS_ClearMsTime ************
// sets the system time to zero (solve for Lab 1), and start a periodic interrupt
// Inputs: none
// Outputs: none
// You are free to change how this works
OS_ClearMsTime:
PUSH {LR}
BL StartCritical // Save I bit
LDR R2, =TimeMs
MOVS R1, #0
STR R1, [R2]
BL EndCritical // Restore I bit
LDR R0, =10000 //period
MOVS R1, 4 //prescale
MOVS R2, 1 //priority
BL TimerG8_IntArm //40MHz Clock - will arm @ 1kHz
POP {PC}
// ******** OS_MsTime ************
// reads the current time in msec (solve for Lab 1)
// Inputs: none
// Outputs: time in ms units
// You are free to select the time resolution for this function
// For Labs 2 and beyond, it is ok to make the resolution to match the first call to OS_AddPeriodicThread
OS_MsTime:
LDR R0, =TimeMs
LDR R0, [R0]
BX LR
// Scheduler handles the algorithm for switching threads
Scheduler:
MOVS R0, #0 // priorityReady
LDR R1, =ReadyLists
readyListLoop: // Traverse ReadyLists array looking for highest priority thread that is ready
LDR R2, [R1, R0]
CMP R2, #0
BNE readyFound
ADDS R0, R0, #POINTERSIZE // Keep looking if NULL, will break if no ready threads
B readyListLoop
readyFound:
LDR R1, =RunPts
ADDS R1, R0 // R1 = RunPts + priorityReady Pointer to TCB pointer
LDR R0, [R1] // R0 = RunPts[priorityReady] Pointer to actual TCB
LDR R0, [R0, #TCB_NEXT] // R0 = RunPts[priorityReady]->next
LDR R2, =RunPt
STR R0, [R2] // RunPt = RunPts[priorityReady]->next
STR R0, [R1] // RunPts[priorityReady] = RunPt
BX LR
StartOS:
// put your code here
LDR R0, =RunPt // Pointer to currently running thread
LDR R1, [R0] // R1 = value of RunPt
LDR R2, [R1] // new thread SP
MOV SP, R2 // Switch stack pointer
POP {R4-R7} // Restore R4-R7
POP {R0-R3} // Restore R0-R3
ADD SP, SP, #8 // Discard R12 and LR
POP {R0} // start location
ADD SP, SP, #4 // Discard PSR
CPSIE I // Enable interrupts
BX R0 // Start first thread
OSStartHang:
B OSStartHang // Should never get here
/* *******************************************************************************************************
; HANDLE PendSV EXCEPTION
; void PendSV_Handler(void)
;
; Note(s) : 1) PendSV is used to cause a context switch. This is a recommended method for performing
; context switches with Cortex-M. This is because the Cortex-M auto-saves half of the
; processor context on any exception, and restores same on return from exception. So only
; saving of R4-R7 is required and fixing up the stack pointers. Using the PendSV exception
; this way means that context saving and restoring is identical whether it is initiated from
; a thread or occurs due to an interrupt or exception.
;
; 2) Pseudo-code is:
; a) disable interrupts so context switch is atomic
; b) Push remaining R4-R7 on stack;
; c) Save the SP in its TCB,
; d) Get next thread to run from the OS.c
; e) Load SP of next thread into SP
; f) Pop R4-R7 from new stack;
; g) enable interrupts
; i) Perform exception return which will restore remaining context.
;
; 3) On entry into PendSV handler:
; a) The following have been saved on the process stack (by processor):
; xPSR, PC, LR, R12, R0-R3
; b) RunPt points to the tcb of the task to suspend
; c) NextThreadPt points to the tcb of the task to resume
;
; 4) Since PendSV is set to lowest priority in the system, we
; know that it will only be run when no other exception or interrupt is active, and
; therefore safe to assume that context being switched out was a user thread and not an ISR.
;
; 5) We will assume (hope) the compiler does not use R8-R11
;
;********************************************************************************************************/
.type PendSV_Handler, %function
// Only ISR running at priority 3 guarantees it interrupts only main/foreground code
PendSV_Handler:
// put your code here
TogglePB22 // For profiling
TogglePB22 // For profiling
CPSID I // Prevent interrupt during switch
PUSH {R4-R7}
LDR R4, =RunPt
LDR R1, [R4]
MOV R2, SP
STR R2, [R1] // Save SP into old thread's TCB
MOV R5, LR // Preserve LR when calling scheduler
BL Scheduler // Call scheduler to pick the next thread
MOV LR, R5 // Restore LR
LDR R1, [R4] // R1 = RunPt
LDR R2, [R1] // SP of new thread
MOV SP, R2
POP {R4-R7}
CPSIE I // Re-enable interrupts
TogglePB22 // For profiling
BX LR // Exception return will restore remaining context
/* *******************************************************************************************************
; HANDLE SVC EXCEPTION
; void SVC_Handler(void)
;
; Note(s) : SVC is a software-triggered exception to make OS kernel calls from user land.
; The function ID to call is encoded in the instruction itself, the location of which can be
; found relative to the return address saved on the stack on exception entry.
; Function-call paramters in R0..R3 are also auto-saved on stack on exception entry.
;
;********************************************************************************************************/
.type OS_Id, %function
.type OS_Kill, %function
.type OS_Sleep, %function
.type OS_Time, %function
.type OS_AddThread, %function
.type ST7735_Message, %function
.global OS_Id
.global OS_Kill
.global OS_Sleep
.global OS_Time
.global OS_AddThread
.global ST7735_Message
.type SVC_Handler, %function
SVC_Handler:
PUSH {R4-R5,LR}
POP {R4-R5,PC}
SVCJumpTable:
.long OS_Id
.long OS_Kill
.long OS_Sleep
.long OS_Time
.long OS_AddThread
.long ST7735_Message
.end

View File

@@ -0,0 +1,22 @@
This folder contains files for labs associated with the book
"Embedded Systems: Real-Time Operating Systems for ARM Cortex M Microcontrollers",
ISBN: 978-1466468863, Jonathan Valvano, copyright (c) 2026
The files in this folder are not complete, and will be completed my students doing the labs. For the most recent information on these labs search "EE445M/EE380L.12 utexas", which is currently being taught by Andreas Gerstlauer.
Copyright 2026 by Jonathan W. Valvano, valvano@mail.utexas.edu
You may use, edit, run or distribute these file
s as long as the above copyright notice remains
THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED
OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
VALVANO SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
For more information about my classes, my research, and my books, see
http://users.ece.utexas.edu/~valvano/
June 12, 2025