以及如何使用它?
在上一篇文章中,我们创建了一个用于处理I / O端口的类,已选中。那么,下一步是什么?为什么要把这些全部塞进课堂?
让我们以一个简单的按钮轮询为例:
对于这种方案,在最简单的情况下,调查将如下所示:
int GetKey()
{
volatile uint32_t* addr = reinterpret_cast<uint32_t*>(GPIOA_IDR);
uint32_t ret_val = *addr;
return ret_val & 0x0F;
}
但是,如果更改连接到电路中按钮的端口,则必须更改轮询功能。在每个项目中都是如此。这并不总是很方便。我想编写,测试和使用一次。
让我们在先前创建的类下重写此函数:
int GetKey(Pin* p0, Pin* p1, Pin* p2, Pin* p3)
{
int ret_val = p0->Get() + (p1->Get() << 1) + (p2->Get() << 2) + (p3->Get() << 3);
return ret_val;
}
它保留在主程序中以初始化端口并将它们传递给函数:
...
using namespace STM32F1xx;
Pin key0('a', 0);
Pin key1('a', 1);
Pin key2('a', 2);
Pin key3('a', 3);
...
int main()
{
key0.ModeInput();
key1.ModeInput();
key2.ModeInput();
key3.ModeInput();
int key_code = GetKey(&key0, &key1, &key2, &key3);
...
return 0;
}
接口在哪里?
现在,让我们想象一下f10x系列控制器已经用完了,但是有一堆f030。就性能和引脚数而言,就足够了,您只需要更改GetKey函数的标题或使用... #ifdef。制作一个全局头文件,其中使用的控制器类型(类似于#define STM32F030)并堆积一堆定义。不,这不是为什么创建高级语言来使宏感到困惑的原因!
让我们走另一条路。让我们创建一个类,在其中列出使用端口所需的虚拟方法:
iPin.h
#pragma once
class iPin
{
public:
virtual void ModeInput() = 0;
virtual void ModeAnalogInput() = 0;
virtual void ModeInputPulled() = 0;
virtual void ModeOutput() = 0;
virtual void ModeOutputOpenDrain() = 0;
virtual void Set(bool st) = 0;
virtual bool Get() = 0;
virtual void Reverse() { Set(!Get());}
void On() { Set(true); }
void Off() { Set(false); }
};
(那些等于0的方法必须在派生类中定义!)
,我们将在Pin类中将其用作基类:
...
#include "iPin.h"
...
class Pin : public iPin
...
然后GetKey函数将略有变化:
int GetKey(iPin* p0, iPin* p1, iPin* p2, iPin* p3)
{
int ret_val = p0->Get() + (p1->Get() << 1) + (p2->Get() << 2) + (p3->Get() << 3);
return ret_val;
}
现在我们不在乎任何控制器!即使它是通过SPI或I2C工作的总线扩展器。在下一篇文章中,我们将考虑串行接口。
那么,下一步是什么?
接下来,您需要设计一个用于使用系统计时器的类。但这已经在下一个出版物中了。