Working
This commit is contained in:
63
SHAL/Src/STM32L4xx/Core/SHAL_CORE.cpp
Normal file
63
SHAL/Src/STM32L4xx/Core/SHAL_CORE.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// 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();
|
||||
|
||||
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
|
||||
void systick_init(){
|
||||
SysTick->CTRL = 0; //Disable first
|
||||
SysTick->LOAD = 0xFFFFFF; //Max 24-bit
|
||||
SysTick->VAL = 0; //Clear
|
||||
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;
|
||||
}
|
||||
|
||||
|
||||
void SHAL_delay_us(uint32_t us){
|
||||
uint32_t ticks = us * (SystemCoreClock / 1000000U);
|
||||
uint32_t start = SysTick->VAL;
|
||||
|
||||
//Calculate target value (may wrap around)
|
||||
uint32_t target = (start >= ticks) ? (start - ticks) : (start + 0x01000000 - ticks);
|
||||
target &= 0x00FFFFFF;
|
||||
|
||||
//Wait until we reach the target
|
||||
if (start >= ticks) {
|
||||
//No wraparound case
|
||||
while (SysTick->VAL > target) {}
|
||||
} else {
|
||||
while (SysTick->VAL <= start) {} //Wait for wraparound
|
||||
while (SysTick->VAL > target) {} //Wait for target
|
||||
}
|
||||
}
|
||||
|
||||
void SHAL_delay_ms(uint32_t ms){
|
||||
while(ms-- > 0){
|
||||
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);
|
||||
}
|
||||
47
SHAL/Src/STM32L4xx/EXT/SHAL_EXTI_CALLBACK.cpp
Normal file
47
SHAL/Src/STM32L4xx/EXT/SHAL_EXTI_CALLBACK.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// Created by Luca on 9/3/2025.
|
||||
//
|
||||
|
||||
#include "SHAL_EXTI_CALLBACK.h"
|
||||
|
||||
|
||||
#if defined(STM32L412xx)
|
||||
#elif defined(STM32L422xx)
|
||||
#elif defined(STM32L431xx)
|
||||
#elif defined(STM32L432xx)
|
||||
DEFINE_EXTI_IRQ(0);
|
||||
DEFINE_EXTI_IRQ(1);
|
||||
DEFINE_EXTI_IRQ(2);
|
||||
DEFINE_EXTI_IRQ(3);
|
||||
DEFINE_EXTI_IRQ(4);
|
||||
DEFINE_MULTI_EXTI_IRQ(5,9);
|
||||
DEFINE_MULTI_EXTI_IRQ(10,15);
|
||||
#elif defined(STM32L433xx)
|
||||
#elif defined(STM32L442xx)
|
||||
#elif defined(STM32L443xx)
|
||||
#elif defined(STM32L451xx)
|
||||
#elif defined(STM32L452xx)
|
||||
#elif defined(STM32L462xx)
|
||||
#elif defined(STM32L471xx)
|
||||
#elif defined(STM32L475xx)
|
||||
#elif defined(STM32L476xx)
|
||||
#elif defined(STM32L485xx)
|
||||
#elif defined(STM32L486xx)
|
||||
#elif defined(STM32L496xx)
|
||||
#elif defined(STM32L4A6xx)
|
||||
#elif defined(STM32L4P5xx)
|
||||
#elif defined(STM32L4Q5xx)
|
||||
#elif defined(STM32L4R5xx)
|
||||
#elif defined(STM32L4R7xx)
|
||||
#elif defined(STM32L4R9xx)
|
||||
#elif defined(STM32L4S5xx)
|
||||
#elif defined(STM32L4S7xx)
|
||||
#elif defined(STM32L4S9xx)
|
||||
#error "Please select first the target STM32L4xx device used in your application (in stm32f0xx.h file)"
|
||||
#endif
|
||||
|
||||
|
||||
//Link function to EXTI line
|
||||
void registerEXTICallback(GPIO_Key key, EXTICallback callback){
|
||||
EXTI_callbacks[getGPIOPinNumber(key)] = callback;
|
||||
}
|
||||
308
SHAL/Src/STM32L4xx/Peripheral/ADC/SHAL_ADC.cpp
Normal file
308
SHAL/Src/STM32L4xx/Peripheral/ADC/SHAL_ADC.cpp
Normal file
@@ -0,0 +1,308 @@
|
||||
//
|
||||
// Created by Luca on 9/21/2025.
|
||||
//
|
||||
|
||||
#include "SHAL_ADC.h"
|
||||
#include "SHAL_GPIO.h"
|
||||
#include "SHAL_UART.h"
|
||||
#include <cstdio>
|
||||
|
||||
SHAL_Result SHAL_ADC::init(ADC_Key key) {
|
||||
|
||||
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
|
||||
|
||||
SHAL_apply_bitmask(clock_reg.reg,clock_reg.mask);
|
||||
|
||||
auto clock_select_register = getADCClockSelectRegister();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
configureAlignment(SHAL_ADC_Alignment::RIGHT);
|
||||
configureResolution(SHAL_ADC_Resolution::B12);
|
||||
|
||||
return SHAL_Result::OKAY;
|
||||
}
|
||||
|
||||
SHAL_Result SHAL_ADC::calibrate() {
|
||||
SHAL_ADC_Control_Reg control_reg = getADCControlReg(m_ADCKey);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
addADCChannelToSequence(channel, 0);
|
||||
if(setADCSequenceAmount(1) == SHAL_Result::ERROR) { return 0; }
|
||||
|
||||
if(enable() != SHAL_Result::OKAY) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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) {
|
||||
auto data_reg = getADCDataReg(m_ADCKey); //Where our output will be stored
|
||||
|
||||
setADCSequenceAmount(numChannels); //Convert the correct amount of channels
|
||||
|
||||
for(int i = 0; i < numChannels; i++){
|
||||
auto channel = channels[i];
|
||||
|
||||
auto sampleTimeReg = getADCChannelSamplingTimeRegister(m_ADCKey,channel);
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
startConversion(); //Start ADC conversion
|
||||
|
||||
auto ISR_reg = getADCISRReg(m_ADCKey);
|
||||
|
||||
for(int i = 0; i < numChannels; i++) {
|
||||
if (!SHAL_WAIT_FOR_CONDITION_US(((*ISR_reg.reg & ISR_reg.end_of_conversion_mask) != 0),500)) { //Wait for conversion
|
||||
return SHAL_Result::ERROR; //Failed conversion
|
||||
}
|
||||
result[i] = *data_reg.reg;
|
||||
}
|
||||
|
||||
if (!SHAL_WAIT_FOR_CONDITION_US(((*ISR_reg.reg & ISR_reg.end_of_sequence_mask) != 0),500)) { //Wait for conversion
|
||||
return SHAL_Result::ERROR; //Failed sequence
|
||||
}
|
||||
|
||||
return SHAL_Result::OKAY;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return SHAL_Result::OKAY;
|
||||
}
|
||||
|
||||
|
||||
SHAL_Result SHAL_ADC::startConversion() {
|
||||
auto control_reg = getADCControlReg(m_ADCKey);
|
||||
|
||||
SHAL_apply_bitmask(control_reg.reg,control_reg.start_mask);
|
||||
|
||||
return SHAL_Result::OKAY;
|
||||
}
|
||||
|
||||
bool SHAL_ADC::isValid() {
|
||||
if(m_ADCKey == ADC_Key::INVALID || m_ADCKey == ADC_Key::NUM_ADC){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
SHAL_Result SHAL_ADC::configureResolution(SHAL_ADC_Resolution resolution) {
|
||||
if(!isValid()){
|
||||
return SHAL_Result::ERROR;
|
||||
}
|
||||
|
||||
SHAL_ADC_Config_Reg config_reg = getADCConfigReg(m_ADCKey);
|
||||
|
||||
SHAL_set_bits(config_reg.reg,2,static_cast<uint8_t>(resolution),config_reg.resolution_offset);
|
||||
|
||||
return SHAL_Result::OKAY;
|
||||
}
|
||||
|
||||
SHAL_Result SHAL_ADC::configureAlignment(SHAL_ADC_Alignment alignment) {
|
||||
if(!isValid()){
|
||||
return SHAL_Result::ERROR;
|
||||
}
|
||||
|
||||
SHAL_ADC_Config_Reg config_reg = getADCConfigReg(m_ADCKey);
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
SHAL_Result SHAL_ADC::setADCSequenceAmount(uint32_t amount) {
|
||||
if(!isValid()){return SHAL_Result::ERROR;}
|
||||
|
||||
if(amount == 0){
|
||||
return SHAL_Result::ERROR;
|
||||
}
|
||||
|
||||
SHAL_ADC_Sequence_Amount_Reg sequence_amount_reg = getADCSequenceAmountRegister(m_ADCKey);
|
||||
|
||||
SHAL_set_bits(sequence_amount_reg.reg, 4, amount - 1, sequence_amount_reg.offset);
|
||||
|
||||
return SHAL_Result::OKAY;
|
||||
}
|
||||
|
||||
SHAL_Result SHAL_ADC::addADCChannelToSequence(SHAL_ADC_Channel channel, uint32_t index) {
|
||||
if(!isValid()) { return SHAL_Result::ERROR; }
|
||||
|
||||
auto sequenceRegisters = getADCSequenceRegisters(m_ADCKey);
|
||||
auto channelNum = static_cast<uint8_t>(channel);
|
||||
|
||||
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];
|
||||
|
||||
// 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)];
|
||||
}
|
||||
|
||||
SHAL_ADC& ADCManager::getByIndex(int index) {
|
||||
|
||||
if(index < static_cast<int>(ADC_Key::NUM_ADC)){
|
||||
return m_ADCs[index];
|
||||
}
|
||||
return m_ADCs[0];
|
||||
}
|
||||
126
SHAL/Src/STM32L4xx/Peripheral/GPIO/SHAL_GPIO.cpp
Normal file
126
SHAL/Src/STM32L4xx/Peripheral/GPIO/SHAL_GPIO.cpp
Normal 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];
|
||||
}
|
||||
142
SHAL/Src/STM32L4xx/Peripheral/I2C/SHAL_I2C.cpp
Normal file
142
SHAL/Src/STM32L4xx/Peripheral/I2C/SHAL_I2C.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
//
|
||||
// Created by Luca on 9/9/2025.
|
||||
//
|
||||
|
||||
#include "SHAL_I2C.h"
|
||||
#include "SHAL_GPIO.h"
|
||||
|
||||
#include "SHAL_UART.h"
|
||||
|
||||
void SHAL_I2C::init(I2C_Pair pair) volatile {
|
||||
m_I2CPair = pair;
|
||||
|
||||
SHAL_I2C_Pair I2CPair = getI2CPair(pair); //Get the I2C_PAIR information to be initialized
|
||||
|
||||
//Get the SHAL_GPIO pins for this SHAL_I2C setup
|
||||
GPIO_Key SCL_Key = I2CPair.SCL_Key; //SCL pin
|
||||
GPIO_Key SDA_Key = I2CPair.SDA_Key; //SDA pin
|
||||
|
||||
SHAL_I2C_Enable_Reg pairI2CEnable = getI2CEnableReg(pair); //Register and mask to enable the I2C peripheral
|
||||
|
||||
*pairI2CEnable.reg &= ~pairI2CEnable.mask; //Enable I2C peripheral clock
|
||||
|
||||
GET_GPIO(SCL_Key).setPinMode(PinMode::ALTERNATE_FUNCTION_MODE); //Implicitly initializes and enables GPIO bus
|
||||
GET_GPIO(SDA_Key).setPinMode(PinMode::ALTERNATE_FUNCTION_MODE);
|
||||
|
||||
GET_GPIO(SCL_Key).setAlternateFunction(I2CPair.SCL_Mask);
|
||||
GET_GPIO(SDA_Key).setAlternateFunction(I2CPair.SDA_Mask);
|
||||
|
||||
//These may be abstracted further to support multiple I2C configurations
|
||||
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);
|
||||
|
||||
GET_GPIO(SCL_Key).setInternalResistor(InternalResistorType::PULLUP);
|
||||
GET_GPIO(SDA_Key).setInternalResistor(InternalResistorType::PULLUP);
|
||||
|
||||
SHAL_I2C_Reset_Reg pairI2CReset = getI2CResetReg(pair);
|
||||
|
||||
*pairI2CEnable.reg |= pairI2CEnable.mask; //Enable I2C peripheral clock
|
||||
|
||||
*pairI2CReset.reg |= pairI2CReset.mask; //Reset peripheral
|
||||
*pairI2CReset.reg &= ~pairI2CReset.mask; //Reset peripheral
|
||||
}
|
||||
|
||||
void SHAL_I2C::setClockConfig(uint8_t prescaler, uint8_t dataSetupTime, uint8_t dataHoldTime, uint8_t SCLHighPeriod, uint8_t SCLLowPeriod) {
|
||||
|
||||
SHAL_I2C_Timing_Reg clockReg = getI2CTimerReg(m_I2CPair);
|
||||
|
||||
*clockReg.reg |= (prescaler << clockReg.prescaler_offset);
|
||||
*clockReg.reg |= (dataSetupTime << clockReg.dataSetupTime_offset);
|
||||
*clockReg.reg |= (dataHoldTime << clockReg.dataHoldTime_offset);
|
||||
*clockReg.reg |= (SCLHighPeriod << clockReg.SCLHighPeriod_offset);
|
||||
*clockReg.reg |= (SCLLowPeriod << clockReg.SCLLowPeriod_offset);
|
||||
|
||||
getI2CPair(m_I2CPair).I2CReg->CR1 |= I2C_CR1_PE; //Enable I2C peripheral
|
||||
}
|
||||
|
||||
void SHAL_I2C::setClockConfig(uint32_t configuration) {
|
||||
*getI2CTimerReg(m_I2CPair).reg = configuration;
|
||||
|
||||
getI2CPair(m_I2CPair).I2CReg->CR1 |= I2C_CR1_PE; //Enable I2C peripheral
|
||||
}
|
||||
|
||||
void SHAL_I2C::masterWriteRead(uint8_t addr,const uint8_t* writeData, size_t writeLen, uint8_t* readData, size_t readLen) {
|
||||
|
||||
volatile I2C_TypeDef* I2CPeripheral = getI2CPair(m_I2CPair).I2CReg;
|
||||
|
||||
if(!SHAL_WAIT_FOR_CONDITION_MS((I2CPeripheral->ISR & I2C_ISR_BUSY) == 0, 100)){
|
||||
SHAL_UART2.sendString("I2C timed out waiting for not busy\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
//Write phase
|
||||
if (writeLen > 0) {
|
||||
//Configure: NBYTES = wlen, write mode, START
|
||||
I2CPeripheral->CR2 = (addr << 1) | (writeLen << I2C_CR2_NBYTES_Pos) | I2C_CR2_START;
|
||||
|
||||
for (size_t i = 0; i < writeLen; i++) {
|
||||
if(!SHAL_WAIT_FOR_CONDITION_MS((I2CPeripheral->ISR & I2C_ISR_TXIS) != 0, 100)){
|
||||
SHAL_UART2.sendString("I2C timed out waiting for TX\r\n");
|
||||
return;
|
||||
}
|
||||
I2CPeripheral->TXDR = writeData[i];
|
||||
}
|
||||
|
||||
//Wait until transfer complete
|
||||
if(!SHAL_WAIT_FOR_CONDITION_MS((I2CPeripheral->ISR & I2C_ISR_TC) != 0, 100)){
|
||||
SHAL_UART2.sendString("I2C timed out waiting for TC\r\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//Read phase
|
||||
if (readLen > 0) {
|
||||
|
||||
SHAL_UART2.sendString("Read initiated\r\n");
|
||||
|
||||
I2CPeripheral->CR2 &= ~(I2C_CR2_NBYTES | I2C_CR2_SADD | I2C_CR2_RD_WRN);
|
||||
I2CPeripheral->CR2 |= (addr << 1) |
|
||||
I2C_CR2_RD_WRN |
|
||||
(readLen << I2C_CR2_NBYTES_Pos) |
|
||||
I2C_CR2_START | I2C_CR2_AUTOEND;
|
||||
|
||||
for (size_t i = 0; i < readLen; i++) {
|
||||
if(!SHAL_WAIT_FOR_CONDITION_MS((I2CPeripheral->ISR & I2C_ISR_RXNE) != 0 , 100)){
|
||||
SHAL_UART2.sendString("I2C timed out waiting for RXNE\r\n");
|
||||
return;
|
||||
}
|
||||
SHAL_UART2.sendString("Read byte");
|
||||
readData[i] = static_cast<uint8_t>(I2CPeripheral->RXDR);
|
||||
}
|
||||
}
|
||||
else{
|
||||
I2CPeripheral->CR2 |= I2C_CR2_STOP;
|
||||
}
|
||||
}
|
||||
|
||||
void SHAL_I2C::masterWrite(uint8_t addr, const uint8_t *writeData, uint8_t writeLen) {
|
||||
masterWriteRead(addr,writeData,writeLen,nullptr,0);
|
||||
}
|
||||
|
||||
void SHAL_I2C::masterRead(uint8_t addr, uint8_t *readBuffer, uint8_t bytesToRead) {
|
||||
masterWriteRead(addr,nullptr,0,readBuffer,bytesToRead);
|
||||
}
|
||||
|
||||
uint8_t SHAL_I2C::masterWriteReadByte(uint8_t addr, const uint8_t *writeData, size_t writeLen) {
|
||||
uint8_t val = 0;
|
||||
masterWriteRead(addr, writeData, writeLen, &val, 1);
|
||||
return val;
|
||||
}
|
||||
|
||||
SHAL_I2C& I2CManager::get(uint8_t I2CBus) {
|
||||
|
||||
if(I2CBus > NUM_I2C_BUSES - 1){
|
||||
assert(false);
|
||||
//Memory fault
|
||||
}
|
||||
|
||||
return m_I2CBuses[I2CBus];
|
||||
}
|
||||
117
SHAL/Src/STM32L4xx/Peripheral/Timer/SHAL_TIM.cpp
Normal file
117
SHAL/Src/STM32L4xx/Peripheral/Timer/SHAL_TIM.cpp
Normal 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)];
|
||||
}
|
||||
16
SHAL/Src/STM32L4xx/Peripheral/Timer/SHAL_TIM_CALLBACK.cpp
Normal file
16
SHAL/Src/STM32L4xx/Peripheral/Timer/SHAL_TIM_CALLBACK.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// Created by Luca on 8/28/2025.
|
||||
//
|
||||
|
||||
#include "SHAL_TIM_CALLBACK.h"
|
||||
|
||||
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, 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<uint32_t>(key)] = callback;
|
||||
}
|
||||
87
SHAL/Src/STM32L4xx/Peripheral/UART/SHAL_UART.cpp
Normal file
87
SHAL/Src/STM32L4xx/Peripheral/UART/SHAL_UART.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file SHAL_TIM.h
|
||||
* @author Luca Lizaranzu
|
||||
* @brief Related to USART and SHAL_UART abstractions
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
|
||||
#include "SHAL_UART.h"
|
||||
#include "SHAL_GPIO.h"
|
||||
|
||||
void SHAL_UART::init(UART_Pair_Key pair){
|
||||
|
||||
m_key = pair;
|
||||
|
||||
SHAL_UART_Pair uart_pair = getUARTPair(pair); //Get the UART_PAIR information to be initialized
|
||||
|
||||
//Get the SHAL_GPIO pins for this SHAL_UART setup
|
||||
GPIO_Key Tx_Key = uart_pair.TxKey; //Tx pin
|
||||
GPIO_Key Rx_Key = uart_pair.RxKey; //Rx pin
|
||||
|
||||
GET_GPIO(Tx_Key).setPinMode(PinMode::ALTERNATE_FUNCTION_MODE);
|
||||
GET_GPIO(Rx_Key).setPinMode(PinMode::ALTERNATE_FUNCTION_MODE);
|
||||
|
||||
GET_GPIO(Tx_Key).setAlternateFunction(uart_pair.TxAlternateFunctionMask);
|
||||
GET_GPIO(Rx_Key).setAlternateFunction(uart_pair.RxAlternateFunctionMask);
|
||||
|
||||
SHAL_UART_Enable_Register pairUARTEnable = getUARTEnableReg(pair); //Register and mask to enable the SHAL_UART channel
|
||||
|
||||
*pairUARTEnable.reg |= pairUARTEnable.mask; //Enable SHAL_UART line
|
||||
|
||||
|
||||
}
|
||||
|
||||
void SHAL_UART::begin(uint32_t baudRate) volatile {
|
||||
|
||||
USART_TypeDef* usart = getUARTPair(m_key).USARTReg;
|
||||
|
||||
auto control_reg = getUARTControlRegister1(m_key);
|
||||
|
||||
SHAL_clear_bitmask(control_reg.reg, control_reg.usart_enable_mask); //Clear enable bit (turn off usart)
|
||||
|
||||
usart->CR1 = 0; //Clear USART config
|
||||
|
||||
SHAL_apply_bitmask(control_reg.reg, control_reg.transmit_enable_mask); //Enable Tx
|
||||
SHAL_apply_bitmask(control_reg.reg, control_reg.receive_enable_mask); //Enable Rx
|
||||
|
||||
auto baud_rate_reg = getUARTBaudRateGenerationRegister(m_key);
|
||||
|
||||
uint32_t adjustedBaudRate = SystemCoreClock / baudRate;
|
||||
|
||||
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) {//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_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_register_value_16(transmit_reg.reg, static_cast<uint16_t>(c));
|
||||
}
|
||||
|
||||
|
||||
|
||||
SHAL_UART& UARTManager::get(uint8_t uart) {
|
||||
|
||||
if(uart > NUM_USART_LINES - 1){
|
||||
assert(false);
|
||||
//Memory fault
|
||||
}
|
||||
|
||||
return m_UARTs[uart];
|
||||
}
|
||||
332
SHAL/Src/STM32L4xx/System/system_stm32l4xx.c
Normal file
332
SHAL/Src/STM32L4xx/System/system_stm32l4xx.c
Normal file
@@ -0,0 +1,332 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file system_stm32l4xx.c
|
||||
* @author MCD Application Team
|
||||
* @brief CMSIS Cortex-M4 Device Peripheral Access Layer System Source File
|
||||
*
|
||||
* This file provides two functions and one global variable to be called from
|
||||
* user application:
|
||||
* - SystemInit(): This function is called at startup just after reset and
|
||||
* before branch to main program. This call is made inside
|
||||
* the "startup_stm32l4xx.s" file.
|
||||
*
|
||||
* - SystemCoreClock variable: Contains the core clock (HCLK), it can be used
|
||||
* by the user application to setup the SysTick
|
||||
* timer or configure other parameters.
|
||||
*
|
||||
* - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must
|
||||
* be called whenever the core clock is changed
|
||||
* during program execution.
|
||||
*
|
||||
* After each device reset the MSI (4 MHz) is used as system clock source.
|
||||
* Then SystemInit() function is called, in "startup_stm32l4xx.s" file, to
|
||||
* configure the system clock before to branch to main program.
|
||||
*
|
||||
* This file configures the system clock as follows:
|
||||
*=============================================================================
|
||||
*-----------------------------------------------------------------------------
|
||||
* System Clock source | MSI
|
||||
*-----------------------------------------------------------------------------
|
||||
* SYSCLK(Hz) | 4000000
|
||||
*-----------------------------------------------------------------------------
|
||||
* HCLK(Hz) | 4000000
|
||||
*-----------------------------------------------------------------------------
|
||||
* AHB Prescaler | 1
|
||||
*-----------------------------------------------------------------------------
|
||||
* APB1 Prescaler | 1
|
||||
*-----------------------------------------------------------------------------
|
||||
* APB2 Prescaler | 1
|
||||
*-----------------------------------------------------------------------------
|
||||
* PLL_M | 1
|
||||
*-----------------------------------------------------------------------------
|
||||
* PLL_N | 8
|
||||
*-----------------------------------------------------------------------------
|
||||
* PLL_P | 7
|
||||
*-----------------------------------------------------------------------------
|
||||
* PLL_Q | 2
|
||||
*-----------------------------------------------------------------------------
|
||||
* PLL_R | 2
|
||||
*-----------------------------------------------------------------------------
|
||||
* PLLSAI1_P | NA
|
||||
*-----------------------------------------------------------------------------
|
||||
* PLLSAI1_Q | NA
|
||||
*-----------------------------------------------------------------------------
|
||||
* PLLSAI1_R | NA
|
||||
*-----------------------------------------------------------------------------
|
||||
* PLLSAI2_P | NA
|
||||
*-----------------------------------------------------------------------------
|
||||
* PLLSAI2_Q | NA
|
||||
*-----------------------------------------------------------------------------
|
||||
* PLLSAI2_R | NA
|
||||
*-----------------------------------------------------------------------------
|
||||
* Require 48MHz for USB OTG FS, | Disabled
|
||||
* SDIO and RNG clock |
|
||||
*-----------------------------------------------------------------------------
|
||||
*=============================================================================
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* Copyright (c) 2017 STMicroelectronics.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed under terms that can be found in the LICENSE file
|
||||
* in the root directory of this software component.
|
||||
* If no LICENSE file comes with this software, it is provided AS-IS.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/** @addtogroup CMSIS
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @addtogroup stm32l4xx_system
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @addtogroup STM32L4xx_System_Private_Includes
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "stm32l4xx.h"
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @addtogroup STM32L4xx_System_Private_TypesDefinitions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @addtogroup STM32L4xx_System_Private_Defines
|
||||
* @{
|
||||
*/
|
||||
|
||||
#if !defined (HSE_VALUE)
|
||||
#define HSE_VALUE 8000000U /*!< Value of the External oscillator in Hz */
|
||||
#endif /* HSE_VALUE */
|
||||
|
||||
#if !defined (MSI_VALUE)
|
||||
#define MSI_VALUE 4000000U /*!< Value of the Internal oscillator in Hz*/
|
||||
#endif /* MSI_VALUE */
|
||||
|
||||
#if !defined (HSI_VALUE)
|
||||
#define HSI_VALUE 16000000U /*!< Value of the Internal oscillator in Hz*/
|
||||
#endif /* HSI_VALUE */
|
||||
|
||||
/* Note: Following vector table addresses must be defined in line with linker
|
||||
configuration. */
|
||||
/*!< Uncomment the following line if you need to relocate the vector table
|
||||
anywhere in Flash or Sram, else the vector table is kept at the automatic
|
||||
remap of boot address selected */
|
||||
/* #define USER_VECT_TAB_ADDRESS */
|
||||
|
||||
#if defined(USER_VECT_TAB_ADDRESS)
|
||||
/*!< Uncomment the following line if you need to relocate your vector Table
|
||||
in Sram else user remap will be done in Flash. */
|
||||
/* #define VECT_TAB_SRAM */
|
||||
|
||||
#if defined(VECT_TAB_SRAM)
|
||||
#define VECT_TAB_BASE_ADDRESS SRAM1_BASE /*!< Vector Table base address field.
|
||||
This value must be a multiple of 0x200. */
|
||||
#define VECT_TAB_OFFSET 0x00000000U /*!< Vector Table base offset field.
|
||||
This value must be a multiple of 0x200. */
|
||||
#else
|
||||
#define VECT_TAB_BASE_ADDRESS FLASH_BASE /*!< Vector Table base address field.
|
||||
This value must be a multiple of 0x200. */
|
||||
#define VECT_TAB_OFFSET 0x00000000U /*!< Vector Table base offset field.
|
||||
This value must be a multiple of 0x200. */
|
||||
#endif /* VECT_TAB_SRAM */
|
||||
#endif /* USER_VECT_TAB_ADDRESS */
|
||||
|
||||
/******************************************************************************/
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @addtogroup STM32L4xx_System_Private_Macros
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @addtogroup STM32L4xx_System_Private_Variables
|
||||
* @{
|
||||
*/
|
||||
/* The SystemCoreClock variable is updated in three ways:
|
||||
1) by calling CMSIS function SystemCoreClockUpdate()
|
||||
2) by calling HAL API function HAL_RCC_GetHCLKFreq()
|
||||
3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency
|
||||
Note: If you use this function to configure the system clock; then there
|
||||
is no need to call the 2 first functions listed above, since SystemCoreClock
|
||||
variable is updated automatically.
|
||||
*/
|
||||
uint32_t SystemCoreClock = 4000000U;
|
||||
|
||||
const uint8_t AHBPrescTable[16] = {0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 1U, 2U, 3U, 4U, 6U, 7U, 8U, 9U};
|
||||
const uint8_t APBPrescTable[8] = {0U, 0U, 0U, 0U, 1U, 2U, 3U, 4U};
|
||||
const uint32_t MSIRangeTable[12] = {100000U, 200000U, 400000U, 800000U, 1000000U, 2000000U, \
|
||||
4000000U, 8000000U, 16000000U, 24000000U, 32000000U, 48000000U};
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @addtogroup STM32L4xx_System_Private_FunctionPrototypes
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @addtogroup STM32L4xx_System_Private_Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Setup the microcontroller system.
|
||||
* @retval None
|
||||
*/
|
||||
|
||||
void SystemInit(void)
|
||||
{
|
||||
#if defined(USER_VECT_TAB_ADDRESS)
|
||||
/* Configure the Vector Table location -------------------------------------*/
|
||||
SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET;
|
||||
#endif
|
||||
|
||||
/* FPU settings ------------------------------------------------------------*/
|
||||
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
|
||||
SCB->CPACR |= ((3UL << 20U)|(3UL << 22U)); /* set CP10 and CP11 Full Access */
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update SystemCoreClock variable according to Clock Register Values.
|
||||
* The SystemCoreClock variable contains the core clock (HCLK), it can
|
||||
* be used by the user application to setup the SysTick timer or configure
|
||||
* other parameters.
|
||||
*
|
||||
* @note Each time the core clock (HCLK) changes, this function must be called
|
||||
* to update SystemCoreClock variable value. Otherwise, any configuration
|
||||
* based on this variable will be incorrect.
|
||||
*
|
||||
* @note - The system frequency computed by this function is not the real
|
||||
* frequency in the chip. It is calculated based on the predefined
|
||||
* constant and the selected clock source:
|
||||
*
|
||||
* - If SYSCLK source is MSI, SystemCoreClock will contain the MSI_VALUE(*)
|
||||
*
|
||||
* - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(**)
|
||||
*
|
||||
* - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(***)
|
||||
*
|
||||
* - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(***)
|
||||
* or HSI_VALUE(*) or MSI_VALUE(*) multiplied/divided by the PLL factors.
|
||||
*
|
||||
* (*) MSI_VALUE is a constant defined in stm32l4xx_hal.h file (default value
|
||||
* 4 MHz) but the real value may vary depending on the variations
|
||||
* in voltage and temperature.
|
||||
*
|
||||
* (**) HSI_VALUE is a constant defined in stm32l4xx_hal.h file (default value
|
||||
* 16 MHz) but the real value may vary depending on the variations
|
||||
* in voltage and temperature.
|
||||
*
|
||||
* (***) HSE_VALUE is a constant defined in stm32l4xx_hal.h file (default value
|
||||
* 8 MHz), user has to ensure that HSE_VALUE is same as the real
|
||||
* frequency of the crystal used. Otherwise, this function may
|
||||
* have wrong result.
|
||||
*
|
||||
* - The result of this function could be not correct when using fractional
|
||||
* value for HSE crystal.
|
||||
*
|
||||
* @retval None
|
||||
*/
|
||||
void SystemCoreClockUpdate(void)
|
||||
{
|
||||
uint32_t tmp, msirange, pllvco, pllsource, pllm, pllr;
|
||||
|
||||
/* Get MSI Range frequency--------------------------------------------------*/
|
||||
if ((RCC->CR & RCC_CR_MSIRGSEL) == 0U)
|
||||
{ /* MSISRANGE from RCC_CSR applies */
|
||||
msirange = (RCC->CSR & RCC_CSR_MSISRANGE) >> 8U;
|
||||
}
|
||||
else
|
||||
{ /* MSIRANGE from RCC_CR applies */
|
||||
msirange = (RCC->CR & RCC_CR_MSIRANGE) >> 4U;
|
||||
}
|
||||
/*MSI frequency range in HZ*/
|
||||
msirange = MSIRangeTable[msirange];
|
||||
|
||||
/* Get SYSCLK source -------------------------------------------------------*/
|
||||
switch (RCC->CFGR & RCC_CFGR_SWS)
|
||||
{
|
||||
case 0x00: /* MSI used as system clock source */
|
||||
SystemCoreClock = msirange;
|
||||
break;
|
||||
|
||||
case 0x04: /* HSI used as system clock source */
|
||||
SystemCoreClock = HSI_VALUE;
|
||||
break;
|
||||
|
||||
case 0x08: /* HSE used as system clock source */
|
||||
SystemCoreClock = HSE_VALUE;
|
||||
break;
|
||||
|
||||
case 0x0C: /* PLL used as system clock source */
|
||||
/* PLL_VCO = (HSE_VALUE or HSI_VALUE or MSI_VALUE/ PLLM) * PLLN
|
||||
SYSCLK = PLL_VCO / PLLR
|
||||
*/
|
||||
pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC);
|
||||
pllm = ((RCC->PLLCFGR & RCC_PLLCFGR_PLLM) >> 4U) + 1U ;
|
||||
|
||||
switch (pllsource)
|
||||
{
|
||||
case 0x02: /* HSI used as PLL clock source */
|
||||
pllvco = (HSI_VALUE / pllm);
|
||||
break;
|
||||
|
||||
case 0x03: /* HSE used as PLL clock source */
|
||||
pllvco = (HSE_VALUE / pllm);
|
||||
break;
|
||||
|
||||
default: /* MSI used as PLL clock source */
|
||||
pllvco = (msirange / pllm);
|
||||
break;
|
||||
}
|
||||
pllvco = pllvco * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 8U);
|
||||
pllr = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLR) >> 25U) + 1U) * 2U;
|
||||
SystemCoreClock = pllvco/pllr;
|
||||
break;
|
||||
|
||||
default:
|
||||
SystemCoreClock = msirange;
|
||||
break;
|
||||
}
|
||||
/* Compute HCLK clock frequency --------------------------------------------*/
|
||||
/* Get HCLK prescaler */
|
||||
tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4U)];
|
||||
/* HCLK clock frequency */
|
||||
SystemCoreClock >>= tmp;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
||||
Reference in New Issue
Block a user