Chciałem pokazać prostą obsługę portów w CPP. Napisałem kod pod wpływem poradników zamieszczonych tu na forum. Może i to nie jest odkrywcze, ale przy użyciu szablonów udało się uzyskać całkiem ciekawe wyniki pozwalające na wygodne użytkowanie. Poniżej zamieszczam kod do testowania/krytyki.
Register-class.hpp
Kod: Zaznacz cały
#ifndef REGISTER_CLASS_HPP_
#define REGISTER_CLASS_HPP_
#include <stdint.h>
namespace Avr{
template<typename T, int regAddress>
class Register{
public:
Register() = delete;
void setBit(const uint8_t& bitNum) const {
reg() |= (1 << bitNum);
}
void clearBit(const uint8_t& bitNum) const {
reg() &= (1 << bitNum);
}
void setWithMask(const T& mask) const {
reg() |= mask;
}
void clearWithMask(const T& mask) const {
reg() &= ~mask;
}
void toggleBit(const uint8_t& bitNum) const {
reg() ^= (1 << bitNum);
}
void toggleWithMask(const T& mask) const {
reg() ^= mask;
}
bool bitIsSet(const uint8_t& bitNum) const {
return reg() & (1 << bitNum);
}
const T read() const {
return reg();
}
void write(const T& data) const {
reg() = data;
}
private:
volatile T& reg() const {
return *(volatile T*) (regAddress);
}
};
}
#endif /* REGISTER_CLASS_HPP_ */
Gpio.hpp
Kod: Zaznacz cały
#ifndef GPIO_HPP_
#define GPIO_HPP_
#define _SFR_ASM_COMPAT 1
#include <avr/io.h>
#include "Register-class.hpp"
namespace Avr {
namespace Gpio {
enum pinDirection {
IN = 0, OUT = 1,
};
template<typename T, int d_addr, int p_addr, int i_addr>
class Gpio {
public:
Gpio() = delete;
static const Register<T, d_addr> ddr;
static const Register<T, p_addr> port;
static const Register<T, i_addr> pin;
};
template<typename PortID, pinDirection _pd, uint8_t pinNum>
class Pin {
public:
Pin() {
_pd == OUT ? PortID::ddr.setBit(pinNum) : PortID::ddr.clearBit(pinNum);
}
bool get() const {
return PortID::pin.bitIsSet(pinNum);
}
void setHi() const {
PortID::port.setBit(pinNum);
}
void setLo() const {
PortID::port.clearBit(pinNum);
}
void toggle() const {
PortID::port.toggleBit(pinNum);
}
};
#define DEFINE_CLASS_PORT(N) class Port##N : public Gpio<uint8_t, DDR##N, PORT##N, PIN##N>{}
#if defined (PORTA)
DEFINE_CLASS_PORT(A);
#endif
#if defined (PORTB)
DEFINE_CLASS_PORT(B);
#endif
#if defined (PORTC)
DEFINE_CLASS_PORT(C);
#endif
#if defined (PORTD)
DEFINE_CLASS_PORT(D);
#endif
#if defined (PORTE)
DEFINE_CLASS_PORT(E);
#endif
#if defined (PORTF)
DEFINE_CLASS_PORT(F);
#endif
#if defined (PORTG)
DEFINE_CLASS_PORT(G);
#endif
#if defined (PORTH)
DEFINE_CLASS_PORT(H);
#endif
#if defined (PORTJ)
DEFINE_CLASS_PORT(J);
#endif
#if defined (PORTK)
DEFINE_CLASS_PORT(K);
#endif
#if defined (PORTL)
DEFINE_CLASS_PORT(L);
#endif
}
}
#endif /* GPIO_HPP_ */
No i main.cpp
Kod: Zaznacz cały
#include "Gpio.hpp"
#include <util/delay.h>
using namespace Avr::Gpio;
int main(void) {
Pin<PortB, OUT, PB0> led;
PortB::ddr.setBit(PB1);
while (1) {
_delay_ms(100);
led.toggle();
if (led.get()) {
PortB::port.toggleBit(PB1);
}
}
}
Kod biblioteki sam "tworzy" potrzebne klasy do obsługi istniejących portów w dowolnym AVR8 (nie Xmega) z rejestrami w formie DDRx, PORTx i PINx. W przykładzie pokazałem dwie możliwości skorzystania z biblioteki. Biblioteka zawiera osobną klasę do obsługi rejestrów, którą będzie można wykorzystać przy innych bibliotekach (np. obsługa UART z którą na razie walczę).
Pozdrawiam.