all
460
RTOS_Labs_common/ADC.c
Normal 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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__
|
||||
|
||||
BIN
RTOS_Labs_common/CANwiring.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
22
RTOS_Labs_common/CriticalSection.s
Normal 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
@@ -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
@@ -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
BIN
RTOS_Labs_common/DRV8871_CurrentPaths.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
RTOS_Labs_common/DRV8871_HbridgeControl.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
RTOS_Labs_common/DRV8871_PeakCurrent_2A.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
RTOS_Labs_common/DigitalNotch60Hz.xls
Normal file
BIN
RTOS_Labs_common/DigitalNotchFilter.xls
Normal file
2206
RTOS_Labs_common/FFT.c
Normal file
82
RTOS_Labs_common/FFT.h
Normal 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
@@ -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__
|
||||
104
RTOS_Labs_common/IRDistance.c
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
88
RTOS_Labs_common/IRDistance.h
Normal 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_ */
|
||||
229
RTOS_Labs_common/Interpreter.c
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
39
RTOS_Labs_common/Interpreter.h
Normal 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);
|
||||
|
||||
BIN
RTOS_Labs_common/LCD_SDCinterface.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
364
RTOS_Labs_common/LD19.c
Normal 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%
|
||||
Header:The length is 1 byte, and the value is fixed at 0x54,
|
||||
indicating the beginning of the data packet;
|
||||
VerLen:The 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
|
||||
Speed:The 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
|
||||
Data:Indicates 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;
|
||||
Timestamp:The length is 2 bytes, the unit is milliseconds, and the maximum is 30000.
|
||||
When it reaches 30000, it will be counted again
|
||||
CRC check:The 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
@@ -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__
|
||||
|
||||
/** @}*/
|
||||
BIN
RTOS_Labs_common/LD19_LA1.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
RTOS_Labs_common/LD19_LA2.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
RTOS_Labs_common/LD19_LA3.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
519
RTOS_Labs_common/LPF.c
Normal 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
@@ -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);
|
||||
144
RTOS_Labs_common/LaunchPad.c
Normal 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;
|
||||
}
|
||||
|
||||
314
RTOS_Labs_common/LaunchPad.h
Normal 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
388
RTOS_Labs_common/OS.h
Normal 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
|
||||
BIN
RTOS_Labs_common/PD19_example.png
Normal file
|
After Width: | Height: | Size: 232 KiB |
BIN
RTOS_Labs_common/PD19_figure.png
Normal file
|
After Width: | Height: | Size: 179 KiB |
BIN
RTOS_Labs_common/PD19_message.png
Normal file
|
After Width: | Height: | Size: 96 KiB |
BIN
RTOS_Labs_common/PD19_pins.png
Normal file
|
After Width: | Height: | Size: 89 KiB |
129
RTOS_Labs_common/PWMA0.c
Normal 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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__
|
||||
/** @}*/
|
||||
|
||||
85
RTOS_Labs_common/RTOS_FIFO.c
Normal 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);
|
||||
}
|
||||
|
||||
159
RTOS_Labs_common/RTOS_FIFO.h
Normal 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
@@ -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
@@ -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__
|
||||
/** @}*/
|
||||
|
||||
402
RTOS_Labs_common/RTOS_UART.c
Normal 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
|
||||
|
||||
}
|
||||
|
||||
241
RTOS_Labs_common/RTOS_UART.h
Normal 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__
|
||||
/** @}*/
|
||||
160
RTOS_Labs_common/RTOS_UARTints.c
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
RTOS_Labs_common/RobotSensors.png
Normal file
|
After Width: | Height: | Size: 728 KiB |
280
RTOS_Labs_common/SPI.c
Normal 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
@@ -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
@@ -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
840
RTOS_Labs_common/ST7735.h
Normal 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
|
||||
/** @}*/
|
||||
2128
RTOS_Labs_common/ST7735_SDC.c
Normal file
814
RTOS_Labs_common/ST7735_SDC.h
Normal 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
|
||||
/** @}*/
|
||||
81
RTOS_Labs_common/SchedulerFinder.c
Normal 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;
|
||||
}
|
||||
BIN
RTOS_Labs_common/SensorBoard.png
Normal file
|
After Width: | Height: | Size: 530 KiB |
BIN
RTOS_Labs_common/Servo_G6.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
415
RTOS_Labs_common/TFLuna1.c
Normal 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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__
|
||||
|
||||
/** @}*/
|
||||
26
RTOS_Labs_common/TFLunaCommon.c
Normal 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
@@ -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
@@ -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__
|
||||
/** @}*/
|
||||
44
RTOS_Labs_common/WifiSettings.h
Normal 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
@@ -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
@@ -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
@@ -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
@@ -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
211
RTOS_Labs_common/esp8266.h
Normal 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
78
RTOS_Labs_common/fixed.h
Normal 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);
|
||||
BIN
RTOS_Labs_common/fixedTrig.xlsx
Normal file
102
RTOS_Labs_common/heap.c
Normal 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
@@ -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
@@ -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
|
||||
|
||||
22
RTOS_Labs_common/readme.txt
Normal 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
|
||||