12 Commits

Author SHA1 Message Date
3593d8cbd2 Working 2025-11-14 09:37:35 -08:00
Ea-r-th
7a9cd2f70e Ensuring up to date 2025-11-12 15:26:16 -08:00
Ea-r-th
ec0fea608b Finish project 2025-11-11 19:51:21 -08:00
Ea-r-th
b50e7c25f6 Finish testing stage 2025-11-07 00:20:43 -08:00
Ea-r-th
7a24078e18 Most functionality for seat working 2025-11-06 19:58:38 -08:00
Ea-r-th
63fab62727 For sensor project 2025-11-06 02:40:54 -08:00
Ea-r-th
a1458de235 Fixed PWM mode 2025-11-06 00:41:46 -08:00
Ea-r-th
12aedf1ff9 Abstracted registers for PWM and other timer modes 2025-11-05 18:15:32 -08:00
Ea-r-th
aa7a041946 Updated GPIO to new format 2025-11-03 23:17:55 -08:00
Ea-r-th
0b4a6ef584 Known working minimized setup 2025-10-23 20:52:04 -07:00
Ea-r-th
e41cf30c87 Fixed ADC functionality 2025-10-19 18:33:18 -07:00
Ea-r-th
2c5592c2d3 Refactors for L432KC done for all peripherals except GPIO 2025-10-19 01:49:13 -07:00
39 changed files with 1126 additions and 492 deletions

View File

@@ -7,7 +7,7 @@ set(MCU_FAMILY "STM32L4xx" CACHE STRING "MCU family")
set(MCU_MODEL "STM32L432xx" CACHE STRING "MCU model")
set(CPU_PARAMETERS
-mcpu=cortex-m0
-mcpu=cortex-m4
-mthumb)
set(STARTUP_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/MX/L432KC/startup_stm32l432kcux.s)

View File

@@ -45,8 +45,8 @@
* @brief Configuration of the Cortex-M4 Processor and Core Peripherals
*/
#define __CM4_REV 0x0001U /*!< Cortex-M4 revision r0p1 */
#define __MPU_PRESENT 1U /*!< STM32L4XX provides an MPU */
#define __NVIC_PRIO_BITS 4U /*!< STM32L4XX uses 4 Bits for the Priority Levels */
#define __MPU_PRESENT 1U /*!< STM32L4xx provides an MPU */
#define __NVIC_PRIO_BITS 4U /*!< STM32L4xx uses 4 Bits for the Priority Levels */
#define __Vendor_SysTickConfig 0U /*!< Set to 1 if different SysTick Config is used */
#define __FPU_PRESENT 1U /*!< FPU present */
@@ -59,7 +59,7 @@
*/
/**
* @brief STM32L4XX Interrupt Number Definition, according to the selected device
* @brief STM32L4xx Interrupt Number Definition, according to the selected device
* in @ref Library_configuration_section
*/
typedef enum

View File

@@ -5,6 +5,9 @@ BUILD_DIR := build
BUILD_TYPE ?= Debug
TOOLCHAIN := gcc-arm-none-eabi.cmake
CFLAGS_DEBUG ?= -g3 -O0
CXXFLAGS_DEBUG ?= -g3 -O0
# MCU target (override on command line: make build MCU_MODEL=STM32F051x8)
MCU_MODEL ?= STM32L432xx
MCU_FAMILY ?= STM32L4xx
@@ -20,6 +23,8 @@ ${BUILD_DIR}/build.ninja:
-DCMAKE_BUILD_TYPE=${BUILD_TYPE} \
-DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN} \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DCMAKE_C_FLAGS_DEBUG="${CFLAGS_DEBUG}" \
-DCMAKE_CXX_FLAGS_DEBUG="${CXXFLAGS_DEBUG}" \
-DMCU_MODEL=$(MCU_MODEL) \
-DMCU_FAMILY=$(MCU_FAMILY)

View File

@@ -10,6 +10,7 @@
#define SHMINGO_HAL_SHAL_CORE_H
#include <cstdint>
#include <cstddef>
//Overall init function for SHAL --------------------------
@@ -70,30 +71,59 @@ bool SHAL_wait_for_condition_ms(Condition cond, uint32_t timeout_ms) {
return false; // timeout
}
//Sets bits starting from offset as the LSB
static inline void SHAL_set_bits(volatile uint32_t* reg, uint32_t size, uint32_t bits, uint32_t offset){
uint32_t mask = (1 << (size)) - 1;
*reg &= ~(mask << offset);
*reg |= bits << offset;
}
#define SHAL_set_bits(reg, size, bits, offset) \
do { \
if ((reg) != NULL) { \
uint32_t _mask = ((1U << (size)) - 1U); \
*(reg) &= ~((uint32_t)(_mask) << (offset)); \
*(reg) |= ((uint32_t)(bits) << (offset)); \
} \
} while (0)
//Sets bits starting from offset as the LSB (for uint16_t)
static inline void SHAL_set_bits_16(volatile uint16_t* reg, uint32_t size, uint32_t bits, uint32_t offset){
uint16_t mask = (1 << (size)) - 1;
*reg &= ~(mask << offset);
*reg |= bits << offset;
}
#define SHAL_flip_bits(reg, size, offset) \
do { \
if ((reg) != NULL) { \
uint32_t _mask = ((1U << (size)) - 1U); \
*(reg) ^= (_mask << (offset)); \
} \
} while (0)
static inline void SHAL_clear_bitmask(volatile uint32_t* reg, uint32_t mask){
*reg &= ~(mask);
}
#define SHAL_set_bits_16(reg, size, bits, offset) \
do { \
if ((reg) != NULL) { \
uint16_t _mask = (uint16_t)((1U << (size)) - 1U); \
*(reg) &= (uint16_t)~((uint16_t)(_mask) << (offset)); \
*(reg) |= (uint16_t)((uint16_t)(bits) << (offset)); \
} \
} while (0)
static inline void SHAL_apply_bitmask(volatile uint32_t* reg, uint32_t mask){
SHAL_clear_bitmask(reg,mask);
*reg |= mask;
}
#define SHAL_clear_bitmask(reg, mask) \
do { \
*(reg) &= ~(mask); \
} while (0)
#define SHAL_apply_bitmask(reg, mask) \
do { \
SHAL_clear_bitmask((reg), (mask)); \
*(reg) |= (mask); \
} while (0)
#define SHAL_set_register_value(reg, value) \
do { \
if ((reg) != NULL) { \
*(reg) = (uint32_t)(value); \
} \
} while (0)
#define SHAL_set_register_value_16(reg, value) \
do { \
if ((reg) != NULL) { \
*(reg) = (uint16_t)(value); \
} \
} while (0)
void SHAL_print_register(const volatile uint32_t* reg);
//---------------------------------------------------------

View File

@@ -16,7 +16,7 @@
#define NUM_ADC_CHANNELS 16
enum class SHAL_ADC_Channel : uint32_t {
CH0,
CH0 = 0,
CH1,
CH2,
CH3,
@@ -32,9 +32,11 @@ enum class SHAL_ADC_Channel : uint32_t {
CH13,
CH14,
CH15,
CH16,
CHTemp,
CHRef,
CHBat
CHBat,
NO_ADC_MAPPING
};
enum class ADC_Key : uint8_t{
@@ -43,11 +45,11 @@ enum class ADC_Key : uint8_t{
INVALID = 255
};
enum class ADC_Clock_Source : uint8_t {
SHAL_SYSCLK,
SHAL_PLLSAI1,
SHAL_PLL,
SHAL_MSI
enum class ADC_Clock_Source : uint32_t {
SHAL_NO_CLOCK = 0x00,
SHAL_PLLSAI1 = 0x01,
SHAL_PLLSYS = 0x02,
SHAL_SYSCLK = 0x03,
};
static volatile ADC_TypeDef* ADC_TABLE[1] = { //Lookup table for ADCs
@@ -59,15 +61,21 @@ static inline SHAL_ADC_Common_Control_Reg getADCCommonControl() {
}
static inline SHAL_ADC_RCC_Enable_Reg getADCRCCEnableRegister(ADC_Key key){
SHAL_ADC_RCC_Enable_Reg res = {nullptr, RCC_AHB2ENR_ADCEN};
SHAL_ADC_RCC_Enable_Reg res = {&RCC->AHB2ENR, RCC_AHB2ENR_ADCEN};
res.reg = &(ADC_TABLE[static_cast<uint8_t>(key)]->ISR);
return res;
}
static inline SHAL_ADC_Control_Reg getADCControlReg(ADC_Key key) {
SHAL_ADC_Control_Reg res = {nullptr, ADC_CR_ADEN, ADC_CR_ADDIS, ADC_CR_ADCAL, ADC_CR_ADSTART};
SHAL_ADC_Control_Reg res = {nullptr, ADC_CR_ADEN,
ADC_CR_ADSTP,
ADC_CR_ADDIS,
ADC_CR_ADCAL,
ADC_CR_ADSTART,
ADC_CR_DEEPPWD,
ADC_CR_ADVREGEN,
ADC_CR_ADCALDIF};
res.reg = &(ADC_TABLE[static_cast<uint8_t>(key)]->CR);
return res;
@@ -75,14 +83,14 @@ static inline SHAL_ADC_Control_Reg getADCControlReg(ADC_Key key) {
static inline SHAL_ADC_Config_Reg getADCConfigReg(ADC_Key key) {
SHAL_ADC_Config_Reg res = {nullptr, ADC_CFGR_CONT, ADC_CFGR_RES_Pos, ADC_CFGR_ALIGN_Pos};
SHAL_ADC_Config_Reg res = {nullptr, ADC_CFGR_CONT, ADC_CFGR_RES_Pos, ADC_CFGR_ALIGN_Pos, ADC_CFGR_CONT_Msk};
res.reg = &(ADC_TABLE[static_cast<uint8_t>(key)]->CFGR);
return res;
}
static inline SHAL_ADC_ISR_Reg getADCISRReg(ADC_Key key){
SHAL_ADC_ISR_Reg res = {nullptr, ADC_ISR_EOC, ADC_ISR_EOS, ADC_ISR_ADRDY};
SHAL_ADC_ISR_Reg res = {nullptr, ADC_ISR_EOC, ADC_ISR_EOS, ADC_ISR_ADRDY, ADC_ISR_OVR};
res.reg = &(ADC_TABLE[static_cast<uint8_t>(key)]->ISR);
return res;
@@ -95,19 +103,9 @@ static inline SHAL_ADC_Data_Reg getADCDataReg(ADC_Key key){
return res;
}
static inline SHAL_ADC_Clock_Reg getADCClockSelectRegister(ADC_Clock_Source clockSource) {
SHAL_ADC_Clock_Reg res = {&RCC->CCIPR, RCC_CCIPR_ADCSEL_Msk, 1U << RCC_CCIPR_ADCSEL_Pos}; //Default to PLLSAI1
static inline SHAL_ADC_Clock_Reg getADCClockSelectRegister() {
SHAL_ADC_Clock_Reg res = {&RCC->CCIPR, RCC_CCIPR_ADCSEL_Pos}; //Position
switch(clockSource){
case ADC_Clock_Source::SHAL_PLLSAI1:
res.mask = 1U << RCC_CCIPR_ADCSEL_Pos;
case ADC_Clock_Source::SHAL_PLL:
res.mask = 2U << RCC_CCIPR_ADCSEL_Pos;
case ADC_Clock_Source::SHAL_SYSCLK:
res.mask = 3U << RCC_CCIPR_ADCSEL_Pos;
case ADC_Clock_Source::SHAL_MSI:
break; //TODO implement this
}
return res;
}
@@ -120,10 +118,10 @@ static inline SHAL_ADC_Channel_Sampling_Time_Reg getADCChannelSamplingTimeRegist
auto channelNum = static_cast<uint8_t>(channel);
if (channelNum <= 9) {
SMPReg = &ADCReg->SQR1;
SMPReg = &ADCReg->SMPR1;
pos = (channelNum * 3);
} else {
SMPReg = &ADCReg->SQR2;
SMPReg = &ADCReg->SMPR2;
pos = ((channelNum - 10) * 3);
}
@@ -148,11 +146,11 @@ static inline SHAL_ADC_Sequence_Reg getADCSequenceRegisters(ADC_Key key){
&adc_reg->SQR4,
nullptr,
nullptr},
{1UL << 0,
1UL << 6,
1UL << 12,
1UL << 18,
1UL << 24}
{0,
6,
12,
18,
24}
};
return res;

View File

@@ -16,7 +16,7 @@ class SHAL_ADC {
public:
SHAL_Result init();
SHAL_Result init(ADC_Key key);
SHAL_Result calibrate();
@@ -54,6 +54,9 @@ private:
//Disables peripheral
SHAL_Result disable();
//Wake up ADC from initial deep sleep state
SHAL_Result wakeFromDeepSleep();
SHAL_Result startConversion();
/// Adds an ADC channel to the conversion sequence

View File

@@ -23,9 +23,13 @@ struct SHAL_ADC_RCC_Enable_Reg {
struct SHAL_ADC_Control_Reg {
volatile uint32_t* reg;
uint32_t enable_mask;
uint32_t stop_mask;
uint32_t disable_mask;
uint32_t calibration_mask;
uint32_t start_mask;
uint32_t deep_power_down_mask;
uint32_t voltage_regulator_mask;
uint32_t differential_mode_mask;
};
//Register controlling ADC configuration
@@ -35,6 +39,7 @@ struct SHAL_ADC_Config_Reg {
uint32_t resolution_offset;
uint32_t alignment_offset;
uint32_t continuous_mode_mask;
};
//Register for all ADC data
@@ -49,13 +54,13 @@ struct SHAL_ADC_ISR_Reg {
uint32_t end_of_conversion_mask;
uint32_t end_of_sequence_mask;
uint32_t ready_mask;
uint32_t overrun_mask;
};
//Register controlling the clock source for the ADC
struct SHAL_ADC_Clock_Reg {
volatile uint32_t* reg;
uint32_t clear;
uint32_t mask;
uint32_t offset;
};
//Register controlling the sampling time of ADC samples

View File

@@ -10,19 +10,28 @@
#define EXTI_PENDING_REG(line) ((line) < 32 ? EXTI->PR1 : EXTI->PR2)
static inline SHAL_EXTI_Control_Register getEXTIControlRegister(uint32_t line){
uint8_t maskOffset = line % 4; //Each register has four 4-bit wide fields
uint8_t registerOffset = line / 4; //Composed of four registers with 4 fields each
SHAL_EXTI_Control_Register res = {&SYSCFG->EXTICR[registerOffset], maskOffset};
return res;
}
static inline SHAL_EXTI_Interrupt_Mask_Register getEXTIInterruptMaskRegister(uint32_t line){
auto imr = line < 32 ? EXTI->IMR1 : EXTI->IMR2;
return {&imr};
volatile uint32_t* reg = line < 32 ? &EXTI->IMR1 : &EXTI->IMR2;
return {reg};;
}
static inline SHAL_EXTI_Rising_Trigger_Selection_Register getEXTIRisingTriggerSelectionRegister(uint32_t line){
auto reg = line < 32 ? EXTI->RTSR1 : EXTI->RTSR2;
return {&reg};
volatile uint32_t* reg = line < 32 ? &EXTI->RTSR1 : &EXTI->RTSR2;
return {reg};
}
static inline SHAL_EXTI_Falling_Trigger_Selection_Register getEXTIFallingTriggerSelectionRegister(uint32_t line){
auto reg = line < 32 ? EXTI->FTSR1 : EXTI->FTSR2;
return {&reg};
volatile uint32_t* reg = line < 32 ? &EXTI->FTSR1 : &EXTI->FTSR2;
return {reg};
}
#endif //SHMINGO_HAL_SHAL_EXTI_REG_L432KC_H

View File

@@ -19,4 +19,9 @@ struct SHAL_EXTI_Falling_Trigger_Selection_Register {
volatile uint32_t* reg;
};
struct SHAL_EXTI_Control_Register {
volatile uint32_t* reg;
uint32_t offset;
};
#endif //SHMINGO_HAL_SHAL_EXTI_TYPES_H

View File

@@ -10,55 +10,69 @@
#include "SHAL_GPIO_TYPES.h"
#define AVAILABLE_PORTS 3
#define AVAILABLE_PORTS 2
#define PINS_PER_PORT 16
#define NUM_EXTI_LINES 16
#define AVAILABLE_GPIO \
X(A0) X(A1) X(A2) X(A3) X(A4) X(A5) X(A6) X(A7) X(A8) X(A9) X(A10) X(A11) X(A12) X(A13) X(A14) X(A15) \
X(B0) X(B1) X(B3) X(B4) X(B5) X(B6) X(B7)
//Build enum map of available SHAL_GPIO pins
enum class GPIO_Key : uint8_t {
#define X(key) key,
AVAILABLE_GPIO
#undef X
A0 = 0,
A1 = 1,
A2 = 2,
A3 = 3,
A4 = 4,
A5 = 5,
A6 = 6,
A7 = 7,
A8 = 8,
A9,
A10,
A11,
A12,
A13,
A14,
A15,
B0,
B1,
B3 = 19, //Offset to compensate for lack of B2
B4,
B5,
B6,
B7,
NUM_GPIO,
INVALID
};
constexpr SHAL_GPIO_Peripheral getGPIORegister(const GPIO_Key g){
switch(g) {
case GPIO_Key::A0: return {GPIOA,0};
case GPIO_Key::A1: return {GPIOA,1};
case GPIO_Key::A2: return {GPIOA,2};
case GPIO_Key::A3: return {GPIOA,3};
case GPIO_Key::A4: return {GPIOA,4};
case GPIO_Key::A5: return {GPIOA,5};
case GPIO_Key::A6: return {GPIOA,6};
case GPIO_Key::A7: return {GPIOA,7};
case GPIO_Key::A8: return {GPIOA,8};
case GPIO_Key::A9: return {GPIOA,9};
case GPIO_Key::A10: return {GPIOA,10};
case GPIO_Key::A11: return {GPIOA,11};
case GPIO_Key::A12: return {GPIOA,12};
case GPIO_Key::A13: return {GPIOA,13};
case GPIO_Key::A14: return {GPIOA,14};
case GPIO_Key::A15: return {GPIOA,15};
case GPIO_Key::B0: return {GPIOB,0};
case GPIO_Key::B1: return {GPIOB,1};
case GPIO_Key::B3: return {GPIOB,3};
case GPIO_Key::B4: return {GPIOB,4};
case GPIO_Key::B5: return {GPIOB,5};
case GPIO_Key::B6: return {GPIOB,6};
case GPIO_Key::B7: return {GPIOB,7};
case GPIO_Key::INVALID:
case GPIO_Key::NUM_GPIO:
assert(false);
return SHAL_GPIO_Peripheral(nullptr,0); //Unreachable
enum class GPIO_Alternate_Function_Mapping {
A0_TIM2CH1 = 0x01,
A1_TIM2CH2 = 0x01,
A2_TIM2CH3 = 0x01,
A3_TIM2CH4 = 0x01,
A5_TIM2CH1 = 0x01,
A6_TIM1BKIN = 0x01,
A7_TIM1CH1N = 0x01,
A8_TIM1CH1 = 0x01,
A9_TIM1CH2 = 0x01,
A10_TIM1CH3 = 0x01,
A11_TIM1CH4 = 0x01,
A12_TIM1ETR = 0x01,
A15_TIM2CH1 = 0x01,
B0_TIM1CH2N = 0x01,
B1_TIM1CH3N = 0x01,
};
static volatile GPIO_TypeDef * GPIO_TABLE[2] = { //Lookup table for ADCs
GPIOA,
GPIOB
};
constexpr uint8_t getGPIOPinNumber(GPIO_Key key){
return static_cast<uint8_t>(key) % 16;
}
__builtin_unreachable();
constexpr uint8_t getGPIOPortNUmber(GPIO_Key key){
return static_cast<uint8_t>(key) / 16;
}
constexpr SHAL_GPIO_EXTI_Register getGPIOEXTICR(const GPIO_Key g){
@@ -96,119 +110,115 @@ constexpr SHAL_GPIO_EXTI_Register getGPIOEXTICR(const GPIO_Key g){
__builtin_unreachable();
}
constexpr SHAL_Peripheral_Register getGPIORCCEnable(const GPIO_Key g){
switch(g) {
case GPIO_Key::A0:
case GPIO_Key::A1:
case GPIO_Key::A2:
case GPIO_Key::A3:
case GPIO_Key::A4:
case GPIO_Key::A5:
case GPIO_Key::A6:
case GPIO_Key::A7:
case GPIO_Key::A8:
case GPIO_Key::A9:
case GPIO_Key::A10:
case GPIO_Key::A11:
case GPIO_Key::A12:
case GPIO_Key::A13:
case GPIO_Key::A14:
case GPIO_Key::A15:
return {&RCC->AHB2ENR, RCC_AHB2ENR_GPIOAEN_Pos};
case GPIO_Key::B0:
case GPIO_Key::B1:
case GPIO_Key::B3:
case GPIO_Key::B4:
case GPIO_Key::B5:
case GPIO_Key::B6:
case GPIO_Key::B7:
return {&RCC->AHB2ENR, RCC_AHB2ENR_GPIOBEN_Pos};
case GPIO_Key::INVALID:
case GPIO_Key::NUM_GPIO:
assert(false);
return SHAL_Peripheral_Register(nullptr,0); //Unreachable
}
__builtin_unreachable();
static inline SHAL_GPIO_RCC_Enable_Register getGPIORCCEnable(const GPIO_Key g){
volatile uint32_t* reg = &RCC->AHB2ENR; //register
uint32_t offset;
offset = (static_cast<uint8_t>(g) / 16) == 0 ? RCC_AHB2ENR_GPIOAEN_Pos : RCC_AHB2ENR_GPIOBEN_Pos;
return {reg,offset};
}
constexpr uint32_t getGPIOPortNumber(const GPIO_Key g){
switch(g) {
case GPIO_Key::A0:
case GPIO_Key::A1:
case GPIO_Key::A2:
case GPIO_Key::A3:
case GPIO_Key::A4:
case GPIO_Key::A5:
case GPIO_Key::A6:
case GPIO_Key::A7:
case GPIO_Key::A8:
case GPIO_Key::A9:
case GPIO_Key::A10:
case GPIO_Key::A11:
case GPIO_Key::A12:
case GPIO_Key::A13:
case GPIO_Key::A14:
case GPIO_Key::A15:
return 0;
case GPIO_Key::B0:
case GPIO_Key::B1:
case GPIO_Key::B3:
case GPIO_Key::B4:
case GPIO_Key::B5:
case GPIO_Key::B6:
case GPIO_Key::B7:
case GPIO_Key::INVALID:
case GPIO_Key::NUM_GPIO:
assert(false);
return 0;
return (static_cast<uint8_t>(g) / 16);
}
__builtin_unreachable();
static inline SHAL_GPIO_Mode_Register getGPIOModeRegister(const GPIO_Key key){
volatile uint32_t* reg = &GPIO_TABLE[static_cast<uint8_t>(key) / 16]->MODER;
uint32_t offset = 2 * (static_cast<uint8_t>(key) % 16);
return {reg,offset};
}
static inline SHAL_GPIO_Pullup_Pulldown_Register getGPIOPUPDRegister(const GPIO_Key key){
volatile uint32_t* reg = &GPIO_TABLE[static_cast<uint8_t>(key) / 16]->PUPDR;
uint32_t offset = 2 * static_cast<uint8_t>(key) % 16;
return {reg,offset};
}
static inline SHAL_GPIO_Alternate_Function_Register getGPIOAlternateFunctionRegister(const GPIO_Key key){
uint32_t pinNumber = static_cast<uint8_t>(key) % 16; //Number of pin (We need 0-7 to be AFR 1 and 8-15 to be AFR 2)
uint32_t afrIndex = pinNumber < 8 ? 0 : 1;
volatile uint32_t* reg = &GPIO_TABLE[static_cast<uint8_t>(key) / 16]->AFR[afrIndex];
uint32_t offset = (pinNumber % 8) * 4; //Increment in groups of four
return {reg,offset};
}
static inline SHAL_GPIO_Output_Speed_Register getGPIOOutputSpeedRegister(const GPIO_Key key){
volatile uint32_t* reg = &GPIO_TABLE[static_cast<uint8_t>(key) / 16]->OSPEEDR;
uint32_t offset = 2 * static_cast<uint8_t>(key) % 16;
return {reg,offset};
}
static inline SHAL_GPIO_Output_Type_Register getGPIOOutputTypeRegister(const GPIO_Key key){
volatile uint32_t* reg = &GPIO_TABLE[static_cast<uint8_t>(key) / 16]->OTYPER;
uint32_t offset = static_cast<uint8_t>(key) % 16;
return {reg,offset};
}
static inline SHAL_GPIO_Output_Data_Register getGPIOOutputDataRegister(const GPIO_Key key){
volatile uint32_t* reg = &GPIO_TABLE[static_cast<uint8_t>(key) / 16]->ODR;
uint32_t offset = (static_cast<uint8_t>(key) % 16);
return {reg,offset};
}
static inline SHAL_GPIO_Input_Data_Register getGPIOInputDataRegister(const GPIO_Key key){
volatile uint32_t* reg = &GPIO_TABLE[static_cast<uint8_t>(key) / 16]->IDR;
uint32_t offset = static_cast<uint8_t>(key) % 16;
return {reg,offset};
}
constexpr SHAL_GPIO_Port_Info getGPIOPortInfo(GPIO_Key key){
switch(key){
case GPIO_Key::A0:
return {0, SHAL_ADC_Channel::CH5};
case GPIO_Key::B0:
return {0, SHAL_ADC_Channel::CH0};
return {0, SHAL_ADC_Channel::CH15};
case GPIO_Key::A1:
return {1, SHAL_ADC_Channel::CH6};
case GPIO_Key::B1:
return {1, SHAL_ADC_Channel::CH1};
return {1, SHAL_ADC_Channel::CH16};
case GPIO_Key::A2:
return {2, SHAL_ADC_Channel::CH2};
return {2, SHAL_ADC_Channel::CH7};
case GPIO_Key::A3:
return {3, SHAL_ADC_Channel::CH8};
case GPIO_Key::B3:
return {3, SHAL_ADC_Channel::CH3};
return {3, SHAL_ADC_Channel::NO_ADC_MAPPING};
case GPIO_Key::A4:
return {4, SHAL_ADC_Channel::CH9};
case GPIO_Key::B4:
return {4, SHAL_ADC_Channel::CH4};
return {4, SHAL_ADC_Channel::NO_ADC_MAPPING};
case GPIO_Key::A5:
return {5, SHAL_ADC_Channel::CH10};
case GPIO_Key::B5:
return {5, SHAL_ADC_Channel::CH5};
return {5, SHAL_ADC_Channel::NO_ADC_MAPPING};
case GPIO_Key::A6:
return {6, SHAL_ADC_Channel::CH11};
case GPIO_Key::B6:
return {6, SHAL_ADC_Channel::CH6};
return {6, SHAL_ADC_Channel::NO_ADC_MAPPING};
case GPIO_Key::A7:
return {7, SHAL_ADC_Channel::CH12};
case GPIO_Key::B7:
return {7, SHAL_ADC_Channel::CH7};
return {7, SHAL_ADC_Channel::NO_ADC_MAPPING};
case GPIO_Key::A8:
return {8, SHAL_ADC_Channel::CH8};
return {8, SHAL_ADC_Channel::NO_ADC_MAPPING};
case GPIO_Key::A9:
return {9, SHAL_ADC_Channel::CH9};
return {9, SHAL_ADC_Channel::NO_ADC_MAPPING};
case GPIO_Key::A10:
return {10, SHAL_ADC_Channel::CH10};
return {10, SHAL_ADC_Channel::NO_ADC_MAPPING};
case GPIO_Key::A11:
return {11, SHAL_ADC_Channel::CH11};
return {11, SHAL_ADC_Channel::NO_ADC_MAPPING};
case GPIO_Key::A12:
return {12, SHAL_ADC_Channel::CH12};
return {12, SHAL_ADC_Channel::NO_ADC_MAPPING};
case GPIO_Key::A13:
return {13, SHAL_ADC_Channel::CH13};
return {13, SHAL_ADC_Channel::NO_ADC_MAPPING};
case GPIO_Key::A14:
return {14, SHAL_ADC_Channel::CH14};
return {14, SHAL_ADC_Channel::NO_ADC_MAPPING};
case GPIO_Key::A15:
return {15, SHAL_ADC_Channel::CH15};
return {15, SHAL_ADC_Channel::NO_ADC_MAPPING};
case GPIO_Key::NUM_GPIO:
case GPIO_Key::INVALID:
return {0, SHAL_ADC_Channel::CH0};
return {0, SHAL_ADC_Channel::NO_ADC_MAPPING};
}
__builtin_unreachable();
}

View File

@@ -29,20 +29,20 @@ public:
/// \return ADC result
uint16_t analogRead(SHAL_ADC_SampleTime sampleTime = SHAL_ADC_SampleTime::C8);
void setPinMode(PinMode mode) volatile;
uint16_t digitalRead();
void setAlternateFunction(GPIO_Alternate_Function AF) volatile;
void setAlternateFunction(GPIO_Alternate_Function_Mapping AF) volatile;
void setPinType(PinType type) volatile;
void setOutputType(PinType type) volatile;
void setOutputSpeed(OutputSpeed speed) volatile;
void setInternalResistor(InternalResistorType type) volatile;
void useAsExternalInterrupt(TriggerMode mode, EXTICallback callback);
SHAL_Result setPinMode(PinMode mode) volatile;
private:
@@ -67,6 +67,7 @@ private:
#define SET_ANALOGREAD_ADC(x) GPIOManager::setGPIOADC(x)
//Manages instances of SHAL_GPIO objects
class GPIOManager{

View File

@@ -15,14 +15,44 @@ struct SHAL_GPIO_EXTI_Register{
IRQn_Type IRQN; //IRQ number for enabling lines
};
struct SHAL_GPIO_Peripheral {
GPIO_TypeDef * reg;
unsigned long global_offset;
struct SHAL_GPIO_RCC_Enable_Register{
volatile uint32_t* reg;
uint32_t offset;
};
struct SHAL_Peripheral_Register {
struct SHAL_GPIO_Mode_Register {
volatile uint32_t* reg;
unsigned long offset;
uint32_t offset;
};
struct SHAL_GPIO_Pullup_Pulldown_Register {
volatile uint32_t* reg;
uint32_t offset;
};
struct SHAL_GPIO_Alternate_Function_Register {
volatile uint32_t* reg;
uint32_t offset;
};
struct SHAL_GPIO_Output_Speed_Register{
volatile uint32_t* reg;
uint32_t offset;
};
struct SHAL_GPIO_Output_Type_Register {
volatile uint32_t* reg;
uint32_t offset;
};
struct SHAL_GPIO_Output_Data_Register {
volatile uint32_t* reg;
uint32_t offset;
};
struct SHAL_GPIO_Input_Data_Register {
volatile uint32_t* reg;
uint32_t offset;
};
struct SHAL_GPIO_Port_Info{

View File

@@ -53,7 +53,6 @@ static IRQn_Type IRQN_TABLE[6] = {
#define SHAL_TIM16 TimerManager::get(Timer_Key::S_TIM16)
static inline SHAL_TIM_Status_Register getTimerStatusRegister(Timer_Key key){
SHAL_TIM_Status_Register res = {nullptr, TIM_SR_UIF};
@@ -66,7 +65,11 @@ static inline SHAL_TIM_Status_Register getTimerStatusRegister(Timer_Key key){
static inline SHAL_TIM_Control_Register_1 getTimerControlRegister1(Timer_Key key){
SHAL_TIM_Control_Register_1 res = {nullptr, TIM_CR1_CEN_Msk, TIM_CR1_UDIS, TIM_CR1_OPM, TIM_CR1_CMS_Pos};
SHAL_TIM_Control_Register_1 res = {nullptr, TIM_CR1_CEN_Msk,
TIM_CR1_UDIS,
TIM_CR1_OPM,
TIM_CR1_CMS_Pos,
TIM_CR1_ARPE};
volatile TIM_TypeDef* tim = TIM_TABLE[static_cast<uint8_t>(key)];
@@ -131,6 +134,68 @@ static inline SHAL_TIM_RCC_Register getTimerRCC(Timer_Key t) {
__builtin_unreachable();
}
static inline SHAL_TIM_Capture_Compare_Mode_Registers_Input getTimerCaptureCompareModeRegisters(Timer_Key key){
SHAL_TIM_Capture_Compare_Mode_Registers_Input res = {{nullptr,
nullptr},
TIM_CCMR1_CC1S_Pos,
TIM_CCMR1_IC1PSC_Pos,
TIM_CCMR1_IC1F_Pos,
TIM_CCMR1_CC2S_Pos,
TIM_CCMR1_IC2PSC_Pos,
TIM_CCMR1_IC2F_Pos
};
volatile TIM_TypeDef* tim = TIM_TABLE[static_cast<uint8_t>(key)];
res.regs[0] = &tim->CCMR1;
res.regs[1] = &tim->CCMR2;
return res;
}
static inline SHAL_TIM_Capture_Compare_Mode_Registers_Output
getTimerCaptureCompareModeRegistersOutput(Timer_Key key) {
SHAL_TIM_Capture_Compare_Mode_Registers_Output res = {
{nullptr, nullptr},
TIM_CCMR1_CC1S_Pos, //Channel 1 Capture/Compare selection
TIM_CCMR1_OC1FE, //Channel 1 Fast enable
TIM_CCMR1_OC1PE, //Channel 1 Preload enable
TIM_CCMR1_OC1M_Pos, //Channel 1 Mode (OC1M)
TIM_CCMR1_OC1CE, //Channel 1 Clear enable
TIM_CCMR1_CC2S_Pos, //Channel 2 Capture/Compare selection
TIM_CCMR1_OC2FE, //Channel 2 Fast enable
TIM_CCMR1_OC2PE, //Channel 2 Preload enable
TIM_CCMR1_OC2M_Pos, //Channel 2 Mode (OC2M)
TIM_CCMR1_OC2CE //Channel 2 Clear enable
};
volatile TIM_TypeDef* tim = TIM_TABLE[static_cast<uint8_t>(key)];
res.regs[0] = &tim->CCMR1;
res.regs[1] = &tim->CCMR2;
return res;
}
static inline SHAL_TIM_Break_Dead_Time_Register getTimerBreakDeadTimeRegister(Timer_Key key){
SHAL_TIM_Break_Dead_Time_Register res = {nullptr, TIM_BDTR_MOE};
volatile TIM_TypeDef* tim = TIM_TABLE[static_cast<uint8_t>(key)];
res.reg = &tim->BDTR;
return res;
}
static inline SHAL_TIM_Capture_Compare_Enable_Register getTimerCaptureCompareEnableRegister(Timer_Key key){
volatile TIM_TypeDef* tim = TIM_TABLE[static_cast<uint8_t>(key)];
return {&tim->CCER};
}
static inline SHAL_TIM_Capture_Compare_Register getTimerCaptureCompareRegister(Timer_Key key){
volatile TIM_TypeDef* tim = TIM_TABLE[static_cast<uint8_t>(key)];
return {&tim->CCR2};
}
//Get timer IRQN from lookup table
static inline IRQn_Type getTimerIRQn(Timer_Key t) {

View File

@@ -21,7 +21,7 @@ public:
/// Initializes a timer
/// \param prescaler The amount of times the base clock has to cycle before the timer adds one to the count
/// \param autoReload The number of timer counts before the count is reset and IRQ is called
void init(uint32_t prescaler, uint32_t autoReload);
void init(uint16_t prescaler, uint16_t autoReload);
//Starts the counter
void start();
@@ -38,6 +38,12 @@ public:
//Enable interrupts
void enableInterrupt();
void setPWMMode(SHAL_Timer_Channel channel, SHAL_TIM_Output_Compare_Mode outputCompareMode, SHAL_Timer_Channel_Main_Output_Mode mainOutputMode, SHAL_Timer_Channel_Complimentary_Output_Mode complimentaryOutputMode);
/// Set the duty cycle for PWM
/// \param dutyCycle 10 * percentage (e.g. 500 = 50%)
void setPWMDutyCycle(uint32_t dutyCycle);
//Set TIMER_KEY IRQ callback function
void setCallbackFunc(TimerCallback callback){
registerTimerCallback(m_key, callback);
@@ -50,6 +56,7 @@ private:
Timer_Key m_key;
};

View File

@@ -18,6 +18,7 @@ struct SHAL_TIM_Control_Register_1 {
uint32_t update_disable_mask;
uint32_t one_pulse_mode_mask;
uint32_t center_align_mode_offset;
uint32_t auto_reload_preload_enable_mask;
};
struct SHAL_TIM_DMA_Interrupt_Enable_Register {
@@ -45,4 +46,81 @@ struct SHAL_TIM_Auto_Reload_Register {
uint32_t offset;
};
#endif //SHMINGO_HAL_SHAL_TIM_TYPES_H
struct SHAL_TIM_Capture_Compare_Mode_Registers_Input {
volatile uint32_t* regs[2];
uint32_t input_capture_1_filter_offset;
uint32_t input_capture_1_prescaler_offset;
uint32_t capture_compare_1_selection_offset;
uint32_t input_capture_2_filter_offset;
uint32_t input_capture_2_prescaler_offset;
uint32_t capture_compare_2_selection_offset;
};
struct SHAL_TIM_Capture_Compare_Mode_Registers_Output {
volatile uint32_t* regs[2];
uint32_t capture_compare_1_selection_offset;
uint32_t output_compare_1_fast_enable_mask;
uint32_t output_compare_1_preload_enable_mask;
uint32_t output_compare_1_mode_offset;
uint32_t output_compare_1_clear_enable_mask;
uint32_t capture_compare_2_selection_offset;
uint32_t output_compare_2_fast_enable_mask;
uint32_t output_compare_2_preload_enable_mask;
uint32_t output_compare_2_mode_offset;
uint32_t output_compare_2_clear_enable_mask;
};
struct SHAL_TIM_Break_Dead_Time_Register {
volatile uint32_t* reg;
uint32_t main_output_enable_mask;
};
struct SHAL_TIM_Capture_Compare_Enable_Register {
volatile uint32_t* reg;
};
struct SHAL_TIM_Capture_Compare_Register {
volatile uint32_t* reg;
};
enum class SHAL_TIM_Output_Compare_Mode : uint8_t {
Frozen = 0b000, //Output compare frozen
ActiveOnMatch = 0b001, //Set output to active level on match
InactiveOnMatch = 0b010, //Set output to inactive level on match
Toggle = 0b011, //Toggle output on match
ForceInactive = 0b100, //Force output to inactive
ForceActive = 0b101, //Force output to active
PWMMode1 = 0b110, //PWM mode 1 (active until compare match)
PWMMode2 = 0b111, //PWM mode 2 (inactive until compare match)
};
enum class SHAL_TIM_Output_Compare_Preload : uint8_t {
Disabled = 0b0, //CCRx register is updated immediately
Enabled = 0b1, //CCRx register is buffered; updated on update event (UEV)
};
enum class SHAL_Timer_Channel : uint8_t { //TODO change if other timers have fewer than 6 channels
CH1 = 0,
CH2 = 1,
CH3 = 2,
CH4 = 3,
CH5 = 4,
CH6 = 5,
};
enum class SHAL_Timer_Channel_Main_Output_Mode : uint8_t {
Disabled = 0b00,
Polarity_Normal = 0b01,
Polarity_Reversed = 0b11,
};
enum class SHAL_Timer_Channel_Complimentary_Output_Mode : uint8_t {
Disabled = 0b00,
Polarity_Normal = 0b01,
Polarity_Reversed = 0b11,
};
#endif //SHAL_TIM_TYPES_H

View File

@@ -81,14 +81,14 @@ static inline SHAL_UART_Control_Register_1 getUARTControlRegister1(UART_Pair_Key
};
static inline SHAL_UART_Baud_Rate_Generation_Register getUARTBaudRateGenerationRegister(UART_Pair_Key key){
SHAL_UART_Baud_Rate_Generation_Register res = {nullptr, 1UL << 15}; //TODO un-hardcode if other devices have wider baud rate allowances
SHAL_UART_Baud_Rate_Generation_Register res = {nullptr}; //TODO un-hardcode if other devices have wider baud rate allowances
res.reg = &getUARTPair(key).USARTReg->BRR;
return res;
};
static inline SHAL_UART_Transmit_Data_Register getUARTTransmitDataRegister(UART_Pair_Key key){
SHAL_UART_Transmit_Data_Register res = {nullptr, 1UL << 15}; //TODO un-hardcode if other devices have wider baud rate allowances
SHAL_UART_Transmit_Data_Register res = {nullptr}; //TODO un-hardcode if other devices have wider baud rate allowances
res.reg = &getUARTPair(key).USARTReg->TDR;
return res;

View File

@@ -33,12 +33,10 @@ struct SHAL_UART_Control_Register_1 {
struct SHAL_UART_Baud_Rate_Generation_Register {
volatile uint32_t* reg;
uint32_t offset;
};
struct SHAL_UART_Transmit_Data_Register {
volatile uint16_t* reg;
uint16_t offset;
};
struct SHAL_UART_ISR_FIFO_Disabled {

View File

@@ -20,46 +20,38 @@ SHAL_GPIO::SHAL_GPIO(GPIO_Key key) : m_GPIO_KEY(key) {
}
void SHAL_GPIO::setLow() {
auto gpioPeripheral = getGPIORegister(m_GPIO_KEY);
gpioPeripheral.reg->ODR &= ~(1 << gpioPeripheral.global_offset);
auto outputDataReg = getGPIOOutputDataRegister(m_GPIO_KEY);
SHAL_set_bits(outputDataReg.reg,1,0,outputDataReg.offset);
}
void SHAL_GPIO::setHigh() {
auto gpioPeripheral = getGPIORegister(m_GPIO_KEY);
gpioPeripheral.reg->ODR |= (1 << gpioPeripheral.global_offset);
auto outputDataReg = getGPIOOutputDataRegister(m_GPIO_KEY);
SHAL_set_bits(outputDataReg.reg,1,1,outputDataReg.offset);
}
void SHAL_GPIO::toggle() volatile {
SHAL_GPIO_Peripheral gpioPeripheral = getGPIORegister(m_GPIO_KEY);
gpioPeripheral.reg->ODR ^= (1 << gpioPeripheral.global_offset);
auto outputDataReg = getGPIOOutputDataRegister(m_GPIO_KEY);
SHAL_flip_bits(outputDataReg.reg,1,outputDataReg.offset);
}
void SHAL_GPIO::setPinType(PinType type) volatile {
SHAL_GPIO_Peripheral gpioPeripheral = getGPIORegister(m_GPIO_KEY);
gpioPeripheral.reg->OTYPER &= ~(1 << gpioPeripheral.global_offset);
gpioPeripheral.reg->OTYPER |= (static_cast<uint8_t>(type) << gpioPeripheral.global_offset);
void SHAL_GPIO::setOutputType(PinType type) volatile {
auto outputTypeReg = getGPIOOutputTypeRegister(m_GPIO_KEY);
SHAL_set_bits(outputTypeReg.reg,2,static_cast<uint8_t>(type),outputTypeReg.offset);
}
void SHAL_GPIO::setOutputSpeed(OutputSpeed speed) volatile {
SHAL_GPIO_Peripheral gpioPeripheral = getGPIORegister(m_GPIO_KEY);
gpioPeripheral.reg->OSPEEDR |= (static_cast<uint8_t>(speed) << (2 * gpioPeripheral.global_offset));
auto outputSpeedReg = getGPIOOutputSpeedRegister(m_GPIO_KEY);
SHAL_set_bits(outputSpeedReg.reg,2,static_cast<uint8_t>(speed),outputSpeedReg.offset);
}
void SHAL_GPIO::setInternalResistor(InternalResistorType type) volatile {
SHAL_GPIO_Peripheral gpioPeripheral = getGPIORegister(m_GPIO_KEY);
gpioPeripheral.reg->PUPDR &= ~(0x03 << (2 * gpioPeripheral.global_offset));
gpioPeripheral.reg->PUPDR |= (static_cast<uint8_t>(type) << (2 * gpioPeripheral.global_offset));
auto pupdreg = getGPIOPUPDRegister(m_GPIO_KEY);
SHAL_set_bits(pupdreg.reg,2,static_cast<uint8_t>(type),pupdreg.offset);
}
void SHAL_GPIO::setAlternateFunction(GPIO_Alternate_Function AF) volatile {
SHAL_GPIO_Peripheral gpioPeripheral = getGPIORegister(m_GPIO_KEY);
int afrIndex = gpioPeripheral.global_offset < 8 ? 0 : 1; //Get index of AFR
gpioPeripheral.reg->AFR[afrIndex] &= ~(0xF << (gpioPeripheral.global_offset * 4));
gpioPeripheral.reg->AFR[afrIndex] |= (static_cast<int>(AF) << (gpioPeripheral.global_offset * 4));
auto alternateFunctionReg = getGPIOAlternateFunctionRegister(m_GPIO_KEY);
SHAL_set_bits(alternateFunctionReg.reg,4,static_cast<uint8_t>(AF),alternateFunctionReg.offset);
}
void SHAL_GPIO::setPinMode(PinMode mode) volatile {

View File

@@ -27,8 +27,8 @@ void SHAL_I2C::init(I2C_Pair pair) volatile {
GET_GPIO(SDA_Key).setAlternateFunction(I2CPair.SDA_Mask);
//These may be abstracted further to support multiple I2C configurations
GET_GPIO(SCL_Key).setPinType(PinType::OPEN_DRAIN);
GET_GPIO(SDA_Key).setPinType(PinType::OPEN_DRAIN);
GET_GPIO(SCL_Key).setOutputType(PinType::OPEN_DRAIN);
GET_GPIO(SDA_Key).setOutputType(PinType::OPEN_DRAIN);
GET_GPIO(SCL_Key).setOutputSpeed(OutputSpeed::HIGH_SPEED);
GET_GPIO(SDA_Key).setOutputSpeed(OutputSpeed::HIGH_SPEED);

View File

@@ -1,123 +0,0 @@
//
// Created by Luca on 8/30/2025.
//
#include "SHAL_GPIO.h"
#include "SHAL_EXTI_CALLBACK.h"
SHAL_GPIO::SHAL_GPIO() : m_GPIO_KEY(GPIO_Key::INVALID){
//Do not initialize anything
}
SHAL_GPIO::SHAL_GPIO(GPIO_Key key) : m_GPIO_KEY(key) {
volatile unsigned long* gpioEnable = getGPIORCCEnable(key).reg;
unsigned long gpioOffset = getGPIORCCEnable(key).offset;
*gpioEnable |= (1 << gpioOffset); //Set enable flag
}
void SHAL_GPIO::setLow() {
auto gpioPeripheral = getGPIORegister(m_GPIO_KEY);
gpioPeripheral.reg->ODR &= ~(1 << gpioPeripheral.global_offset);
}
void SHAL_GPIO::setHigh() {
auto gpioPeripheral = getGPIORegister(m_GPIO_KEY);
gpioPeripheral.reg->ODR |= (1 << gpioPeripheral.global_offset);
}
void SHAL_GPIO::toggle() volatile {
SHAL_GPIO_Peripheral gpioPeripheral = getGPIORegister(m_GPIO_KEY);
gpioPeripheral.reg->ODR ^= (1 << gpioPeripheral.global_offset);
}
void SHAL_GPIO::setPinType(PinType type) volatile {
SHAL_GPIO_Peripheral gpioPeripheral = getGPIORegister(m_GPIO_KEY);
gpioPeripheral.reg->OTYPER &= ~(1 << gpioPeripheral.global_offset);
gpioPeripheral.reg->OTYPER |= (static_cast<uint8_t>(type) << gpioPeripheral.global_offset);
}
void SHAL_GPIO::setOutputSpeed(OutputSpeed speed) volatile {
SHAL_GPIO_Peripheral gpioPeripheral = getGPIORegister(m_GPIO_KEY);
gpioPeripheral.reg->OSPEEDR |= (static_cast<uint8_t>(speed) << (2 * gpioPeripheral.global_offset));
}
void SHAL_GPIO::setInternalResistor(InternalResistorType type) volatile {
SHAL_GPIO_Peripheral gpioPeripheral = getGPIORegister(m_GPIO_KEY);
gpioPeripheral.reg->PUPDR &= ~(0x03 << (2 * gpioPeripheral.global_offset));
gpioPeripheral.reg->PUPDR |= (static_cast<uint8_t>(type) << (2 * gpioPeripheral.global_offset));
}
void SHAL_GPIO::setAlternateFunction(GPIO_Alternate_Function AF) volatile {
SHAL_GPIO_Peripheral gpioPeripheral = getGPIORegister(m_GPIO_KEY);
int afrIndex = gpioPeripheral.global_offset < 8 ? 0 : 1; //Get index of AFR
gpioPeripheral.reg->AFR[afrIndex] &= ~(0xF << (gpioPeripheral.global_offset * 4));
gpioPeripheral.reg->AFR[afrIndex] |= (static_cast<int>(AF) << (gpioPeripheral.global_offset * 4));
}
void SHAL_GPIO::setPinMode(PinMode mode) volatile {
SHAL_GPIO_Peripheral gpioPeripheral = getGPIORegister(m_GPIO_KEY);
gpioPeripheral.reg->MODER &= ~(0x03 << (2 * gpioPeripheral.global_offset)); //Clear any previous mode
gpioPeripheral.reg->MODER |= (static_cast<uint8_t>(mode) << (2 * gpioPeripheral.global_offset)); //Set mode based on pinmode bit structure
}
void SHAL_GPIO::useAsExternalInterrupt(TriggerMode mode, EXTICallback callback) {
uint32_t gpioPin = getGPIORegister(m_GPIO_KEY).global_offset; //Use existing structs to get offset
setPinMode(PinMode::INPUT_MODE); //Explicitly set mode to input
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; //Enable EXT, TODO Add this to a global SHAL_GLOBAL_TYPES.h file
NVIC_EnableIRQ(getGPIOEXTICR(m_GPIO_KEY).IRQN); //Enable IRQN for pin
auto ext_imr = getEXTIInterruptMaskRegister(gpioPin);
SHAL_set_bits(ext_imr.reg,1,1,gpioPin);
SHAL_GPIO_EXTI_Register EXTILineEnable = getGPIOEXTICR(m_GPIO_KEY);
*EXTILineEnable.EXT_ICR |= EXTILineEnable.mask; //Set bits to enable correct port on correct line TODO Find way to clear bits before
uint32_t rising_mask = 0x00;
uint32_t falling_mask = 0x00;
if(mode == TriggerMode::RISING_EDGE || mode == TriggerMode::RISING_FALLING_EDGE) {
auto rising_trigger_selection_reg = getEXTIRisingTriggerSelectionRegister(gpioPin);
SHAL_set_bits(rising_trigger_selection_reg.reg, 1, 1, gpioPin);
}
if(mode == TriggerMode::FALLING_EDGE || mode == TriggerMode::RISING_FALLING_EDGE) {
auto falling_trigger_selection_reg = getEXTIFallingTriggerSelectionRegister(gpioPin);
SHAL_set_bits(falling_trigger_selection_reg.reg,1,1,gpioPin);
}
//Set callback
registerEXTICallback(m_GPIO_KEY,callback);
__enable_irq(); //Enable IRQ just in case
}
uint16_t SHAL_GPIO::analogRead(SHAL_ADC_SampleTime sampleTime) {
SHAL_ADC_Channel channel = getGPIOPortInfo(m_GPIO_KEY).ADCChannel;
return GPIOManager::getGPIOADC().singleConvertSingle(channel,sampleTime);
}
SHAL_GPIO& GPIOManager::get(GPIO_Key key) {
unsigned int gpioPort = getGPIOPortNumber(key);
unsigned long gpioPin = getGPIORegister(key).global_offset; //Use existing structs to get offset
if (m_gpios[gpioPort][gpioPin].m_GPIO_KEY == GPIO_Key::INVALID){
m_gpios[gpioPort][gpioPin] = SHAL_GPIO(key);
}
return m_gpios[gpioPort][gpioPin];
}

View File

@@ -1,71 +0,0 @@
//
// Created by Luca on 8/28/2025.
//
#include "SHAL_TIM.h"
#include <cassert>
Timer::Timer(Timer_Key key) : m_key(key){
}
Timer::Timer() : m_key(Timer_Key::S_TIM_INVALID){
}
void Timer::start() {
auto control_reg = getTimerControlRegister1(m_key);
auto event_generation_reg = getTimerEventGenerationRegister(m_key);
SHAL_apply_bitmask(control_reg.reg, control_reg.counter_enable_mask); //Enable counter
SHAL_apply_bitmask(event_generation_reg.reg, event_generation_reg.update_generation_mask);
enableInterrupt();
}
void Timer::stop() {
auto control_reg = getTimerControlRegister1(m_key);
SHAL_clear_bitmask(control_reg.reg, control_reg.counter_enable_mask); //Enable counter
}
void Timer::setPrescaler(uint16_t presc) {
auto prescaler_reg = getTimerPrescalerRegister(m_key);
SHAL_set_bits(prescaler_reg.reg, 16, presc, prescaler_reg.offset);
}
void Timer::setARR(uint16_t arr) {
auto autoreload_reg = getTimerAutoReloadRegister(m_key);
SHAL_set_bits(autoreload_reg.reg, 16, arr, autoreload_reg.offset);
}
void Timer::enableInterrupt() {
auto dma_ier = getTimerDMAInterruptEnableRegister(m_key);
SHAL_apply_bitmask(dma_ier.reg,dma_ier.update_interrupt_enable_mask);
NVIC_EnableIRQ(getTimerIRQn(m_key)); //Enable the IRQn in the NVIC
}
void Timer::init(uint32_t prescaler, uint32_t autoReload) {
SHAL_TIM_RCC_Register rcc = getTimerRCC(m_key);
SHAL_apply_bitmask(rcc.reg,rcc.enable_mask);
setPrescaler(prescaler);
setARR(autoReload);
}
Timer &TimerManager::get(Timer_Key timer_key) {
//Ensure that we don't try to get invalid timers
assert(timer_key != Timer_Key::S_TIM_INVALID && timer_key != Timer_Key::NUM_TIMERS);
Timer& selected = timers[static_cast<int>(timer_key)];
//Timer queried is not initialized yet (defaults to invalid)
if(selected.m_key == Timer_Key::S_TIM_INVALID){
timers[static_cast<int>(timer_key)] = Timer(timer_key); //Initialize TIMER_KEY
}
return timers[static_cast<int>(timer_key)];
}

View File

@@ -2,10 +2,25 @@
// Created by Luca on 9/15/2025.
//
#include <cstdio>
#include "SHAL_CORE.h"
#include "SHAL_GPIO.h"
#include "SHAL_ADC.h"
#include "SHAL_UART.h"
void SHAL_init(){
systick_init(); //Just this for now
systick_init();
for(auto i = static_cast<uint8_t>(ADC_Key::S_ADC1); i < static_cast<uint8_t>(ADC_Key::NUM_ADC); i++){ //Init all ADCs
auto adc_key = static_cast<ADC_Key>(i);
ADCManager::getByIndex(i).init(adc_key);
}
SET_ANALOGREAD_ADC(SHAL_ADC1); //Default ADC1 for analogread calls
}
@@ -40,3 +55,9 @@ void SHAL_delay_ms(uint32_t ms){
SHAL_delay_us(1000);
}
}
void SHAL_print_register(const volatile uint32_t* reg){
char buff[32];
sprintf(buff, "0x%08lX\r\n", (unsigned long)(*reg));
SHAL_UART2.sendString(buff);
}

View File

@@ -43,5 +43,5 @@ DEFINE_MULTI_EXTI_IRQ(10,15);
//Link function to EXTI line
void registerEXTICallback(GPIO_Key key, EXTICallback callback){
EXTI_callbacks[getGPIORegister(key).global_offset] = callback;
EXTI_callbacks[getGPIOPinNumber(key)] = callback;
}

View File

@@ -3,30 +3,36 @@
//
#include "SHAL_ADC.h"
#include "SHAL_GPIO.h"
#include "SHAL_UART.h"
#include <cstdio>
//Can hard code registers on F0 because all F0 devices have only one ADC, and use only one clock
SHAL_Result SHAL_ADC::init() {
SHAL_Result SHAL_ADC::init(ADC_Key key) {
if(m_ADCKey == ADC_Key::INVALID || m_ADCKey == ADC_Key::NUM_ADC){
m_ADCKey = key;
if(!isValid()){
SHAL_UART2.sendString("Not valid\r\n");
return SHAL_Result::ERROR;
}
SHAL_ADC_RCC_Enable_Reg clock_reg = getADCRCCEnableRegister(m_ADCKey); //Clock enable
*clock_reg.reg |= clock_reg.mask;
SHAL_apply_bitmask(clock_reg.reg,clock_reg.mask);
SHAL_ADC_Control_Reg control_reg = getADCControlReg(m_ADCKey);
auto clock_select_register = getADCClockSelectRegister();
if (*control_reg.reg & control_reg.enable_mask) {
//request disable: ADEN=1 -> set ADDIS to disable
*control_reg.reg |= control_reg.disable_mask;
//wait until ADEN cleared (ISR.ADREADY == 0)
if(!SHAL_WAIT_FOR_CONDITION_MS((*control_reg.reg & control_reg.enable_mask) == 0, 100)){
return SHAL_Result::ERROR;
}
}
SHAL_set_bits(clock_select_register.reg, 2, static_cast<uint32_t>(ADC_Clock_Source::SHAL_SYSCLK),clock_select_register.offset); //Set ADC clock
wakeFromDeepSleep();
if(calibrate() != SHAL_Result::OKAY){ //Calibrate
SHAL_UART2.sendString("Calibration failed");
return SHAL_Result::ERROR;
}
if(enable() != SHAL_Result::OKAY){
SHAL_UART2.sendString("Could not enable from init\r\n");
return SHAL_Result::ERROR;
}
@@ -37,46 +43,73 @@ SHAL_Result SHAL_ADC::init() {
}
SHAL_Result SHAL_ADC::calibrate() {
if(disable() != SHAL_Result::OKAY){ //Disable the ADC
return SHAL_Result::ERROR;
}
SHAL_ADC_Control_Reg control_reg = getADCControlReg(m_ADCKey);
*control_reg.reg |= control_reg.calibration_mask;
if(!SHAL_WAIT_FOR_CONDITION_US(((*control_reg.reg & control_reg.calibration_mask) == 0),500)){ //Wait for calibration
if(disable() != SHAL_Result::OKAY){
return SHAL_Result::ERROR;
}
SHAL_delay_us(1000);
if ((*control_reg.reg & (control_reg.enable_mask | control_reg.disable_mask)) != 0) {
return SHAL_Result::ERROR;
}
SHAL_clear_bitmask(control_reg.reg, control_reg.differential_mode_mask);
SHAL_apply_bitmask(control_reg.reg, control_reg.calibration_mask);
if ((*control_reg.reg & control_reg.calibration_mask) == 0) {
return SHAL_Result::ERROR;
}
if (!SHAL_WAIT_FOR_CONDITION_US(((*control_reg.reg & control_reg.calibration_mask) != 0),500)) { //Wait for conversion
return SHAL_Result::ERROR; //Failed sequence
}
SHAL_UART2.sendString("Calibration OK\r\n");
return SHAL_Result::OKAY;
}
uint16_t SHAL_ADC::singleConvertSingle(SHAL_ADC_Channel channel, SHAL_ADC_SampleTime time) {
auto data_reg = getADCDataReg(m_ADCKey);
auto ISR_reg = getADCISRReg(m_ADCKey);
auto config_reg = getADCConfigReg(m_ADCKey);
auto data_reg = getADCDataReg(m_ADCKey); //Where our output will be stored
SHAL_clear_bitmask(config_reg.reg, config_reg.continuous_mode_mask);
auto sampleTimeReg = getADCChannelSamplingTimeRegister(m_ADCKey, channel);
SHAL_set_bits(sampleTimeReg.reg, 3, static_cast<uint8_t>(time), sampleTimeReg.channel_offset);
SHAL_set_bits(sampleTimeReg.reg,3,static_cast<uint8_t>(time),sampleTimeReg.channel_offset); //Set sample time register TODO un-hardcode bit width?
addADCChannelToSequence(channel,0); //Use index 0 to convert channel
setADCSequenceAmount(1); //Since we're using single convert, convert 1 channel
addADCChannelToSequence(channel, 0);
if(setADCSequenceAmount(1) == SHAL_Result::ERROR) { return 0; }
if(enable() != SHAL_Result::OKAY) {
return 0;
}
startConversion(); //Start ADC conversion
auto ISR_reg = getADCISRReg(m_ADCKey);
if(!SHAL_WAIT_FOR_CONDITION_US(((*ISR_reg.reg & ISR_reg.end_of_conversion_mask) != 0),500)){ //Wait for conversion
return 0; //Failed
// CRITICAL: Clear ALL relevant flags before starting
SHAL_apply_bitmask(ISR_reg.reg, ISR_reg.end_of_sequence_mask);
SHAL_apply_bitmask(ISR_reg.reg, ISR_reg.end_of_conversion_mask);
if(ISR_reg.overrun_mask) {
SHAL_apply_bitmask(ISR_reg.reg, ISR_reg.overrun_mask);
}
return *data_reg.reg;
volatile uint16_t dummy = *data_reg.reg;
(void)dummy;
startConversion();
if(!SHAL_WAIT_FOR_CONDITION_US(((*ISR_reg.reg & ISR_reg.end_of_conversion_mask) != 0), 2000)) {
return 0;
}
uint16_t result = *data_reg.reg;
SHAL_apply_bitmask(ISR_reg.reg, ISR_reg.end_of_conversion_mask);
SHAL_apply_bitmask(ISR_reg.reg, ISR_reg.end_of_sequence_mask);
return result;
}
SHAL_Result SHAL_ADC::multiConvertSingle(SHAL_ADC_Channel* channels, const int numChannels, uint16_t* result, SHAL_ADC_SampleTime time) {
@@ -92,10 +125,6 @@ SHAL_Result SHAL_ADC::multiConvertSingle(SHAL_ADC_Channel* channels, const int n
SHAL_set_bits(sampleTimeReg.reg,3,static_cast<uint8_t>(time),sampleTimeReg.channel_offset); //Set sample time register TODO un-hardcode bit width?
addADCChannelToSequence(channel,i); //Use index 0 to convert channel
if(enable() != SHAL_Result::OKAY){
return SHAL_Result::ERROR;
}
}
startConversion(); //Start ADC conversion
@@ -118,33 +147,70 @@ SHAL_Result SHAL_ADC::multiConvertSingle(SHAL_ADC_Channel* channels, const int n
SHAL_Result SHAL_ADC::enable() {
if(!isValid()){
SHAL_UART2.sendString("Enable failed: Invalid \r\n");
return SHAL_Result::ERROR;
}
SHAL_ADC_Control_Reg control_reg = getADCControlReg(m_ADCKey);
SHAL_ADC_ISR_Reg ISR_reg = getADCISRReg(m_ADCKey);
*control_reg.reg |= control_reg.enable_mask; //Enable
if(!SHAL_WAIT_FOR_CONDITION_MS((*control_reg.reg & control_reg.calibration_mask) == 0, 100)) {
return SHAL_Result::ERROR;
}
if (*control_reg.reg & control_reg.enable_mask) {
return SHAL_Result::OKAY; //Not an error
}
if (*control_reg.reg & control_reg.disable_mask) {
return SHAL_Result::ERROR;
}
//Clear ADRDY flag by writing 1 to it
SHAL_apply_bitmask(ISR_reg.reg, ISR_reg.ready_mask);
//Enable the ADC by setting ADEN
SHAL_apply_bitmask(control_reg.reg, control_reg.enable_mask);
if(!SHAL_WAIT_FOR_CONDITION_MS((*ISR_reg.reg & ISR_reg.ready_mask) != 0, 100)) {
return SHAL_Result::ERROR;
}
//Clear ADRDY again
SHAL_apply_bitmask(ISR_reg.reg, ISR_reg.ready_mask);
return SHAL_Result::OKAY;
}
SHAL_Result SHAL_ADC::wakeFromDeepSleep() {
SHAL_ADC_Control_Reg control_reg = getADCControlReg(m_ADCKey); //ADC Control register
SHAL_clear_bitmask(control_reg.reg,control_reg.deep_power_down_mask); //Wake ADC from sleep
SHAL_apply_bitmask(control_reg.reg,control_reg.voltage_regulator_mask);
SHAL_delay_us(50); //Wait for regulator to stabilize
return SHAL_Result::OKAY;
}
SHAL_Result SHAL_ADC::disable() {
if(!isValid()){
return SHAL_Result::ERROR;
}
SHAL_ADC_Control_Reg control_reg = getADCControlReg(m_ADCKey);
auto control_reg = getADCControlReg(m_ADCKey);
//Stop any ongoing conversion
if (*control_reg.reg & control_reg.start_mask) {
SHAL_apply_bitmask(control_reg.reg, control_reg.stop_mask);
}
//Only disable if ADC is enabled otherwise it hangs
if (*control_reg.reg & control_reg.enable_mask) {
//request disable: ADEN=1 -> set ADDIS to disable
*control_reg.reg |= control_reg.disable_mask;
//wait until ADEN cleared (ISR.ADREADY == 0)
if(!SHAL_WAIT_FOR_CONDITION_MS((*control_reg.reg & control_reg.enable_mask) == 0, 100)){
SHAL_apply_bitmask(control_reg.reg, control_reg.disable_mask);
if (!SHAL_WAIT_FOR_CONDITION_MS(((*control_reg.reg & (control_reg.enable_mask | control_reg.disable_mask)) == 0),500)){
return SHAL_Result::ERROR;
}
}
@@ -152,6 +218,7 @@ SHAL_Result SHAL_ADC::disable() {
return SHAL_Result::OKAY;
}
SHAL_Result SHAL_ADC::startConversion() {
auto control_reg = getADCControlReg(m_ADCKey);
@@ -186,8 +253,8 @@ SHAL_Result SHAL_ADC::configureAlignment(SHAL_ADC_Alignment alignment) {
SHAL_ADC_Config_Reg config_reg = getADCConfigReg(m_ADCKey);
*config_reg.reg &= ~(0x1UL << config_reg.alignment_offset); //TODO check if this needs to be abstracted (Do other platforms have >2 resolution possibilities?
*config_reg.reg |= static_cast<uint8_t>(alignment) << config_reg.alignment_offset;
//TODO check if this needs to be abstracted (Do other platforms have >2 resolution possibilities?
SHAL_set_bits(config_reg.reg,1,static_cast<uint8_t>(alignment),config_reg.alignment_offset);
return SHAL_Result::OKAY;
}
@@ -210,18 +277,23 @@ SHAL_Result SHAL_ADC::addADCChannelToSequence(SHAL_ADC_Channel channel, uint32_t
if(!isValid()) { return SHAL_Result::ERROR; }
auto sequenceRegisters = getADCSequenceRegisters(m_ADCKey);
auto channelNum = static_cast<uint8_t>(channel);
uint32_t bitSection = (index + 1) % 5; //Need a new variable since SQR1 has its data bits shifted up by one section to make room for the L section
uint32_t bitSection = (index + 1) % 5;
uint32_t sequenceRegNumber = (index + 1) / 5;
volatile uint32_t* sequenceReg = sequenceRegisters.regs[sequenceRegNumber];
uint32_t bitSectionOffset = sequenceRegisters.offsets[bitSection];
SHAL_set_bits(sequenceReg,5,channelNum,bitSectionOffset);
}
// Clear only the specific 5 bits we're setting, not the entire register
uint32_t clearMask = ~(0x1F << bitSectionOffset);
*sequenceReg &= clearMask;
// Set the new channel number
*sequenceReg |= (channelNum << bitSectionOffset);
return SHAL_Result::OKAY;
}
SHAL_ADC &ADCManager::get(ADC_Key key) {
return m_ADCs[static_cast<uint8_t>(key)];

View File

@@ -0,0 +1,126 @@
//
// Created by Luca on 8/30/2025.
//
#include <cstdio>
#include "SHAL_GPIO.h"
#include "SHAL_EXTI_CALLBACK.h"
#include "SHAL_UART.h"
SHAL_GPIO::SHAL_GPIO() : m_GPIO_KEY(GPIO_Key::INVALID){
//Do not initialize anything
}
SHAL_GPIO::SHAL_GPIO(GPIO_Key key) : m_GPIO_KEY(key) {
volatile unsigned long* gpioEnable = getGPIORCCEnable(key).reg;
unsigned long gpioOffset = getGPIORCCEnable(key).offset;
*gpioEnable |= (1 << gpioOffset); //Set enable flag
}
void SHAL_GPIO::setLow() {
auto outputDataReg = getGPIOOutputDataRegister(m_GPIO_KEY);
SHAL_set_bits(outputDataReg.reg,1,0,outputDataReg.offset);
}
void SHAL_GPIO::setHigh() {
auto outputDataReg = getGPIOOutputDataRegister(m_GPIO_KEY);
SHAL_set_bits(outputDataReg.reg,1,1,outputDataReg.offset);
}
void SHAL_GPIO::toggle() volatile {
auto outputDataReg = getGPIOOutputDataRegister(m_GPIO_KEY);
SHAL_flip_bits(outputDataReg.reg,1,outputDataReg.offset);
}
void SHAL_GPIO::setOutputType(PinType type) volatile {
auto outputTypeReg = getGPIOOutputTypeRegister(m_GPIO_KEY);
SHAL_set_bits(outputTypeReg.reg,2,static_cast<uint8_t>(type),outputTypeReg.offset);
}
void SHAL_GPIO::setOutputSpeed(OutputSpeed speed) volatile {
auto outputSpeedReg = getGPIOOutputSpeedRegister(m_GPIO_KEY);
SHAL_set_bits(outputSpeedReg.reg,2,static_cast<uint8_t>(speed),outputSpeedReg.offset);
}
void SHAL_GPIO::setInternalResistor(InternalResistorType type) volatile {
auto pupdreg = getGPIOPUPDRegister(m_GPIO_KEY);
SHAL_set_bits(pupdreg.reg,2,static_cast<uint8_t>(type),pupdreg.offset);
}
void SHAL_GPIO::setAlternateFunction(GPIO_Alternate_Function AF) volatile {
auto alternateFunctionReg = getGPIOAlternateFunctionRegister(m_GPIO_KEY);
SHAL_set_bits(alternateFunctionReg.reg,4,static_cast<uint8_t>(AF),alternateFunctionReg.offset);
}
SHAL_Result SHAL_GPIO::setPinMode(PinMode mode) volatile {
auto pinModeReg = getGPIOModeRegister(m_GPIO_KEY);
if(mode == PinMode::ANALOG_MODE && getGPIOPortInfo(m_GPIO_KEY).ADCChannel == SHAL_ADC_Channel::NO_ADC_MAPPING){
char buff[100];
sprintf(buff, "Error: GPIO pin %d has no valid ADC mapping\r\n", static_cast<uint8_t>(m_GPIO_KEY));
SHAL_UART2.sendString(buff);
return SHAL_Result::ERROR;
}
SHAL_set_bits(pinModeReg.reg,2,static_cast<uint8_t>(mode),pinModeReg.offset); //Set mode
return SHAL_Result::OKAY;
}
void SHAL_GPIO::useAsExternalInterrupt(TriggerMode mode, EXTICallback callback) {
/* ---- Connect PB6 to EXTI6 via SYSCFG ---- */
uint32_t port_b_val = 1; // 0=A, 1=B, 2=C, 3=D, etc.
SYSCFG->EXTICR[1] &= ~(0xFUL << 8); // Clear EXTI6 bits (bits 8-11)
SYSCFG->EXTICR[1] |= (port_b_val << 8); // Set EXTI6 to PB6
/* ---- Configure EXTI line 6 ---- */
EXTI->IMR1 |= (1UL << 6); // Unmask line 6
EXTI->RTSR1 |= (1UL << 6); // Rising trigger enable
EXTI->FTSR1 &= ~(1UL << 6); // Falling trigger disable
/* ---- Enable NVIC interrupt for EXTI lines [9:5] ---- */
NVIC_SetPriority(EXTI9_5_IRQn, 2);
NVIC_EnableIRQ(EXTI9_5_IRQn);
__enable_irq(); //Enable IRQ just in case
}
uint16_t SHAL_GPIO::analogRead(SHAL_ADC_SampleTime sampleTime) {
SHAL_ADC_Channel channel = getGPIOPortInfo(m_GPIO_KEY).ADCChannel;
return GPIOManager::getGPIOADC().singleConvertSingle(channel,sampleTime);
}
void SHAL_GPIO::setAlternateFunction(GPIO_Alternate_Function_Mapping AF) volatile {
setPinMode(PinMode::ALTERNATE_FUNCTION_MODE);
auto alternateFunctionReg = getGPIOAlternateFunctionRegister(m_GPIO_KEY);
SHAL_set_bits(alternateFunctionReg.reg,4,static_cast<uint8_t>(AF),alternateFunctionReg.offset);
}
uint16_t SHAL_GPIO::digitalRead() {
auto inputDataReg = getGPIOInputDataRegister(m_GPIO_KEY);
if((*inputDataReg.reg & (1 << 6)) != 0){
return 1;
}
return 0;
}
SHAL_GPIO& GPIOManager::get(GPIO_Key key) {
unsigned int gpioPort = getGPIOPortNumber(key);
uint8_t gpioPin = getGPIOPinNumber(key);
if (m_gpios[gpioPort][gpioPin].m_GPIO_KEY == GPIO_Key::INVALID){
m_gpios[gpioPort][gpioPin] = SHAL_GPIO(key);
}
return m_gpios[gpioPort][gpioPin];
}

View File

@@ -27,8 +27,8 @@ void SHAL_I2C::init(I2C_Pair pair) volatile {
GET_GPIO(SDA_Key).setAlternateFunction(I2CPair.SDA_Mask);
//These may be abstracted further to support multiple I2C configurations
GET_GPIO(SCL_Key).setPinType(PinType::OPEN_DRAIN);
GET_GPIO(SDA_Key).setPinType(PinType::OPEN_DRAIN);
GET_GPIO(SCL_Key).setOutputType(PinType::OPEN_DRAIN);
GET_GPIO(SDA_Key).setOutputType(PinType::OPEN_DRAIN);
GET_GPIO(SCL_Key).setOutputSpeed(OutputSpeed::HIGH_SPEED);
GET_GPIO(SDA_Key).setOutputSpeed(OutputSpeed::HIGH_SPEED);

View File

@@ -0,0 +1,117 @@
//
// Created by Luca on 8/28/2025.
//
#include "SHAL_TIM.h"
#include <cassert>
Timer::Timer(Timer_Key key) : m_key(key){
}
Timer::Timer() : m_key(Timer_Key::S_TIM_INVALID){
}
void Timer::start() {
auto control_reg = getTimerControlRegister1(m_key);
auto event_generation_reg = getTimerEventGenerationRegister(m_key);
auto status_reg = getTimerStatusRegister(m_key);
SHAL_apply_bitmask(control_reg.reg, control_reg.counter_enable_mask); //Enable counter
SHAL_apply_bitmask(control_reg.reg, control_reg.auto_reload_preload_enable_mask); //Preload enable (buffer)
SHAL_apply_bitmask(event_generation_reg.reg, event_generation_reg.update_generation_mask);
SHAL_clear_bitmask(status_reg.reg,status_reg.update_interrupt_flag_mask);
enableInterrupt();
}
void Timer::stop() {
auto control_reg = getTimerControlRegister1(m_key);
SHAL_clear_bitmask(control_reg.reg, control_reg.counter_enable_mask); //Enable counter
}
void Timer::setPrescaler(uint16_t presc) {
auto prescaler_reg = getTimerPrescalerRegister(m_key);
SHAL_set_register_value(prescaler_reg.reg,presc);
}
void Timer::setARR(uint16_t arr) {
auto autoreload_reg = getTimerAutoReloadRegister(m_key);
SHAL_set_register_value(autoreload_reg.reg,arr);
}
void Timer::enableInterrupt() {
auto dma_ier = getTimerDMAInterruptEnableRegister(m_key);
SHAL_apply_bitmask(dma_ier.reg,dma_ier.update_interrupt_enable_mask);
NVIC_EnableIRQ(getTimerIRQn(m_key)); //Enable the IRQn in the NVIC
}
void Timer::init(uint16_t prescaler, uint16_t autoReload) {
SHAL_TIM_RCC_Register rcc = getTimerRCC(m_key);
SHAL_apply_bitmask(rcc.reg,rcc.enable_mask);
setPrescaler(prescaler);
setARR(autoReload);
*getTimerStatusRegister(m_key).reg = 0;
*getTimerDMAInterruptEnableRegister(m_key).reg = 0;
}
void Timer::setPWMMode(SHAL_Timer_Channel channel, SHAL_TIM_Output_Compare_Mode outputCompareMode, SHAL_Timer_Channel_Main_Output_Mode mainOutputMode,
SHAL_Timer_Channel_Complimentary_Output_Mode complimentaryOutputMode) {
auto ccer = getTimerCaptureCompareEnableRegister(m_key);
auto ccmr1 = getTimerCaptureCompareModeRegistersOutput(m_key);
auto bdtr = getTimerBreakDeadTimeRegister(m_key);
uint8_t fullChannelModeMask = static_cast<uint8_t>(mainOutputMode) | (static_cast<uint8_t>(complimentaryOutputMode) << 2);
uint8_t channelNum = static_cast<uint8_t>(channel);
if (channelNum <= 3) {
uint32_t regNum = channelNum / 2; //TODO change later for support for channels 5 and 6
if (channelNum % 2 == 1) {
SHAL_set_bits(ccmr1.regs[regNum], 4, static_cast<uint8_t>(outputCompareMode),
ccmr1.output_compare_2_mode_offset);
} else {
SHAL_set_bits(ccmr1.regs[regNum], 4, static_cast<uint8_t>(outputCompareMode),
ccmr1.output_compare_1_mode_offset);
}
}
uint32_t offset = channelNum * 4;
if (static_cast<uint8_t>(m_key) > 3) {
fullChannelModeMask &= (0b0011); //Clear bits for complimentary output since channels 4,5,6 don't support it
}
SHAL_set_bits(ccer.reg, 4, fullChannelModeMask, offset);
SHAL_apply_bitmask(bdtr.reg, bdtr.main_output_enable_mask);
}
void Timer::setPWMDutyCycle(uint32_t dutyCycle) {
auto reg = getTimerCaptureCompareRegister(m_key);
SHAL_set_bits(reg.reg,16,dutyCycle,0);
}
Timer &TimerManager::get(Timer_Key timer_key) {
//Ensure that we don't try to get invalid timers
assert(timer_key != Timer_Key::S_TIM_INVALID && timer_key != Timer_Key::NUM_TIMERS);
Timer& selected = timers[static_cast<int>(timer_key)];
//Timer queried is not initialized yet (defaults to invalid)
if(selected.m_key == Timer_Key::S_TIM_INVALID){
timers[static_cast<int>(timer_key)] = Timer(timer_key); //Initialize TIMER_KEY
}
return timers[static_cast<int>(timer_key)];
}

View File

@@ -8,9 +8,9 @@ DEFINE_TIMER_IRQ(Timer_Key::S_TIM1, TIM1_IRQHandler)
DEFINE_TIMER_IRQ(Timer_Key::S_TIM2, TIM2_IRQHandler)
DEFINE_TIMER_IRQ(Timer_Key::S_TIM6, TIM6_IRQHandler)
DEFINE_TIMER_IRQ(Timer_Key::S_TIM7, TIM7_IRQHandler)
DEFINE_TIMER_IRQ(Timer_Key::S_TIM15, TIM15_IRQHandler)
DEFINE_TIMER_IRQ(Timer_Key::S_TIM16, TIM16_IRQHandler)
DEFINE_TIMER_IRQ(Timer_Key::S_TIM15, TIM1_BRK_TIM15_IRQHandler)
DEFINE_TIMER_IRQ(Timer_Key::S_TIM16, TIM1_UP_TIM16_IRQHandler)
void registerTimerCallback(Timer_Key key, TimerCallback callback){
timer_callbacks[static_cast<int>(key)] = callback;
timer_callbacks[static_cast<uint32_t>(key)] = callback;
}

View File

@@ -48,26 +48,30 @@ void SHAL_UART::begin(uint32_t baudRate) volatile {
auto baud_rate_reg = getUARTBaudRateGenerationRegister(m_key);
unsigned long adjustedBaudRate = 8000000 / baudRate;
SHAL_set_bits(baud_rate_reg.reg,16,adjustedBaudRate,baud_rate_reg.offset); //MAKE SURE ANY FUNCTION THAT CHANGES CLOCK UPDATES THIS! //TODO DO NOT HARDCODE THIS SHIT
uint32_t adjustedBaudRate = SystemCoreClock / baudRate;
SHAL_apply_bitmask(control_reg.reg, control_reg.usart_enable_mask); //Clear enable bit (turn off usart)
SHAL_set_register_value(baud_rate_reg.reg,adjustedBaudRate);
SHAL_apply_bitmask(control_reg.reg, control_reg.usart_enable_mask); //Turn on usart
}
void SHAL_UART::sendString(const char *s) volatile {
while (*s) sendChar(*s++); //Send chars while we haven't reached end of s
while (*s) {//Send chars while we haven't reached end of s
sendChar(*s++);
}
}
void SHAL_UART::sendChar(char c) volatile {
auto ISR_non_fifo = getUARTISRFifoDisabled(m_key);
if(!SHAL_WAIT_FOR_CONDITION_US((*ISR_non_fifo.reg & ISR_non_fifo.transmit_data_register_empty_mask) == 0, 500)){
if(!SHAL_WAIT_FOR_CONDITION_MS((*ISR_non_fifo.reg & ISR_non_fifo.transmit_data_register_empty_mask) != 0, 500)){
PIN(B3).setHigh();
return;
}
auto transmit_reg = getUARTTransmitDataRegister(m_key);
SHAL_set_bits_16(transmit_reg.reg,16,static_cast<uint16_t>(c),transmit_reg.offset);
SHAL_set_register_value_16(transmit_reg.reg, static_cast<uint16_t>(c));
}

View File

@@ -1,12 +1,264 @@
#include <cstdio>
#include "SHAL.h"
#include <cstdlib>
#define NUM_CHANNELS 6
// Physical order on right-side header: A0, A1, A3, A4, A5, A6, A7
SHAL_ADC_Channel channels[NUM_CHANNELS] = {
SHAL_ADC_Channel::CH5,
SHAL_ADC_Channel::CH6,
SHAL_ADC_Channel::CH8,
SHAL_ADC_Channel::CH9,
SHAL_ADC_Channel::CH10,
SHAL_ADC_Channel::CH12,
};
bool isDeviceOn = false;
bool shouldToggleDeviceState = true;
bool shouldCheckSensorThresholds = true;
uint16_t vals[NUM_CHANNELS] = {0,0,0,0,0,0};
uint8_t currentSensor = 0;
bool isAlarmBeeping = false;
uint16_t sensorThresholds[NUM_CHANNELS] = {0,0,0,0,0,0};
int buzzer_beepCount = 0;
bool isBeepingForCalibration = false;
bool prevIsCalibrateButtonHigh = false;
int cyclesPerPrint = 2;
int currentCycle = 0;
bool areSensorRequirementsMetCurrent = false;
bool areSensorRequirementsMetPrevious = false;
void getSensorData(){
vals[currentSensor] = SHAL_ADC1.singleConvertSingle(channels[currentSensor]);
if(currentSensor == (NUM_CHANNELS - 1) && currentCycle == cyclesPerPrint - 1){
char buff[125];
// Print in the same order as the channels[] array (physical order)
sprintf(buff, "A0:%u,A1:%u,A3:%u,A4:%u,A5:%u,A6:%u\r\n",
vals[0], vals[1], vals[2], vals[3], vals[4], vals[5]);
SHAL_UART2.sendString(buff);
}
currentSensor = (currentSensor + 1) % NUM_CHANNELS;
currentCycle = (currentCycle + 1) % cyclesPerPrint;
}
void startBeeping(){
SHAL_TIM6.setPrescaler(4000);
SHAL_TIM6.setARR(200);
SHAL_TIM6.start();
}
void stopBeeping(){
SHAL_TIM1.stop();
SHAL_TIM6.stop();
isAlarmBeeping = false;
isBeepingForCalibration = false;
}
void checkSensorThresholds(){
bool localFlag = true;
for(int i = 0; i < NUM_CHANNELS; i++){
if(vals[i] < sensorThresholds[i]){
areSensorRequirementsMetCurrent = false; //Conditions not met
localFlag = false;
break;
}
}
if(localFlag){
areSensorRequirementsMetCurrent = true;
}
if(areSensorRequirementsMetCurrent){
if(!areSensorRequirementsMetPrevious){
SHAL_TIM1.stop();
SHAL_TIM6.stop();
SHAL_TIM15.stop();
PIN(A9).setLow();
stopBeeping();
}
}
else{
if(areSensorRequirementsMetPrevious){
SHAL_TIM15.start();
PIN(A9).setHigh();
}
}
areSensorRequirementsMetPrevious = areSensorRequirementsMetCurrent;
}
void calibrateThresholds(){
// Read every channel once and set threshold to 80% of reading
for(int i = 0; i < NUM_CHANNELS; i++){
uint16_t sensorVal = (vals[i] * 3) / 5;
if(sensorVal < 50){
sensorVal = 0;
}
else{
sensorVal = sensorVal - 50;
}
sensorThresholds[i] = sensorVal;
}
char buff[125];
// Print in the same order as the channels[] array (physical order)
sprintf(buff, "Thresholds calibrated to: A0:%u,A1:%u,A3:%u,A4:%u,A5:%u,A6:%u\r\n",
sensorThresholds[0], sensorThresholds[1], sensorThresholds[2], sensorThresholds[3], sensorThresholds[4], sensorThresholds[5]);
SHAL_UART2.sendString(buff);
}
void PWMToggle(){
//Flash light
PIN(A9).toggle();
SHAL_TIM15.stop(); //Stop timer for allowed time off sensors
if(isBeepingForCalibration && buzzer_beepCount > 2){
isBeepingForCalibration = false;
buzzer_beepCount = 0;
SHAL_TIM6.stop(); //Reset timer 6
SHAL_TIM1.stop(); //Stop buzzer
SHAL_TIM6.setPrescaler(4000);
SHAL_TIM6.setARR(400);
}
if(!isAlarmBeeping){
SHAL_TIM1.start();
buzzer_beepCount++;
}
else{
SHAL_TIM1.stop();
}
isAlarmBeeping = !isAlarmBeeping;
}
void buttonHoldCallback(){
shouldCheckSensorThresholds = false; //Dont check sensor thresholds yet, ensure that calibration beep happens
SHAL_TIM7.stop(); //Stop this timer
SHAL_TIM2.stop(); //Stop reading from ADC
buzzer_beepCount = 0;
isBeepingForCalibration = true;
SHAL_TIM6.init(4000,50);
SHAL_TIM6.start();
calibrateThresholds();
SHAL_TIM1.start();
SHAL_TIM2.start(); //Restart value checks
shouldToggleDeviceState = false;
shouldCheckSensorThresholds = true;
}
int main() {
//End setup
SHAL_init();
while (true) {
__WFI();
//SHAL_UART2.init(UART_Pair_Key::Tx2A2_Rx2A3);
//SHAL_UART2.begin(115200);
PIN(A0).setPinMode(PinMode::ANALOG_MODE);
PIN(A1).setPinMode(PinMode::ANALOG_MODE);
PIN(A3).setPinMode(PinMode::ANALOG_MODE);
PIN(A4).setPinMode(PinMode::ANALOG_MODE);
PIN(A5).setPinMode(PinMode::ANALOG_MODE);
PIN(A6).setPinMode(PinMode::ANALOG_MODE);
PIN(A7).setPinMode(PinMode::ANALOG_MODE);
PIN(B6).setPinMode(PinMode::INPUT_MODE);
PIN(A9).setPinMode(PinMode::OUTPUT_MODE);
PIN(B0).setAlternateFunction(GPIO_Alternate_Function_Mapping::B0_TIM1CH2N);
PIN(A8).setPinMode(PinMode::OUTPUT_MODE);
PIN(A8).setInternalResistor(InternalResistorType::NO_PULL);
SHAL_TIM2.init(4000,200);
SHAL_TIM2.setCallbackFunc(getSensorData);
SHAL_TIM2.enableInterrupt();
SHAL_TIM2.start();
SHAL_TIM1.init(0,2400); //PWM signal
SHAL_TIM1.setPWMMode(SHAL_Timer_Channel::CH2,SHAL_TIM_Output_Compare_Mode::PWMMode1,SHAL_Timer_Channel_Main_Output_Mode::Polarity_Normal,SHAL_Timer_Channel_Complimentary_Output_Mode::Polarity_Reversed);
SHAL_TIM1.setPWMDutyCycle(900);
SHAL_TIM6.init(4000,500); //PWM switcher
SHAL_TIM6.setCallbackFunc(PWMToggle);
SHAL_TIM6.enableInterrupt();
SHAL_TIM7.init(4000,3000); //Calibrate timer
SHAL_TIM7.setCallbackFunc(buttonHoldCallback);
SHAL_TIM7.enableInterrupt();
SHAL_TIM15.init(4000,5000); //5 seconds
SHAL_TIM15.setCallbackFunc(startBeeping);
SHAL_TIM15.enableInterrupt();
SHAL_UART2.sendString("Hello3\r\n");
while (true) { //TODO set to use button for simulating off sensor, uncomment for real functionality
if(PIN(B6).digitalRead() != 1){
if(prevIsCalibrateButtonHigh){
SHAL_TIM7.start();
}
prevIsCalibrateButtonHigh = false;
}
else{
if(!prevIsCalibrateButtonHigh){
if(shouldToggleDeviceState){
if(!isDeviceOn){ //Turn device on
PIN(A8).setHigh();
isDeviceOn = true;
}
else{ //Turn device off
PIN(A8).setLow();
PIN(A9).setLow();
isDeviceOn = false;
areSensorRequirementsMetCurrent = true;
areSensorRequirementsMetPrevious = true;
SHAL_TIM15.stop();
stopBeeping();
}
}
shouldToggleDeviceState = true;
SHAL_TIM7.stop();
}
prevIsCalibrateButtonHigh = true;
}
if(isDeviceOn && shouldCheckSensorThresholds){
checkSensorThresholds();
}
}
}

View File

@@ -10,7 +10,7 @@ set(CMAKE_ASM_COMPILER ${TOOLCHAIN_PREFIX}gcc)
set(CMAKE_OBJCOPY ${TOOLCHAIN_PREFIX}objcopy)
set(CMAKE_SIZE ${TOOLCHAIN_PREFIX}size)
set(COMMON_FLAGS "-mcpu=cortex-m0 -mthumb -fdata-sections -ffunction-sections")
set(COMMON_FLAGS "-mcpu=cortex-m4 -mthumb -fdata-sections -ffunction-sections")
set(CMAKE_C_FLAGS_INIT "${COMMON_FLAGS} --specs=nano.specs")
set(CMAKE_CXX_FLAGS_INIT "${COMMON_FLAGS} -fno-rtti -fno-exceptions -fno-threadsafe-statics --specs=nano.specs")