And how do you use it?
In the previous article, we created a class for working with I / O ports, checked. So, what is next? Why stuff this all into class?
Let's take a simple button poll as an example:
For this scheme, in the simplest case, the survey will look like this:
int GetKey()
{
volatile uint32_t* addr = reinterpret_cast<uint32_t*>(GPIOA_IDR);
uint32_t ret_val = *addr;
return ret_val & 0x0F;
}
But, if you change the ports connected to the buttons in the circuit, you will have to change the polling function. And so in every project. This is not always convenient. I would like to write, test and use once.
Let's rewrite this function under the previously created class:
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;
}
It remains in the main program to initialize the ports and pass them to the function:
...
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;
}
Where are the interfaces?
And now let's imagine that the f10x series controllers have run out, but there are a bunch of f030s. In terms of performance and the number of pins, it is enough, only you need to change the header for the GetKey function or use ... #ifdef. Make a global header file, in which the type of controller used (something like #define STM32F030) and heap up a bunch of defines. No, this is not why high-level languages ββwere created to get confused in macros!
Let's go the other way. Let's create a class in which we list the virtual methods that we need in life to work with ports:
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); }
};
(those methods that are equal to 0 must be defined in the derived class!)
and we will use it as the base one in the Pin class:
...
#include "iPin.h"
...
class Pin : public iPin
...
then the GetKey function will change slightly:
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;
}
Now we don't care about any controller! Even if it is a bus expander working over SPI or I2C. We will consider serial interfaces in the next articles.
So, what is next?
Next, you need to design a class to work with the system timer. But this is already in the next publication.