当前位置:网站首页>100ask seven day IOT training camp learning notes - bare metal program framework design
100ask seven day IOT training camp learning notes - bare metal program framework design
2022-06-26 01:15:00 【_ kukukukiki__】
1. Preface
Signed up 100ask Organized a seven day IOT smart home training camp , Every day in the morning 2 An hour to talk about the basics , Afternoon 2 Hours of advanced lecture . After two days' study, I really feel more fulfilled . In the next few days, I will focus on the key points of each class 、 Record the difficulties and your understanding .
2. theory
2.1 Throw questions
In this lesson, Mr. Wei started the course with the program framework design as the starting point , Take embedded bare metal development as an example , Many beginners usually work in business layer code or even main Function , Directly call the interface used to describe or hardware ( Such as HAL library ):
void main(void)
{
GPIO_PinState key;
while (1)
{
key = HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_6);
if (key == GPIO_PIN_RESET)
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_5, GPIO_PIN_RESET);
else
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_5, GPIO_PIN_SET);
}
}
This will cause serious coupling between business layer code and board level code , Extend the following software functions 、 Hardware upgrade and code reuse will cause inconvenience , At the same time, it will also create obstacles for business layer developers who do not understand hardware .
To solve this problem , We layered the program structure , Separate business logic from hardware driver code :
// main.c
void main(void)
{
int key;
while (1)
{
key = read_key();
if (key == UP)
led_on();
else
led_off();
}
}
// key.c
int read_key(void)
{
GPIO_PinState key;
key = HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_6);
if (key == GPIO_PIN_RESET)
return 0;
else
return 1;
}
// led.c
void led_on(void)
{
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_5, GPIO_PIN_RESET);
}
void led_off(void)
{
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_5, GPIO_PIN_SET);
}
This solves the problem of coupling business logic code with hardware driver code , But there are still two unsolved problems :
One 、 Software compatibility problems caused by hardware version iteration
Two 、 Functional scalability issues
2.2 Introduce function pointer
Here we have to solve the first problem , Usually there are 3 Methods :
Macro switch
#define HARDWARE_VER 1 // key.c // Return value : 0 Indicates being pressed , 1 Indicates that it is loosened int read_key(void) { GPIO_PinState key; #if (HARDWARE_VER == 1) key = HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_6); #else key = HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_7); #endif if (key == GPIO_PIN_RESET) return 0; else return 1; }Macro switch if more than one , It will be a disaster to maintain .
stay EEPROM Save the hardware version number in , Call the hardware version difference interface according to the version number
// key.c // Return value : 0 Indicates being pressed , 1 Indicates that it is loosened int read_key(void) { GPIO_PinState key; int ver = read_hardware_ver(); if (ver == 1) key = HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_6); else (ver == 2) key = HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_7); if (key == GPIO_PIN_RESET) return 0; else return 1; }Similar to macro switches , More than one , It's hard to maintain .
A function pointer
// key.c int (*read_key)(void); // Return value : 0 Indicates being pressed , 1 Indicates that it is loosened int read_key_ver1(void) { GPIO_PinState key; key = HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_6); if (key == GPIO_PIN_RESET) return 0; else return 1; } // Return value : 0 Indicates being pressed , 1 Indicates that it is loosened int read_key_ver2(void) { GPIO_PinState key; key = HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_6); if (key == GPIO_PIN_RESET) return 0; else return 1; } void key_init() { int ver = read_hardware_ver(); if (ver == 1) read_key = read_key_ver1; else read_key = read_key_ver2; } // main.c void main(void) { int key; key_init(); while (1) { key = read_key(); if (key == UP) led_on(); else led_off(); } }
I think this method should belong to the 2 An upgraded version of the method , It is also necessary to write the hardware version number to EEPROM in , Make judgments in the software , But the difference lies in the introduction of function pointers , You only need to judge once according to the version number during power on initialization , Assign the interface corresponding to the version to the pointer , There is no need to make a lot of judgment calls in subsequent code .
So the first problem is solved , Let's look at the second question , How to solve the problem of software scalability ?
There is a design principle in the design pattern :OCP, Opening and closing principle , Good design needs to be open to extensions , Turn off for changes . In other words, only new code is added during function expansion , Do not modify the existing code .
Back to the question , If we need to increase the number of buttons , According to our previous thinking, it should be :
// key.c
// Return value : 0 Indicates being pressed , 1 Indicates that it is loosened
int read_key1(void)
{
GPIO_PinState key;
key = HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_6);
if (key == GPIO_PIN_RESET)
return 0;
else
return 1;
}
// Return value : 0 Indicates being pressed , 1 Indicates that it is loosened
int read_key2(void)
{
GPIO_PinState key;
key = HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_7);
if (key == GPIO_PIN_RESET)
return 1;
else
return 0;
}
or
// key.c
// Return value : 0 Indicates being pressed , 1 Indicates that it is loosened
int read_key(int which)
{
GPIO_PinState key;
switch (which)
{
case 0:
key = HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_6);
if (key == GPIO_PIN_RESET)
return 0;
else
return 1;
break;
case 1:
key = HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_7);
if (key == GPIO_PIN_RESET)
return 1;
else
return 0;
break;
}
}
Both of these methods can alleviate the problem to some extent , But to cure the symptoms is not to cure the root . The former method will increase with the number of keys , More and more confusion at the call , Difficult to maintain . The latter method , As long as there is a button to add, it will modify our read_key function , In violation of the OCP principle .
2.3 Introduce structure
In order to solve problems with new ideas , Here new knowledge points are introduced : Structure .

Let's start by layering the program ,main Functions belong to the application layer or business logic layer ,key_manager It belongs to the middle layer , The bottom layer belongs to the hardware driver layer , The management of keys is realized through the middle layer , At the same time, the business logic layer and the driver layer are decoupled .
2.3.1 key_system
// key_manager.h
typedef struct key {
char *name;
void (*init)(struct key *k);
int (*read)(void);
}key, *p_key;
// Initialization of all keys
void key_init(void);
// According to the key name Get the key
key *get_key(char *name);
// key_manager.c
int key_cnt = 0;
key *keys[32];
void register_key(key *k)
{
keys[key_cnt] = k;
key_cnt++;
}
void key_init(void)
{
k1_init();
k2_init();
}
key *get_key(char *name)
{
int i = 0;
for (i = 0; i < key_cnt; i++)
if (strcmp(name, keys[i]->name) == 0)
return keys[i];
return 0;
}
// key1.c
// Return value : 0 Indicates being pressed , 1 Indicates that it is loosened
static int read_key1(void)
{
GPIO_PinState key_status;
key_status = HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_6);
if (key_status == GPIO_PIN_RESET)
return 0;
else
return 1;
}
static key k1 = {
"k1", 0, read_key1};
void k1_init(void)
{
register_key(&k1);
}
// key2.c
// Return value : 0 Indicates being pressed , 1 Indicates that it is loosened
static int read_key2(void)
{
GPIO_PinState key_status;
key_status = HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_7);
if (key_status == GPIO_PIN_RESET)
return 1;
else
return 0;
}
static key k2 = {
"k2", NULL, read_key2};
void k2_init(void)
{
register_key(&k2);
}
// main.c
void main(void)
{
key *k;
key_init();
/* Use a key */
k = get_key("k1");
if (k == NULL)
return;
while (1)
{
if (k->read(k) == 0)
led_on();
else
led_off();
}
}
2.3.2 key_system_read_multi_key
There are still some problems with the current code , There is no decoupling between the business layer and the driver layer , stay main There are also specific keys in the function read Function call and state judgment . meanwhile , As the business layer, the middle layer is expected to read the status of all keys at the same time .
Then optimize the implementation of the middle tier :
// key_manager.h
typedef struct key {
char *name;
unsigned char id;
void (*init)(struct key *k);
int (*read)(void);
}key, *p_key;
#define KEY_UP 0xA
#define KEY_DOWN 0xB
// Initialization of all keys
void key_init(void);
// Read the status of all keys
int read_key(void);
// key_manager.c
int key_cnt = 0;
key *keys[32];
void register_key(key *k)
{
keys[key_cnt] = k;
key_cnt++;
}
void key_init(void)
{
k1_init();
k2_init();
}
int read_key(void)
{
int val;
for (int i = 0; i < key_cnt; i++)
{
val = keys[i]->read();
if (val == -1)
continue;
else
return val;
}
return -1;
}
// key1.c
// Return value : 0 Indicates being pressed , 1 Indicates that it is loosened
#define KEY1_ID 1
static int read_key1(void)
{
static GPIO_PinState pre_key_status;
GPIO_PinState key_status;
key_status = HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_6);
if (key_status == pre_key_status)
return -1;
pre_key_status = key_status;
if (key_status == GPIO_PIN_RESET)
return KEY_DOWN | (KEY1_ID << 8);
else
return KEY_UP | (KEY1_ID << 8);
}
static key k1 = {
"k1", KEY1_ID, NULL, read_key1};
void k1_init(void)
{
register_key(&k1);
}
// key2.c
// Return value : 0 Indicates being pressed , 1 Indicates that it is loosened
#define KEY2_ID 2
static int read_key1(void)
{
static GPIO_PinState pre_key_status;
GPIO_PinState key_status;
key_status = HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_7);
if (key_status == pre_key_status)
return -1;
pre_key_status = key_status;
if (key_status == GPIO_PIN_RESET)
return KEY_UP | (KEY2_ID << 8);
else
return KEY_DOWN | (KEY2_ID << 8);
}
static key k2 = {
"k2", KEY2_ID, NULL, read_key2};
void k2_init(void)
{
register_key(&k2);
}
// main.c
void main(void)
{
int val;
key_init();
while (1)
{
val = read_key();
if (val == -1)
{
/* There are no buttons */
}
else
{
key_status = val & 0xFF;
key_id = (val>>8) & 0xFF:
switch (key_status)
{
case KEY_UP:
/* key_id Release */
break;
case KEY_DOWN:
/* key_id Press down */
break;
default:
break;
}
}
}
}
2.3.3 key_system_read_usr_irq
however , There are still some problems with this code , For example, the key detection now belongs to polling , If you can change key detection to interrupt mode , In the use of RTOS There is no need to poll regularly , You can wait for an interrupt to trigger .
here , You can introduce a fifo, Interrupt events as producers of data , The application layer acts as a consumer of data , Further decoupling .
here fifo The realization is from github Casually found on the .
// key_manager.h
typedef struct key {
char *name;
unsigned char id;
void (*init)(struct key *k);
int (*read)(void);
}key, *p_key;
#define KEY_UP 0xA
#define KEY_DOWN 0xB
// Initialization of all keys
void key_init(void);
// Read key status
int read_key(void)
// towards fifo Write a key status
void put_buffer(int val);
// from fifo Read a key state
int read_buffer(void);
// key_manager.c
int key_cnt = 0;
key *keys[32];
// Define a Fifo Buffer
static RingBufferPointer fifo;
void put_buffer(int val)
{
ringBufferAdd(fifo, val);
}
int read_buffer()
{
int val = -1;
if (isRingBufferNotEmpty(fifo))
val = ringBufferGet(fifo);
return val;
}
void register_key(key *k)
{
keys[key_cnt] = k;
key_cnt++;
}
void key_init(void)
{
fifo = getRingBufferInstance(100);
k1_init();
k2_init();
}
int read_key(void)
{
return read_buffer();
}
// key0.c
#define KEY0_ID 0
static key k0 = {
"k0", KEY0_ID, NULL, NULL};
void k0_init(void)
{
register_key(&k0);
}
void key0_irq(void)
{
int val;
GPIO_PinState key_status;
key_status = HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin);
if (key_status == GPIO_PIN_RESET)
val = KEY_DOWN | (KEY0_ID << 8);
else
val = KEY_UP | (KEY0_ID << 8);
put_buffer(val);
}
// key1.c
#define KEY1_ID 1
static key k1 = {
"k1", KEY1_ID, NULL, read_key1};
void k1_init(void)
{
register_key(&k1);
}
void key1_irq(void)
{
int val;
GPIO_PinState key_status;
key_status = HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_6);
if (key_status == GPIO_PIN_RESET)
val = KEY_DOWN | (KEY1_ID << 8);
else
val = KEY_UP | (KEY1_ID << 8);
put_buffer(val);
}
// key2.c
#define KEY2_ID 2
static key k2 = {
"k2", KEY2_ID, NULL, read_key2};
void k2_init(void)
{
register_key(&k2);
}
void key2_irq(void)
{
int val;
GPIO_PinState key_status;
key_status = HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_7);
if (key_status == GPIO_PIN_RESET)
val = KEY_DOWN | (KEY2_ID << 8);
else
val = KEY_UP | (KEY2_ID << 8);
put_buffer(val);
}
// stm32f7xx_it.c
void EXTI2_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(KEY1_Pin);
}
void EXTI3_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(KEY0_Pin);
}
void EXTI15_10_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(KEY2_Pin);
}
// main.c
void main(void)
{
int val;
char key_status;
char key_id;
key_init();
// If it is rtos The polling method is not applicable
while (1)
{
val = read_key();
if (val != -1)
{
key_status = val & 0xFF;
key_id = (val >> 8) & 0xFF;
switch (key_status)
{
case KEY_DOWN:
if (key_id == 0)
{
HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_RESET);
}
else if (key_id == 1)
{
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);
}
else if (key_id == 2)
{
HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);
}
break;
case KEY_UP:
break;
}
}
}
}
void HAL_GPIO_EXTI_Callback(uint16_t pin)
{
HAL_Delay(50);
switch (pin)
{
case KEY0_Pin:
key0_irq();
break;
case KEY1_Pin:
key1_irq();
break;
case KEY2_Pin:
key2_irq();
break;
default:
break;
}
}
3. Experimental process
According to the idea above , Practice on the development board .
3.1 establish cubemx engineering

After checking the schematic diagram of the development board, we know , The resources are distributed as follows :
- The development board has user buttons 3 individual , Namely KEY0(PH3)、KEY1(PH2)、KEY2(PC13)
- The development board has LED2 individual , Namely LED0(PB1)、LED1(PB0)

For this 5 individual GPIO To configure , And then generate the code .
3.2 Project structure

3.3 Experimental results
Press down KEY0,LED0 Light up , Press down KEY1,LED1 Light up , Press down KEY2,LED0、LED1 It goes out at the same time . The experiment is finished .
4. summary
Actually, according to Mr. Wei's course , take led A subsystem has also been established , Manage through the middle tier led, To reduce code coupling , Enhance code scalability 、 reusability . Because of ideas and key The subsystems are basically the same , There is no further implementation .
Through this lesson, I learned the idea of hierarchical design of software structure , Consider future functional extensions from the beginning of the design 、 The robustness of the code . By sacrificing a little bit of operational efficiency , To improve the maintainability of the whole project .
边栏推荐
- FPGA notes -- implementation of FPGA floating point operation
- 马斯克 VS 乔布斯,谁是21世纪最伟大的创业家
- C#使用MySql进行操作
- Cartoon shader
- Is it safe for flush software to buy stocks for trading? How to open an account to buy shares
- 新库上线 | CnOpenDataA股上市公司IPO申报发行文本数据
- Multiple interface calls, using promise all、Promise. Race and promise any
- QT cmake pure C code calls the system console to input scanf and Chinese output garbled code
- C thread pool control semaphore
- [system architecture] - what are MDA architecture, ADL and DSSA
猜你喜欢

Summary of push-pull output and open drain output of STM32 and failure of analog IIC driving mlx90615

Electronic training.

jarvisoj_ level2_ x64

Typescript for Web Learning

Msp430f5529lp official board (red) can not debug the problem

The kth largest element in the array

新库上线 | CnOpenData中国新房信息数据

LabVIEW开发监控聚变实验脉冲电源

下载安装Flume

JS reverse case: cracking login password
随机推荐
Modelsim simulation FFT core cannot be simulated solution (qsys)
Preordered clue binary tree
ETCD数据库源码分析——集群通信初始化
C#使用MySql进行操作
从查询数据库性能优化谈到redis缓存-谈一谈缓存的穿透、雪崩、击穿
数字电路——加法器
Qt Cmake 纯C 代码调用系统控制台输入scanf 及 中文输出乱码
Freertos+stm32l+esp8266+mqtt protocol transmits temperature and humidity data to Tencent cloud IOT platform
Flex & bison start
Redis之Strings命令
Daily question: the difference between threads and processes
mysql错误代码2003的解决办法
Typescript for Web Learning
经典面试题之老鼠试药与汉明码
Is it safe for flush software to buy stocks for trading? How to open an account to buy shares
Download and install flume
New library launched | cnopendata China new house information data
ASP.NET cache缓存的用法
sqlserver 区分字符串中字母大小写
Preorder and middle order traversal of forest