Engineers' Practice: How to Create a Bare Metal Program Framework for MCUs

Publisher:SereneMelodyLatest update time:2021-06-30 Source: eefocusKeywords:MCU Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

Preface

Not long ago, a friend of mine who was a tester switched to a development job. He encountered a question in the interview, and he didn't understand it, so he called me to ask. The question was roughly:

When developing a bare-metal microcontroller, the microcontroller has to handle multiple tasks. What is your program framework at this time?

This is actually a classic interview question, and I have been asked this question in interviews before.

Answer 1: Polling system

The code structure is as follows:

Swipe left and right to view all codes>>>

int main(void)
{
 init_something();
 
 while(1)
 {
  do_something1();
        do_something2();
        do_something3();
 }
}

This structure is probably the code structure when we first learn microcontrollers. It can be used better when there is no external event drive.

If you only answer this kind of question, your impression score will probably be low and you will most likely be a failure.

Answer 2: Front and back-end systems

The code structure is as follows (the code comes from the "RT-Thread Kernel Implementation and Application Development Practice Guide"):

Swipe left and right to view all codes>>>

int flag1 = 0;
int flag2 = 0;
int flag3 = 0;

int main(void)
{
 /* Hardware related initialization */
 HardWareInit();

 /* Infinite loop */
 for (;;) {
   if (flag1) {
     /* Processing 1 */
     DoSomething1();
   }

   if (flag2) {
     /* Processing 2 */
     DoSomethingg2();
   }

   if (flag3) {
     /* Processing event 3 */
     DoSomethingg3();
   }
 }
}

void ISR1(void)
{
 /* Set flag */
 flag1 = 1;
 /* If the event processing time is very short, process it in the interrupt
 If the event processing time is long, return to the background processing */
 DoSomething1();
}

void ISR2(void)
{
 /* Set flag */
 flag2 = 2;

 /* If the event processing time is very short, process it in the interrupt
 If the event processing time is long, return to the background processing */
 DoSomething2();
}

void ISR3(void)
{
 /* Set flag */
 flag3 = 1;
 /* If the event processing time is very short, process it in the interrupt
 If the event processing time is long, return to the background processing */
 DoSomething3();
}

Here, the interrupt is called the foreground, and the while loop in main is called the background. Compared with the loop system, this method can relatively improve the real-time response capability of external events.

If you can answer this question, you will get more than half of the impression points, and we will ask you in detail.

Answer 3: Upgraded front-end and back-end system (software timer method)

In the past, when I was learning C language, I often heard people say: pointers are the soul of C language, if you don’t learn pointers, you haven’t learned C language.

Later, when I was learning about single-chip microcomputers, I heard someone say: Interrupts and timers are the soul of single-chip microcomputers. If you don’t master interrupts and timers, you haven’t learned single-chip microcomputers.

The big guys have said that, so let's do something with the timer. Timers are full of treasures. In this note, we will introduce the bare metal framework made by using timers (system tick timers or other timers). There is another way to say the software timer method: time slice polling method.

If you can answer this question, you are likely to pass the interview.

Let's take the STM32 microcontroller as an example to see how this method is used.

Standing on the shoulders of giants

Open source project——MultiTimer, project repository address:

https://github.com/0x1abin/MultiTimer

1. Introduction to MultiTimer

MultiTimer is a software timer expansion module that can infinitely expand the timer tasks you need, replacing the traditional flag judgment method, and managing the program's time trigger timing more elegantly and conveniently.

2、MultiTimer的demo

Swipe left and right to view all codes>>>

#include "multi_timer.h"

struct Timer timer1;
struct Timer timer2;

void timer1_callback()
{
    printf("timer1 timeout!rn");
}

void timer2_callback()
{
    printf("timer2 timeout!rn");
}

int main()
{
    timer_init(&timer1, timer1_callback, 1000, 1000); //1s loop
    timer_start(&timer1);
    
    timer_init(&timer2, timer2_callback, 50, 0); //50ms delay
    timer_start(&timer2);
    
    while(1) {
        
        timer_loop();
    }
}

void HAL_SYSTICK_Callback(void)
{
    timer_ticks(); //1ms ticks
}

3. Transplantation and analysis of MultiTimer

If you want to learn more about MultiTimer, you can read the project source code and the following article:

Issue 6 | MultiTimer, an infinitely scalable software timer

living comfortably without anybody's help

1. Code templates

Prepare a timer, which can be a system tick timer or a TIM timer, and use this timer to expand multiple software timers.

For example, our system has three tasks: LED flipping, temperature collection, and temperature display. At this time, we can use a hardware timer to expand three software timers and define the following macro definitions:

Swipe left and right to view all codes>>>

#define MAX_TIMER 3 // Maximum number of timers
EXT volatile unsigned long    g_Timer1[MAX_TIMER]; 
#define LedTimer g_Timer1[0] // LED flip timer
#define GetTemperatureTimer g_Timer1[1] // Temperature acquisition timer
#define SendToLcdTimer g_Timer1[2] // Temperature display timer

#define  TIMER1_SEC        (1)              // 秒
#define TIMER1_MIN (TIMER1_SEC*60) // 分


When the timer is initialized, the three software timers are also initialized:

Swipe left and right to view all codes>>>

/********************************************************************************************************
** Function: TIM1_Init, general timer 1 initialization
**------------------------------------------------------------------------------------------------------
** Parameters: arr: auto-reload value psc: clock pre-scaling number
** Note: Timer overflow time calculation method: Tout=((arr+1)*(psc+1))/Ft
** Returns: void 
********************************************************************************************************/
void TIM1_Init(uint16_t arr, uint16_t psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
 NVIC_InitTypeDef NVIC_InitStructure;
 
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); 
 
 /* Timer TIM1 initialization */
 TIM_TimeBaseStructure.TIM_Period = arr; 
 TIM_TimeBaseStructure.TIM_Prescaler =psc; 
 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; 
 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  
 TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
 TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); 
  TIM_ClearFlag(TIM1,TIM_FLAG_Update );
 
 /* Interrupt enable */
 TIM_ITConfig(TIM1,TIM_IT_Update, ENABLE ); 
 
 /* Interrupt priority NVIC setting */
    NVIC_InitStructure.NVIC_IRQChannel =  TIM1_UP_IRQn;
 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  
 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  
 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
 NVIC_Init(&NVIC_InitStructure);  
 TIM_Cmd(TIM1, ENABLE);  
    
 // Initialize the global timer
 for(int i = 0; i < MAX_TIMER; i++)
 {
  g_Timer1[i] = 0;   
 }
}


In the timer interrupt, the timing values ​​of these software timers are decremented:

Swipe left and right to view all codes>>>

/********************************************************************************************************
** Function: TIM1_IRQHandler, Timer 1 interrupt service routine
**------------------------------------------------------------------------------------------------------
** Parameters: None
** Return: None 
********************************************************************************************************/
void TIM1_UP_IRQHandler(void) //TIM1 interrupt
{
 uint8 i;
 
 if (TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET) // Check whether TIM1 update interrupt occurs
 {
  //-------------------------------------------------------------------------------
  // Various timers timing
  for (i = 0; i < MAX_TIMER; i++) // Timer time decreases     
   if( g_Timer1[i] ) g_Timer1[i]-- ;
  TIM_ClearITPendingBit(TIM1, TIM_IT_Update); //Clear TIMx update interrupt flag 
 }
}


We assign timing values ​​to these software timers in each scheduled task. When these timing values ​​decrease to 0, the task will be triggered to execute, for example:

Swipe left and right to view all codes>>>

void Task_Led(void)
{
 //----------------------------------------------------------------
 // Wait for the timing time
 if(LedTimer) return;
 LedTimer = 1 * TIMER1_SEC;
 //----------------------------------------------------------------
 //LED task body
 LedToggle();
}

void Task_GetTemperature(void)
{
 //----------------------------------------------------------------
 // Wait for the timing time
 if(LedTimer) return;
 LedTimer = 2 * TIMER1_SEC;
 //----------------------------------------------------------------
 // Temperature collection task body
 GetTemperature();
}

void Task_SendToLcd(void)
{
 //----------------------------------------------------------------
 // Wait for the timing time
 if(LedTimer) return;
 LedTimer = 2 * TIMER1_SEC;
 //----------------------------------------------------------------
 // Temperature display task body
 LcdDisplay();
}


In this way, the LED flipping task, temperature collection task, and temperature display task are triggered every 1, 2, and 4 seconds respectively.

The minimum timing unit configured here is 1 second. Of course, it should be configured according to actual needs (timer initialization). Timer initialization can be placed in the system unified initialization function:

Swipe left and right to view all codes>>>

/********************************************************************************************************
** Function: SysInit, system power-on initialization
**------------------------------------------------------------------------------------------------------
** Parameters: 
** illustrate: 
** return: 
********************************************************************************************************/
void SysInit(void)
{
 CpuInit(); //Configure system information function
 SysTickInit(); // System tick timer initialization function
 UsartInit(115200); //Serial port initialization function, baud rate 115200
 TIM1_Init(2000-1, 36000-1); // Timing period 1s
 LedInit(); // Led initialization
 TemperatureInit(); // Temperature sensor initialization
 LcdInit(); // LCD initialization
}


[1] [2]
Keywords:MCU Reference address:Engineers' Practice: How to Create a Bare Metal Program Framework for MCUs

Previous article:How to distinguish byte transfer and bit transfer in microcontroller instructions
Next article:MCU I/O control method (difference between UART interrupt and DMA interrupt)

Latest Microcontroller Articles
  • Download from the Internet--ARM Getting Started Notes
    A brief introduction: From today on, the ARM notebook of the rookie is open, and it can be regarded as a place to store these notes. Why publish it? Maybe you are interested in it. In fact, the reason for these notes is ...
  • Learn ARM development(22)
    Turning off and on interrupts Interrupts are an efficient dialogue mechanism, but sometimes you don't want to interrupt the program while it is running. For example, when you are printing something, the program suddenly interrupts and another ...
  • Learn ARM development(21)
    First, declare the task pointer, because it will be used later. Task pointer volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • Learn ARM development(20)
    With the previous Tick interrupt, the basic task switching conditions are ready. However, this "easterly" is also difficult to understand. Only through continuous practice can we understand it. ...
  • Learn ARM development(19)
    After many days of hard work, I finally got the interrupt working. But in order to allow RTOS to use timer interrupts, what kind of interrupts can be implemented in S3C44B0? There are two methods in S3C44B0. ...
  • Learn ARM development(14)
  • Learn ARM development(15)
  • Learn ARM development(16)
  • Learn ARM development(17)
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号