Simplify the process of program development in single chip microcomputer

Time:2020-12-1

First of all, as a new comer, I don’t have much experience in SCM development, so there may be some errors in writing some related documents. I hope you can include them more! I also hope that you will give me your advice and advice!

Good memory is not as good as bad writing. The reason why I choose to open a blog is that I want to record some problems and doubts I encounter in the process of work and study, and actively locate the source of the problem and seek solutions. Maybe I can quickly solve the same problem when I encounter it. At the same time, you can also learn a lot of engineers’ long-term accumulated experience on the blog, and share some of their own experience, which is very helpful for personal self-improvement!

OK, now I’m going to the topic. When I first came into contact with SCM, I saw that other people’s programs would use many macro definitions and structures. At that time, I always thought that such a practice was cumbersome and flowery. Later, when I started to work, I saw the code frameworks of some mass-produced products, and I realized how ignorant I was at that time (of course, the code authors who had just started to contact were of average level. Although they used a lot of macro definitions and structures, they were not standardized and could not understand their names. On the contrary, I was dazzled and confused). A good code framework pays more attention to details. For example, the naming is very standard, which makes people clear at a glance, and the typesetting is also very hard. The readability is naturally strong, and the portability of the code is also enhanced. From that time on, I began to pay attention to the readability of the code and the application of some methods. Although I can’t do it very well now, I have made some progress compared with the initial level.

In recent work and study, we have been exposed to stm32ubemx for many times. This is a tool that STM32 is currently promoting. It can generate initialization code for users, which greatly facilitates users and improves development efficiency. The generated code uses Hal library, which is very similar to the standard library. But now the standard library is not updated, Hal library must become the mainstream.

From a simple start, today we will start with lighting and pressing buttons (involving timers and external interrupts). The following is the configuration of cubemx:

1. Here, the high-speed RCC clock is used:

2. Timers: tim3 is selected randomly here, and other timers can be selected, such as tim6 and tim7

Because tim3 is mounted on apb1, the maximum frequency of apb1 timer clock I use is 84 (MHz), and what I need to do is to enter an interrupt once every 1ms. Therefore, other configurations are shown in the figure:

Interrupt memory is configured to enable:

3. Key configuration (key1: PA0, key2: Pe2, Key3: pe3, key4: PE4): all are configured as external interrupts

   

Because key1 hardware is designed to input high level after pressing, key2 ~ key4 is to input low level after pressing, so PA0 is configured as rising edge trigger, Pe2 ~ 4 is configured as falling edge trigger

 

 

Interrupt memory enable:

4. The configuration of LED lamp is similar to that of key configuration, but the mode is configured as output. In terms of hardware design, my lights are all low-level lights, so I configure them as high-level during initialization and low-level when they need to be lit

 

 

5. Clock tree configuration:

This basically completes the configuration. Click generate code and open the project to add code.

1. First of all, in order to facilitate the control of LED lights, I made several macro definitions of LED:

#define led1 0x01

#define led2 0x02

#define led3 0x03

#define led4 0x04

#define LED_ON(LEDx) { \

if(LEDx == led1) HAL_GPIO_WritePin(GPIOF, LED1_Pin, GPIO_PIN_RESET); \

else if(LEDx == led2) HAL_GPIO_WritePin(GPIOF, LED2_Pin, GPIO_PIN_RESET); \

else if(LEDx == led3) HAL_GPIO_WritePin(GPIOF, LED3_Pin, GPIO_PIN_RESET); \

else if(LEDx == led4) HAL_GPIO_WritePin(LED4_GPIO_Port, LED4_Pin, GPIO_PIN_RESET); \

}

#define LED_OFF(LEDx) { \

if(LEDx == led1) HAL_GPIO_WritePin(GPIOF, LED1_Pin, GPIO_PIN_SET); \

else if(LEDx == led2) HAL_GPIO_WritePin(GPIOF, LED2_Pin, GPIO_PIN_SET); \

else if(LEDx == led3) HAL_GPIO_WritePin(GPIOF, LED3_Pin, GPIO_PIN_SET); \

else if(LEDx == led4) HAL_GPIO_WritePin(LED4_GPIO_Port, LED4_Pin, GPIO_PIN_SET); \

}

#define LED_Toggle(LEDx) { \

if(LEDx == led1) HAL_GPIO_TogglePin(GPIOF, LED1_Pin); \

else if(LEDx == led2) HAL_GPIO_TogglePin(GPIOF, LED2_Pin); \

else if(LEDx == led3) HAL_GPIO_TogglePin(GPIOF, LED3_Pin); \

else if(LEDx == led4) HAL_GPIO_TogglePin(LED4_GPIO_Port,LED4_Pin); \

}

2. The next step is key detection. I also made a macro definition and a structure for key

typedef struct KEY

{

  uint8_ T keystatus; / * when the key is pressed, the first external interrupt is triggered, which will cause this status position 1*/

  uint32_ T holdtime; / * keystatus is set to 1 to start timing, that is, the duration between pressing the key and releasing the key*/

  uint8_ T ispress; / * is the key pressed, 1 pressed, 0 not pressed*/

  uint8_ T pressmode; / * pressed mode, 0 not pressed, 1 short press, 2 long press*/

  uint32_ T loosentime; / * after releasing the key, the next judgment can be made only after 10 ms of timing, so as to avoid misjudgment caused by jitter after releasing the key*/

  uint8_ T isloosen; / * whether the key has been released after pressing the key, 0 is not released, 1 is released*/

}Key;

extern Key key[5];

#define key1 0x01

#define key2 0x02

#define key3 0x03

#define key4 0x04

Another function is written to detect the level of the key

/*********************************************

*Function function: return the status value of key pin level

*Function parameter: uint8_ T keyx — key

*Function return value: 1 — press, 0 — release

*Remarks: none

**********************************************/

uint8_t Key_Status(uint8_t KEYx)

{

  uint8_t ret;

  if(KEYx == key1)

  {

    if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin) == GPIO_PIN_SET)

    {

      ret = 1;

    }

    else

    {

      ret = 0;

    }

  }

  else if(KEYx == key2)

  {

    if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin) == GPIO_PIN_RESET)

    {

      ret = 1;

    }

    else

    {

      ret = 0;

    }

  }

  else if(KEYx == key3)

  {

    if(HAL_GPIO_ReadPin(KEY3_GPIO_Port,KEY3_Pin) == GPIO_PIN_RESET)

    {

      ret = 1;

    }

    else

    {

      ret = 0;

    }

  }

  else if(KEYx == key4)

  {

    if(HAL_GPIO_ReadPin(KEY4_GPIO_Port,KEY4_Pin) == GPIO_PIN_RESET)

    {

      ret = 1;

    }

    else

    {

      ret = 0;

    }

  }

  return ret;

}

Use external interrupt to judge whether there is level change

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)

{

  if((GPIO_Pin == KEY1_Pin) && (!key[1].LoosenTime) && (!key[1].HoldTime))

  {

    key[1].KeyStatus = 1;

  }

  else if((GPIO_Pin == KEY2_Pin) && (!key[2].LoosenTime) && (!key[2].HoldTime))

  {

    key[2].KeyStatus = 1;

  }

  else if((GPIO_Pin == KEY3_Pin) && (!key[3].LoosenTime) && (!key[3].HoldTime))

  {

    key[3].KeyStatus = 1;

  }

  else if((GPIO_Pin == KEY4_Pin) && (!key[4].LoosenTime) && (!key[4].HoldTime))

  {

    key[4].KeyStatus = 1;

  }

}

Two variables, key [x]. Loosentime and key [x]. Holdtime, are added as judgment conditions. Only when these two variables are 0, can the corresponding key [x]. Keystatus be evaluated To judge, because there will be about 10ms of jitter when the button is pressed and released. If these two variables are not used as the judgment condition, the judgment may be repeated. However, after testing, whether the two variables are added or not has little effect. Here is just to make the logic more rigorous. This callback function will be triggered when the key is pressed, and then the timer will work.

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)

{

  uint8_t index;

For (index = 1; index < 5; index + +) / / 1ms, enter an interrupt and poll the four keys

  {

If (key [index]. Keystatus = = 1) / / key [index]. Keystatus is equal to 1, indicating that there is a sudden change in the level of a key pin

    {

Key [index]. Holdtime + +; / / start timing, key [index]. Holdtime increases by 1 every 1ms

If (key [index]. Holdtime > = 10) / / if key [index]. Holdtime is greater than or equal to 10, this period of time is the time of key jitter

      {

        if(Key_ Status (index) = = 1) / / judge whether the level of the key pin is in the pressed state. If so, set key [index]. Ispress to 1

        {

          key[index].IsPress = 1;

        }

      }

    }



If (key [index]. Ispress = = 1) / / key [index]. Ispress is 1, it means that you can determine whether the key is pressed. The next step is to judge whether the key is pressed for a long time or a short time

    {

      if(Key_ Status (index) = = 0) / / press the key and release it again. At this time, judge how long the process lasts from press to release, so as to judge whether it is long press or short press

      {

If (key [index]. Holdtime < 1500) / / if the duration is less than 1500ms, press the key [index]. Pressmode to 1, key [index]. Isloosen to 1, and other parameters to zero

        {

          key[index].PressMode = 1;

          key[index].HoldTime = 0;

          key[index].KeyStatus = 0;

          key[index].IsPress = 0;

          key[index].IsLoosen = 1;

          key[index].LoosenTime = 0;

        }

        else

        {

Key [index]. Pressmode = 2; / / if the duration is greater than or equal to 1500ms, press and hold, key [index]. Pressmode is set to 2, key [index]. Isloosen is set to 1, and other parameters are cleared

          key[index].HoldTime = 0;

          key[index].KeyStatus = 0;

          key[index].IsPress = 0;

          key[index].IsLoosen = 1;

          key[index].LoosenTime = 0;

        }

      }

    }



If (key [index]. Isloosen = = 1) / / key [index]. Isloosen is 1, it indicates that the key has gone through the process from press to release, and there will be 10ms shaking after loosening, and the key is not detected

    {

      key[index].LoosenTime++;

      if(key[index].LoosenTime >= 10)

      {

        key[index].IsLoosen = 0;

        key[index].LoosenTime = 0;

      }

    }



  }

}

By the way, although the timer has been configured as interrupt enable when cubemx is configured, it needs to add a statement in the project to enable interrupt:

HAL_TIM_Base_Start_IT(&htim3);

This is also what I found after debugging the code. Originally, I thought that as long as the cube MX is configured as interrupt enable, the interrupt can be turned on.

Then, as long as we judge the value of key [x]. Pressmode, we can know the status of the key at a certain time, and then we can make corresponding LED control (or other operations, depending on the demand). After the operation, remember to clear the key [x]. Pressmode, otherwise, it will always be judged as the last key state.

In fact, in addition to the two modes of long press and short press, I have also tried to add double-click logic. The test effect is not particularly good, and it is easy to judge errors. It may be that some logic has not been handled well, and due to time, I did not continue to study. But I think the MCU buttons actually do not need to be very complex, can achieve long press short press is generally able to meet most of the needs, too complex but appears cumbersome.

Maybe this code is not well written, but I think there are some desirable places, such as the application of structure array. There will be some attributes or flag bits in the process of key implementation. It will become very clear when we write them into the same structure. In fact, multiple keys have the same properties. If you make them into an array, you can greatly simplify the code by polling

Recommended Today

SDS of redis data structure

SDS(simple dynamic string), simple dynamic string. S and it’s called hacking string. Where hack is stored is the length of the string and the remaining space in SDS. The implementation of SDS insds.cIn the middle. C language string uses a character array of length N + 1 to represent the string of length N, and […]