2020-08-08 04:56 AM
Hi all,
from last 8 yrs i am writing programs for microcontrollers (mostly project on microchip, st) 8 bit/ 32 bit.
i always write programs by writing directly to the registers. i never used any library like SPL, HAL, LL doesn't matter how long the code is i always directly write to registers .
what is better way writing directly to registers or using any library by future prospective of my carrer.
i wrote something like this.
////////////////////////////////////////////////////////////////////
#include "stm32f0xx.h"
#include "stm32f030x8.h"
void GPIO_Init(void);
void ADC_Init(void);
void TIM3_Init(void);
void delay(void);
unsigned int i=0,j=0,count = 0, data[10],avg = 0,diff=0,run_curr= 0,last_curr= 0,stability=0,FB=0,out_curr=0,aux_out_curr=0,last_out_curr=0,duty_cycle=0;
unsigned long input=0,k=0;
int read=5;
int main()
{
/****start PLL setting for system clock at 48Mhz using 8Mhz HSI****/
RCC->CFGR |= ((1<<19)|(1<<21)); // PLL Multification factor = 12
RCC->CFGR |= (1<<1); // PLL selected as system clock
RCC->CR |= (1<<24); // Enable / ON PLL
while((RCC->CR & (1<<25)) == 0); // Wait until PLL gets Ready
/****PLL settigs end here****/
GPIO_Init();
TIM3_Init();
ADC_Init();
while(1)
{
/****** Input current sense routine*******/
//data = 0;
ADC1->CHSELR = (1<<0);
for(i = 0; i <=9; i++)
{
input = 0;
for(j = 1; j <=100; j++)
{
if ((ADC1->CR & (1<<2)) == 0)
{
ADC1->CR |= (1<<2);
}
while((ADC1->ISR & (1<<2)) == 0);
input += ADC1->DR;
}
data[i] = input/100;
//for(k=1;k<100000;k++);
}
/****** Input current Sense routine ends here******/
if((data[9] >=3315)) // 11.1V - 11.6V
{
if(data[9]>=data[0])
{
diff=data[9]-data[0];
}
else
{
diff=data[0]-data[9];
}
if(diff < 2)
{
run_curr = data[9];
ADC1->CHSELR = (1<<7);
input = 0;
for(j = 1; j <=100; j++)
{
if ((ADC1->CR & (1<<2)) == 0)
{
ADC1->CR |= (1<<2);
}
while((ADC1->ISR & (1<<2)) == 0);
input += ADC1->DR;
}
out_curr = input/100;
}
else
{
}
GPIOA->ODR &= ~(1<<5); //LED INDICATION OFF
GPIOA->ODR |= (1<<4); // Mosfet 1 ON
GPIOB->ODR &= ~(1<<0); // Mosfet 2 Off
TIM3->CCR1 = 1800; // duty cylce = 25%
/*ADC1->CHSELR = (1<<1);
input = 0;
for(j = 1; j <=100; j++)
{
if ((ADC1->CR & (1<<2)) == 0)
{
ADC1->CR |= (1<<2);
}
while((ADC1->ISR & (1<<2)) == 0);
input += ADC1->DR;
}
FB = input/100;
if(FB<=(run_curr))
{
duty_cycle++;
if(duty_cycle>360)
{
duty_cycle = 360;
}
TIM3->CCR1 = duty_cycle;
}
else if(FB>(run_curr))
{
duty_cycle--;
TIM3->CCR1 = duty_cycle;
}*/
}
else
{
//stability = 0;
last_out_curr = out_curr;
//last_curr = run_curr;
GPIOA->ODR |= (1<<5); //LED INDICATION ON
GPIOA->ODR &= ~(1<<4); // Mosfet 1 Off
GPIOB->ODR |= (1<<0); // Mosfet 2 ON
//TIM3->CCR1 = 200; // duty cylce = 25%
ADC1->CHSELR = (1<<7);
input = 0;
for(j = 1; j <=100; j++)
{
if ((ADC1->CR & (1<<2)) == 0)
{
ADC1->CR |= (1<<2);
}
while((ADC1->ISR & (1<<2)) == 0);
input += ADC1->DR;
}
aux_out_curr = input/100;
if(aux_out_curr==last_out_curr)
{
}
if(aux_out_curr<last_out_curr)
{
duty_cycle++;
if(duty_cycle>1870)
{
duty_cycle = 1870;
}
TIM3->CCR1 = duty_cycle;
}
else if(aux_out_curr>last_out_curr)
{
duty_cycle--;
TIM3->CCR1 = duty_cycle;
}
}
}
}
void GPIO_Init()
{
RCC->AHBENR |= (1<<17); // Enable clock for GPIOA
RCC->AHBENR |= (1<<18); // Enable clock for GPIOB
GPIOA->MODER |= (1<<10); // PA5 as general purpose output mode
GPIOA->OTYPER &= ~(1<<5); // PA5 as output push pull
GPIOA->OSPEEDR |= ((1<<10)|(1<<11)); // PA5 at high speed
GPIOA->PUPDR &= ~((1<<10)|(1<<11)); // PA5 as no pull-up, pull-down
GPIOA->ODR &= ~(1<<5); // PA5 at low ouput level
GPIOA->MODER |= (1<<8); // PA4 as general purpose output mode
GPIOA->OTYPER &= ~(1<<4); // PA4 as output push pull
GPIOA->OSPEEDR |= ((1<<8)|(1<<9)); // PA4 at high speed
GPIOA->PUPDR &= ~((1<<8)|(1<<9)); // PA4 as no pull-up, pull-down
GPIOA->ODR &= ~(1<<4); // PA4 at low ouput level
GPIOB->MODER |= (1<<0); // PB0 as general purpose output mode
GPIOB->OTYPER &= ~(1<<0); // PB0 as output push pull
GPIOB->OSPEEDR |= ((1<<0)|(1<<1)); // PB0 at high speed
GPIOB->PUPDR &= ~((1<<0)|(1<<1)); // PB0 as no pull-up, pull-down
GPIOB->ODR |= (1<<0); // PB0 at high ouput level
GPIOA->MODER &= ~((1<<0)|(1<<1)); // PA0 as input mode
GPIOA->PUPDR &= ~((1<<0)|(1<<1)); // PA0 as no pull-up, pull-down
GPIOA->MODER &= ~((1<<2)|(1<<3)); // PA1 as input mode
GPIOA->PUPDR &= ~((1<<2)|(1<<3)); // PA1 as no pull-up, pull-down
GPIOA->MODER &= ~((1<<14)|(1<<15)); // PA7 as input mode
GPIOA->PUPDR &= ~((1<<14)|(1<<15)); // PA7 as no pull-up, pull-down
GPIOA->MODER |= (1<<13); // PA6 in alternate function mode
GPIOA->OTYPER &= ~(1<<6); // PA6 as output push pull
GPIOA->OSPEEDR |= ((1<<12)|(1<<13)); // PA6 at high speed
GPIOA->PUPDR &= ~((1<<12)|(1<<13)); // PA6 no pull-up, no pull-down
GPIOA->AFR[0] |= (1<<24); // PA6 as TIM3_CH1 alternate function
}
void ADC_Init()
{
RCC->APB2ENR |= (1<<9); // Enable clock for ADC
ADC1->CFGR2 |= (1<<30); // ADC clock = PCLK/2
ADC1->CFGR1 &= ~((1<<3)|(1<<4)); // 12 bit resolution
ADC1->CR = 1; // Enable ADC
while((ADC1->ISR & 1) ==0); // Wait for ADC Ready
ADC1->CFGR1 &= ~(1<<13); // Single conversion mode
ADC1->CFGR1 &= ~((1<<10)|(1<<11)); // Software tiggerd
ADC1->CFGR1 &= ~(1<<5); // Converted data RIGHT alignment
ADC1->CFGR1 &= ~(1<<2); // Upward sequence of conversion CH0 to CH17
ADC1->SMPR |= ((1<<0)|(1<<1)); // sampling time = 28.5 ADC clock
//ADC1->CHSELR |= ((1<<0)|(1<<1)); // CH0,CH1 is selected for conversion
}
void TIM3_Init()
{
RCC->APB1ENR |= (1<<1); // Enable clock for TIM3
// Configuration of TIM3_CH1 as PWM output Mode1 with 15% duty cycle @ 40KHz
// TIM3 clock = 8000000Hz
// PSC = 0 i.e. TIM3 counting clock will be same a timer clock 8000000Hz
// ARR = 399 i.e. pwm output freq. = (Timer counter clock / (ARR+1)) = (8000000 / (399+1)) = 20000Hz
// CCR1 = 100 i.e. duty cycle = (CCR1 / (ARR+1)) = (100 / (399+1)) = 25%
TIM3->PSC = 0;
TIM3->ARR = 2399;
TIM3->CCMR1 |= ((1<<5)|(1<<6)); // TIM3_CH1 as PWM mode-1
TIM3->CCR1 = 0; // initaillay duty cylce = 0
TIM3->CCER = 1; // TIM3_CH1 is Active, active high
TIM3->CR1 = 1; // TIM3 Counter enable
}
void delay()
{
for(k=1;k<2500000;k++);
}
/////////////////////////////////////////////////////////////
this code is small but for lengthy code i have same habit.
i know one advantage it makes my hex file less size.
Solved! Go to Solution.
2020-08-08 07:51 AM
> what is better way writing directly to registers or using any library by future prospective of my carrer.
It's going to vary depending on what you want to do.
HAL is fine, there are an increasing number of examples, it's supported by ST.
Direct register access is future-proof, as long as the chips exist. Also a fine option.
LL is mostly a renamed direct register access. I do not recommend it ever.
One problem with direct register is it's hard to read. Compare
RCC->AHBENR |= (1<<17); // Enable clock for GPIOA
and
__HAL_RCC_GPIOA_CLK_ENABLE();
One of them relies on comments to explain what it's doing. One of them relies on the library to be correct. If I'm inheriting a project from a random developer, I'll trust the library way more than the comments. Suppose the pin is changed from GPIOA to GPIOB and someone changes the 1<<17 value, but not the comment. You're probably going to spend a bit tracking down that bug.
Another example:
TIM3->CR1 = 1; // TIM3 Counter enable
This is hard to read. Again, you're relying on the comments. There are standard CMSIS headers that have all the bits defined. Do this instead:
TIM3->CR1 |= TIM_CR1_CEN;
It makes the code way more readable.
> i know one advantage it makes my hex file less size.
While true, with the larger flash memory chips (1-2MB), this isn't typically a limiting factor.
Ultimately, for the "future prospective of my career," the most beneficial thing would be to be flexible and understand how to work in both environments. Realize a large part of programming is working with other people's code and not your own. Understand that things change and the best thing today is unlikely be the best thing 20 years from now. The longevity of C and C++ is an anomaly.
2020-08-08 07:51 AM
> what is better way writing directly to registers or using any library by future prospective of my carrer.
It's going to vary depending on what you want to do.
HAL is fine, there are an increasing number of examples, it's supported by ST.
Direct register access is future-proof, as long as the chips exist. Also a fine option.
LL is mostly a renamed direct register access. I do not recommend it ever.
One problem with direct register is it's hard to read. Compare
RCC->AHBENR |= (1<<17); // Enable clock for GPIOA
and
__HAL_RCC_GPIOA_CLK_ENABLE();
One of them relies on comments to explain what it's doing. One of them relies on the library to be correct. If I'm inheriting a project from a random developer, I'll trust the library way more than the comments. Suppose the pin is changed from GPIOA to GPIOB and someone changes the 1<<17 value, but not the comment. You're probably going to spend a bit tracking down that bug.
Another example:
TIM3->CR1 = 1; // TIM3 Counter enable
This is hard to read. Again, you're relying on the comments. There are standard CMSIS headers that have all the bits defined. Do this instead:
TIM3->CR1 |= TIM_CR1_CEN;
It makes the code way more readable.
> i know one advantage it makes my hex file less size.
While true, with the larger flash memory chips (1-2MB), this isn't typically a limiting factor.
Ultimately, for the "future prospective of my career," the most beneficial thing would be to be flexible and understand how to work in both environments. Realize a large part of programming is working with other people's code and not your own. Understand that things change and the best thing today is unlikely be the best thing 20 years from now. The longevity of C and C++ is an anomaly.
2020-08-08 08:34 AM
Thank you for your reply.
Mentioning each point/part of question with good explanation, such a very nice reply
Thanks once again
2020-08-10 05:25 PM
library option is good. I recommend using libraries unless:
1- you need the firmware to run in fastest speed possible
2- you need to use the maximum functionality of hardware. some special functionalities are not covered by libraries because they are rarely used.
you can choose the proper way based on your project requirements.
2020-08-10 10:00 PM
@prain
yes you are right.
please clear one thing also to use HAL library in keil is it compulsory to use CubeMX. because when i create a project in keil and select device > startup or device > STM32CubeHAL it says configuration through cubemx is required. as shown in image
2020-08-11 09:24 AM
you don't need to make a new project in keil from scratch. cubemx generates all thing you need (source files, keil project file and ... ). It is the fastest way you can generate new project. Take a look on cubemx page in st website. there are very good documents to start.
2020-08-11 09:43 AM
@prain
Yes I have done 2 projects by directly creating project from cube mx
Configuration of each pin, clock in cubemx and generating keil project with all configuration code written by cubemx.
That's very easy.