STM32开发时,例程中的文件层级设置常常让人感到困扰。有时候,应用层的文件竟然混杂着硬件层的文件,看上去十分杂乱。这就像整理房间,东西摆放不当,整个空间就显得乱糟糟的,影响了整体的美观。这确实让开发者感到挺烦恼的。
STM32例程现状
网络下载的资源或开发板自带的程序中,经常遇到应用层混用了本该属于硬件层的.h文件。这种现象很常见。就拿我之前开发的一个STM32小项目来说,看到那些层次混乱的文件,真是让人头疼。这不但让代码的结构显得杂乱无章,还使得后续的维护和扩展变得十分困难。尤其是对于初学者,这种混乱的分层结构会让他们在理解代码逻辑时遇到不小的挑战。
在实际的项目开发过程中,若出现层次混乱,可能会使开发周期变长。比如,对于急需上线的项目,开发者若需花费更多时间去理清代码之间的联系,这无疑是不利的。
Linux系统的启示
熟悉Linux的人都知道,Linux系统无法直接对硬件进行操作。观察其源代码,可以发现其中设有驱动程序这一层。以某个Linux服务器的开发为例,我们注意到其代码明确划分了各个层级。这种层级的划分使得整体结构一目了然。Linux对硬件层的隔离措施有效地保障了系统的稳定性。一旦出现故障,我们能够迅速判断是驱动层还是应用层出了问题。
这种分层设计思路就好比将不同部门各自设立办公区域,各自负责各自的工作。在STM32的开发过程中,我们可以采纳这种设计思路,将软件和硬件部分分开管理,这样做不仅能提高开发速度,而且后续的维护工作也会变得更为便捷。
实现原理剖析
在理论层面,将硬件操作接口置于驱动链表中,比如open、read、write等操作在驱动层完成。以智能家居控制系统为例,其中各类硬件设备的操作接口可以纳入驱动链表。然而,此方法存在不足,查找驱动时需遍历整个链表,导致代码执行时间延长。这就像在一家大型公司中寻找文件,若缺乏有效索引,便需逐个文件夹搜索,耗费大量时间。
此时,开发者需考量利弊得失。若代码对实时性的需求不高,软硬分层可通过此法顺利实现。然而,对于对实时性要求极高的项目,可能需对现有方法进行优化调整。
/*
驱动注册
*/
int cola_device_register(cola_device_t *dev);
/*
驱动查找
*/
cola_device_t *cola_device_find(const char *name);
/*
驱动读
*/
int cola_device_read(cola_device_t *dev, int pos, void *buffer, int size);
/*
驱动写
*/
int cola_device_write(cola_device_t *dev, int pos, const void *buffer, int size);
/*
驱动控制
*/
int cola_device_ctrl(cola_device_t *dev, int cmd, void *arg);;
代码实现要点
编写代码时,通常遵循国际规范,首先编写头文件。在此例中,我们使用了单向链表来简化操作,若感兴趣,不妨探究双向链表。确保头文件接口定义准确至关重要。例如,本次仅实现部分接口,其他部分可自行深入研究。以一个简单的传感器数据采集项目为例,在代码编写过程中,采用这种分层结构,应用层仅需调用相应接口即可实现功能。
#ifndef _COLA_DEVICE_H_
#define _COLA_DEVICE_H_
enum LED_state
{
LED_OFF,
LED_ON,
LED_TOGGLE,
};
typedef struct cola_device cola_device_t;
struct cola_device_ops
{
int (*init) (cola_device_t *dev);
int (*open) (cola_device_t *dev, int oflag);
int (*close) (cola_device_t *dev);
int (*read) (cola_device_t *dev, int pos, void *buffer, int size);
int (*write) (cola_device_t *dev, int pos, const void *buffer, int size);
int (*control)(cola_device_t *dev, int cmd, void *args);
};
struct cola_device
{
const char * name;
struct cola_device_ops *dops;
struct cola_device *next;
};
/*
驱动注册
*/
int cola_device_register(cola_device_t *dev);
/*
驱动查找
*/
cola_device_t *cola_device_find(const char *name);
/*
驱动读
*/
int cola_device_read(cola_device_t *dev, int pos, void *buffer, int size);
/*
驱动写
*/
int cola_device_write(cola_device_t *dev, int pos, const void *buffer, int size);
/*
驱动控制
*/
int cola_device_ctrl(cola_device_t *dev, int cmd, void *arg);
#endif
编写头文件.h和源文件.c时,需依据功能需求做好规划。这好比建造房屋前先确定好结构框架,如此编写出的代码运行更流畅,也便于其他开发者阅读与维护。
硬件注册方式示例
以LED为例,存在一个初始化接口,名为void(void),这是在初始化过程中必须调用的。在处理实际硬件接入的项目中,例如LED显示屏的控制项目,这种硬件注册方法能保证在应用层无需调用led.h头文件,从而实现软件与硬件的分离。这就像在组装一台复杂机器时,每个部件都有其特定的安装步骤,遵循这些步骤可以确保设备能顺畅运作。
#include "cola_device.h"
#include
#include
struct cola_device *device_list = NULL;
/*
查找任务是否存在
*/
static bool cola_device_is_exists( cola_device_t *dev )
{
cola_device_t* cur = device_list;
while( cur != NULL )
{
if( strcmp(cur->name,dev->name)==0)
{
return true;
}
cur = cur->next;
}
return false;
}
static int device_list_inster(cola_device_t *dev)
{
cola_device_t *cur = device_list;
if(NULL == device_list)
{
device_list = dev;
dev->next = NULL;
}
else
{
while(NULL != cur->next)
{
cur = cur->next;
}
cur->next = dev;
dev->next = NULL;
}
return 1;
}
/*
驱动注册
*/
int cola_device_register(cola_device_t *dev)
{
if((NULL == dev) || (cola_device_is_exists(dev)))
{
return 0;
}
if((NULL == dev->name) || (NULL == dev->dops))
{
return 0;
}
return device_list_inster(dev);
}
/*
驱动查找
*/
cola_device_t *cola_device_find(const char *name)
{
cola_device_t* cur = device_list;
while( cur != NULL )
{
if( strcmp(cur->name,name)==0)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
/*
驱动读
*/
int cola_device_read(cola_device_t *dev, int pos, void *buffer, int size)
{
if(dev)
{
if(dev->dops->read)
{
return dev->dops->read(dev, pos, buffer, size);
}
}
return 0;
}
/*
驱动写
*/
int cola_device_write(cola_device_t *dev, int pos, const void *buffer, int size)
{
if(dev)
{
if(dev->dops->write)
{
return dev->dops->write(dev, pos, buffer, size);
}
}
return 0;
}
/*
驱动控制
*/
int cola_device_ctrl(cola_device_t *dev, int cmd, void *arg)
{
if(dev)
{
if(dev->dops->control)
{
return dev->dops->control(dev, cmd, arg);
}
}
return 0;
}
这种硬件注册过程既简便又高效,它有助于使代码结构更加分明,同时降低了不必要的相关性。
代码下载与总结
#include "stm32f0xx.h"
#include "led.h"
#include "cola_device.h"
#define PORT_GREEN_LED GPIOC
#define PIN_GREENLED GPIO_Pin_13
/* LED亮、灭、变化 */
#define LED_GREEN_OFF (PORT_GREEN_LED->BSRR = PIN_GREENLED)
#define LED_GREEN_ON (PORT_GREEN_LED->BRR = PIN_GREENLED)
#define LED_GREEN_TOGGLE (PORT_GREEN_LED->ODR ^= PIN_GREENLED)
static cola_device_t led_dev;
static void led_gpio_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);
GPIO_InitStructure.GPIO_Pin = PIN_GREENLED;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(PORT_GREEN_LED, &GPIO_InitStructure);
LED_GREEN_OFF;
}
static int led_ctrl(cola_device_t *dev, int cmd, void *args)
{
if(LED_TOGGLE == cmd)
{
LED_GREEN_TOGGLE;
}
else
{
}
return 1;
}
static struct cola_device_ops ops =
{
.control = led_ctrl,
};
void led_register(void)
{
led_gpio_init();
led_dev.dops = &ops;
led_dev.name = "led";
cola_device_register(&led_dev);
}
这里并未提供代码下载的具体链接,不过我们必须认识到软硬件分层的至关重要。这种分层能提高开发速度,并使代码更易维护与理解。我想问问大家,在使用STM32进行开发时,是否遇到过因软硬件分层不清而引发的大麻烦?欢迎点赞和转发这篇文章,并在评论区参与讨论。