探索构建,编码和测试裸机嵌入式系统(约80MHz,约64kRAM)的许多引脚的配置的最佳方法。我认为这段代码(/层)的一个方面是一个微小的databasecor数据结构,而另一方面,我看到它是通过使用数据库(通过Set / Get)通过一薄层抽象来行使引脚(输出/输入)。
嵌入式裸机系统工具和技术似乎落后于其他计算领域。因此,我想阅读(如果有的话)其他人在该主题上的经验。
主题1:通过使用数据结构(/数据库)来管理(/配置)一个位置(/模块)中的所有引脚。 开机后,引脚根本不会经常更改“模式”(如果有的话)。对于我来说,拥有一个文件(/模块)是有意义的,在该文件中,所有64-100引脚(嵌入式ARM uC常见)都配置有各自的port_name,pin_number,pull_direction,speed,pin_mode(输入,输出或模拟),pin_function (UART,ADC,DAC,比较器等…..)。最后,pin_ID可以用作每个引脚的标识符,或者,如果可以的话,可以用作引脚名称。
主题2:一个非常小的数据库,适合嵌入式裸机应用程序。 通过数据库管理所有这些引脚。例如,所有这些引脚在运行期间的任何给定时刻也可以具有pin_value。我想从(/到)数据结构(/数据库)中写入和读取这些pin_value更改。 扩展以上关于引脚配置和pin_value存储的概念,我们将获得虚拟引脚。 虚拟引脚的行为类似于物理引脚。它将具有pin_ID,pin_mode和pin_value。例如,我们有一个uC,它查看信号的零交叉。过零检测只能从零变为一,然后再变为数字输入。我们可以说这具有一个ZC_ID的pin_ID,一个INPUT_MODE的pin_mode和一个值为CLASS_VIRTUAL_DIGITAL的pin_class新的引脚类型。
主题3:数据库接口-抽象的薄层。 我们应用程序的其他模块可以通过数据库访问物理或虚拟引脚值更改。 我们的应用程序应该能够通过对pin_ID的简单了解来读写物理或虚拟引脚。例如:
Get_Value(uint32_t引脚ID,uint32_t值)
Set_Value(uint32_t引脚ID,uint32_t值)
然后,基础代码确定此pin_ID是对gpio引脚还是虚拟引脚的简单写入/读取。 同样,在现代互联世界中,我需要读写外部设备或服务器。我想使用相同的数据库接口来执行此操作。即,服务器将具有更新值的消息发送到uC模块,在该模块中,数据库将使用新值进行更新。然后,我们的应用程序可以从数据库中读取新值。
谢谢
答案 0 :(得分:1)
在GPIO的特定情况下,最好的想法是尽可能减少抽象层的方式 。
您需要一个功能来设置端口或引脚,并可能需要一些时钟和引脚路由设置。但是,最好通过直接写入数据寄存器直接进行端口访问。为什么?因为如果您进行这样的HAL:
pin_t pin_allocate (port, pin);
void pin_set (pin_t* p);
其中pin_t
是某种抽象的,不透明的类型,那么您可能会遇到非常细微的重新输入错误,这些错误是由于应用程序程序员无法跟踪代码中发生的事情而引起的。
考虑这种情况:
// BAD DESIGN, don't do this
static pin_t* led;
statci pin_t* relay;
void main (void)
{
led = pin_allocate(PORTA, 0);
relay = pin_allocate(PORTA, 1);
...
pin_set(led);
}
...
void PORTA_ISR (void)
{
pin_set(relay);
}
此代码中有一个竞态条件错误,但抽象层将其完全隐藏了。
pin_set()
可能会存储端口寄存器的值,并准备进行按位OR。relay
变量的访问。但是该变量本身可能受到保护并且是线程安全的-问题出在HAL之下。我已经多次看到以上情况,这是所有类别中最难发现的错误之一。所以我的建议是不要在软件中过度设计。
请通过 PCB原理图跟踪引脚,这是规范性文件。任何编写代码 的人都必须先查看原理图,然后再决定使用某个引脚,而不是仅仅盯着自己的软件。在开始编写任何代码之前,请查找与其他硬件的潜在冲突。
此外,GPIO可能是实时关键的,抽象层意味着开销。
答案 1 :(得分:0)
主题1:通过使用数据结构来管理(/配置)一个位置(/模块)中的所有引脚
这应该是完全可行的;实际上,我(和我认为的其他程序员)确实是这样做的:在系统初始化时,所有引脚都从其默认状态(输入)配置为带上拉或输出的输入,或者与某些特殊的内部硬件(例如例如UART)。
主题2:适用于嵌入式裸机应用程序的小型数据库。通过数据库管理所有这些引脚。例如,所有这些引脚在运行期间的任何给定时刻也可以具有pin_value。我想从数据库中读取和修改这些pin_value值。扩展上述有关引脚配置和pin_value存储的概念,我们将获得虚拟引脚。
这实际上是很简单的,是可行的。有时我在ram中使用单个整数,其中每一位都反映引脚的状态或其他状态。中断处理程序更新这些位,然后前台进程查看它们。对于输出,它是相同的,相反的:前台写入这些位,而中断使用这些位来修改实际引脚。 因此,我最终拥有两个变量(input_statuses和output_statuses),例如,可以通过串行,蓝牙或以太网远程读写。
但是从这一点到数据库,相距甚远。通常,出于性能和低成本的考虑,嵌入式应用程序靠近硬件而没有太多的虚拟化。但是谁知道您的要求呢?也许您的预算很大,功能强大的处理器且对速度的需求很小。
答案 2 :(得分:-1)
Arduino使用了一个简单的系统,该系统可以在非常基本的水平上运行,尽管完全在运行时,但仍完全按照您对主题1的描述(并且不要求在单个位置配置引脚分配)。
https://github.com/arduino/ArduinoCore-samd/tree/master/variants
https://github.com/arduino/ArduinoCore-samd/blob/master/cores/arduino/wiring_digital.h
表g_APinDescription
作为数据库,pinMode()
和其他功能管理引脚分配。