Prosta biblioteka do obsługi portów

Tu poruszamy tematy związane z pisaniem programów w języku C++ dla AVR.
Awatar użytkownika
ryba84
Posty: 21
Rejestracja: wtorek 05 kwie 2016, 22:38

Prosta biblioteka do obsługi portów

Postautor: ryba84 » niedziela 15 paź 2017, 00:41

Witam.

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.
Ostatnio zmieniony poniedziałek 16 paź 2017, 00:02 przez ryba84, łącznie zmieniany 5 razy.

Awatar użytkownika
mokrowski
User
User
Posty: 190
Rejestracja: czwartek 08 paź 2015, 20:50
Lokalizacja: Tam gdzie Centymetro

Re: Prosta biblioteka do obsługi portów

Postautor: mokrowski » niedziela 15 paź 2017, 13:01

No i bardzo dobrze :-) Zwróć uwagę na ro że read() zwraca T& ("Te ampersand") a nie T i w zasadzie jest const. Tam może być zbędna kopia.
Te wymaganie const correctness, występuje także w Gpio.
Sprawdź także co dzieje się jeśli nie zezwolisz kompilatorowi na generowanie domyślnego konstruktora a zdecydujesz się na dedykowaną metodę init(). W AVR daje to kilka bajtów oszczędności.
Może nazwa setWithMask() i clearWithMask() będzie lepsza?
Z tego co wiem, dane wejściowe dla portu czytane powinny być z PIN*?

Te uwagi traktuj jako sugestie. Nie mam teraz dostępu do środowiska. Później zweryfikuję te uwagi i ew. dodam jakieś sugestie :-)
,,Myślenie nie jest łatwe, ale można się do niego przyzwyczaić" - Alan Alexander Milne: Kubuś Puchatek

Awatar użytkownika
ryba84
Posty: 21
Rejestracja: wtorek 05 kwie 2016, 22:38

Re: Prosta biblioteka do obsługi portów

Postautor: ryba84 » niedziela 15 paź 2017, 19:39

Naniosłem sugestie w pierwszym poście. Odnośnie blokowania domyślnego konstruktora też się udało we wszystkich klasach poza klasą Pin, ale chyba to wynika ze sposobu użycia w kodzie main. Zmian w długości kodu nie zauważyłem (298bajtów na ATmega2560 - w tym 18 bajtów zajmuje _delay_ms, 226 bajtów to tablica wektorów przerwań). Odczyt stanu pinu odbywa się z rejestrów PIN*, chyba że ja czegoś nie widzę w tym kodzie.

Awatar użytkownika
mokrowski
User
User
Posty: 190
Rejestracja: czwartek 08 paź 2015, 20:50
Lokalizacja: Tam gdzie Centymetro

Re: Prosta biblioteka do obsługi portów

Postautor: mokrowski » poniedziałek 16 paź 2017, 08:53

Poszedłem trochę inną drogą. Metody są statyczne a różnicowanie elementu następuje na poziomie szablonu. IMHO jeszcze daleko do GPIO bo brak definicji portu wejściowego i wyjściowego, trybu pracy pinu (cyfrowy, analogowy, pwm... ). Tak więc ten Led, trochę dla demonstracji.
Powinno być nieco lepiej z zajętością Flash :-)

main.cpp

Kod: Zaznacz cały

#include <util/delay.h>
#include <led.hpp>

using namespace avrcpplib;

int main() {

   Led<PortA, 0, LedMode::SINK_LED> led;
   led.init();
   for(;;) {
      _delay_ms(500);
      led.toggle();
   }
}


ports.hpp

Kod: Zaznacz cały

#ifndef PORTS_HPP_
#define PORTS_HPP_

#include <avr/io.h>
#include <stdbool.h>
#include <stdint.h>

namespace avrcpplib {

#define IORWPort8(port_name) struct port_name##Port { \
   static volatile uint8_t * ptr() { return &port_name; } \
   void operator=(const uint8_t& value) { *ptr() = value; } \
   uint8_t operator()() { return *ptr(); } \
}

template<class IOPort, uint8_t BitNumber>
struct BitInRWPort {
   static void clear();
   static void set();
   static void toggle();
   static bool valueOf();
};

template<class IOPort, uint8_t BitNumber>
void BitInRWPort<IOPort, BitNumber>::clear() {
   *IOPort::ptr() &= ~(1 << BitNumber);
}

template<class IOPort, uint8_t BitNumber>
void BitInRWPort<IOPort, BitNumber>::set() {
   *IOPort::ptr() |= (1 << BitNumber);
}

template<class IOPort, uint8_t BitNumber>
void BitInRWPort<IOPort, BitNumber>::toggle() {
   *IOPort::ptr() ^= (1 << BitNumber);
}

template<class IOPort, uint8_t BitNumber>
bool BitInRWPort<IOPort, BitNumber>::valueOf() {
   return *IOPort::ptr() & (1 << BitNumber) ? true: false;
}

IORWPort8(DDRA);
IORWPort8(PORTA);
IORWPort8(PINA);

IORWPort8(DDRB);
IORWPort8(PORTB);
IORWPort8(PINB);

IORWPort8(DDRC);
IORWPort8(PORTC);
IORWPort8(PINC);

IORWPort8(DDRD);
IORWPort8(PORTD);
IORWPort8(PIND);

template<class InputPort, class OutputPort, class ModePort>
struct Port {
   using input_t = InputPort;
   using output_t = OutputPort;
   using mode_t = ModePort;
};

using PortA = Port<PINAPort, PORTAPort, DDRAPort>;
using PortB = Port<PINBPort, PORTBPort, DDRBPort>;
using PortC = Port<PINCPort, PORTCPort, DDRCPort>;
using PortD = Port<PINDPort, PORTDPort, DDRDPort>;

} // End namespace avrcpplib

#endif // PORTS_HPP_


led.hpp

Kod: Zaznacz cały

#ifndef LED_HPP_
#define LED_HPP_

#include <ports.hpp>

namespace avrcpplib {

enum class LedMode {
   SINK_LED,
   SOURCE_LED
};

template<typename LedPort, uint8_t BitNumber, LedMode Mode = LedMode::SINK_LED>
class Led {
public:
   static void init();
   inline static void on();
   inline static void off();
   inline static void toggle();
};

template<typename LedPort, uint8_t BitNumber, LedMode Mode>
void Led<LedPort, BitNumber, Mode>::init() {
   BitInRWPort<typename LedPort::mode_t, BitNumber>::set();
   Mode == LedMode::SOURCE_LED ?
      on() :
      off();
}

template<typename LedPort, uint8_t BitNumber, LedMode Mode>
void Led<LedPort, BitNumber, Mode>::on() {
   Mode == LedMode::SOURCE_LED ?
         BitInRWPort<typename LedPort::output_t, BitNumber>::clear():
         BitInRWPort<typename LedPort::output_t, BitNumber>::set();
}

template<typename LedPort, uint8_t BitNumber, LedMode Mode>
void Led<LedPort, BitNumber, Mode>::off() {
   Mode == LedMode::SOURCE_LED ?
      BitInRWPort<typename LedPort::output_t, BitNumber>::set():
      BitInRWPort<typename LedPort::output_t, BitNumber>::clear();
}

template<typename LedPort, uint8_t BitNumber, LedMode Mode>
void Led<LedPort, BitNumber, Mode>::toggle() {
   BitInRWPort<typename LedPort::output_t, BitNumber>::toggle();
}

}  // namespace avrcpplib

#endif /* LED_HPP_ */
,,Myślenie nie jest łatwe, ale można się do niego przyzwyczaić" - Alan Alexander Milne: Kubuś Puchatek

Awatar użytkownika
ryba84
Posty: 21
Rejestracja: wtorek 05 kwie 2016, 22:38

Re: Prosta biblioteka do obsługi portów

Postautor: ryba84 » wtorek 17 paź 2017, 03:09

Taka szybka analiza po skompilowaniu. Zajętość flash ta sama co u mnie przy main robiącym to samo. Jedyna różnica, że w twoim kodzie po wywołaniu init jest ustawiany stan początkowy portu (chyba bo z jakiegoś powodu niezależnie czy ustawię SINK czy SOURCE to gasi 0 bit w rejestrze 5 dla portu B), co daje 2 bajty więcej flasha dla ATmego 2560. Generalnie fajny pomysł z tym ustawieniem "kierunku" sterowania ledem podczas inicjalizacji. Jak będę mniej zmęczony to wgryzę się bardziej w Twoją interpretację. Odnośnie tworzenia biblioteki "arduinopodobnej" to raczej nie było to moim zamysłem. Raczej będę dążył do świadomej obsługi sprzętu, z wszystkimi tego konsekwencjami (PWM z timerami, a nie "analogowe" wyjście itp..). Na razie kombinuję jak tu zaszyć obsługę przerwań w szablonie klasy. Już wiem, że będzie to musiała być funkcja "zaprzyjaźniona" z klasą, tylko nie mam pomysłu jak nie generować zbędnych i niewykorzystanych wektorów przerwań, bez używania preprocesora.

Edyta:

Kod: Zaznacz cały

#ifndef LED_HPP_
#define LED_HPP_

#include <stdint.h>
#include "Gpio.hpp"

namespace Avr {
namespace Gpio {
enum ledMode {
   SINK_LED, SOURCE_LED
};
template<typename ledPort, uint8_t bitNumber, ledMode mode>
class Led {
public:
   Led() {
      ledPort::ddr.setBit(bitNumber);
      mode == SOURCE_LED ?
            on() :
            off();
   }
   static void on() {
      mode == SOURCE_LED ?
            ledPort::port.clearBit(bitNumber) :
            ledPort::port.setBit(bitNumber);
   }
   static void off() {
      mode == SOURCE_LED ?
            ledPort::port.setBit(bitNumber) :
            ledPort::port.clearBit(bitNumber);
   }
   static void toggle() {
      ledPort::port.toggleBit(bitNumber);
   }
};
}
}

#endif /* LED_HPP_ */

To Twoja klasa Led przepisana przeze mnie. Ciekawostka! Coś chyba nie tak z kompilatorem, bo w mojej wersji LED jest dodatkowe CBI lub SBI w zależności czy wybiorę SINK czy SOURCE, a u Ciebie zawsze było CBI.
Ostatnio zmieniony wtorek 17 paź 2017, 21:38 przez ryba84, łącznie zmieniany 2 razy.

Awatar użytkownika
mokrowski
User
User
Posty: 190
Rejestracja: czwartek 08 paź 2015, 20:50
Lokalizacja: Tam gdzie Centymetro

Re: Prosta biblioteka do obsługi portów

Postautor: mokrowski » wtorek 17 paź 2017, 15:06

Hmm... no właśnie z powodu zajętości pamięci mam klasy szablonowe oraz odstąpienie od konstruktora który generuje niepotrzebny narzut w asemblerze. Innym problemem jest także to że port (np. PORTA), będąc typem złożonym (posiada volatile), nie może wg. kompilatora i standardu być argumentem szablonu. Chciałem także uniknąć siłowego rzutowania (przez void *).
Mam avr-gcc w wersji 7.1. W przypadku mojego kodu wynik zajętości (dla ATMega16) to:
138 bajtów mojego kodu, dla Twojego (przy tym samy procesorze i kodzie z tego wątku), 150 bajtów.
Jeśli rzeczywiście tak jest że masz w moim kodzie wyłącznie CBI, to coś jest nie tak. Sprawdzę na starszym kompilatorze.
Nie, nie chcę zrobić "Arduino 2" :-) Raczej chcę ograniczyć możliwość ustawiania dla danej nogi tylko tego co wspiera MCU. Stąd pomysł że Input i Output to dobre klasy, ParallelPortA to także dobra klasa do implementacji komunikacji równoległej itp. Po drodze wystąpią jeszcze problemy czytania/pisania synchronicznego i asynchronicznego.
Co zagadnienia generowania ISR'a (lub nie), mam jakiś pomysł ale lepiej go sprawdzić przy sprzęcie i narzędziach... Myślę że nikt się nie obrazi jak w tym wątku "nawrzucamy sobie" trochę kodu :-) Zawsze to ktoś o coś może się pytać. Na razie zainteresowanie nikłe :-/
,,Myślenie nie jest łatwe, ale można się do niego przyzwyczaić" - Alan Alexander Milne: Kubuś Puchatek

Awatar użytkownika
ryba84
Posty: 21
Rejestracja: wtorek 05 kwie 2016, 22:38

Re: Prosta biblioteka do obsługi portów

Postautor: ryba84 » wtorek 17 paź 2017, 21:53

Poprawiłem tą klasę tak by była identyczna jak Twoja. Teraz generuje taki sam kod wynikowy. Chyba już za późno było i jakieś zaćmienie miałem. U mnie wersja avr-gcc to 4.9.2 (AVR_8_bit_GNU_Toolchain_3.5.1_1671). Przy tej wersji obie generują 138 bajtów kodu wynikowego na ATmega16. Czyli są jakieś zmiany w samym kompilatorze. Chyba wiem w czym problem z tym kasowaniem bitu w rejestrze wyjściowym przy inicjalizacji. Jeśli podczas inicjalizacji "led" ma być gaszony to zbędny jest warunkowanie wyboru funkcji w zależności od sposobu podłączenia. Wystarczy wywołanie samej metody off(), bo przecież ona już jest tym uwarunkowana. A i jeszcze w ramach dywagacji na temat rozmiaru to są moje flagi kompilatora:

Kod: Zaznacz cały

-Wall -Os -fpack-struct -fshort-enums -ffunction-sections -fdata-sections -std=c++11 -Wextra -pedantic -g -funsigned-char -funsigned-bitfields -fno-exceptions

A to linkera:

Kod: Zaznacz cały

-mrelax -Wl,--gc-sections

O tym, że typy złożone nie mogą być argumentem szablonu to nie wiedziałem. Twoja koncepcja jest całkiem ciekawa. Mam nadzieję, że się jeszcze inni włączą, bo ja nie jestem zawodowym programistą i się tak naprawdę uczę w ramach relaksu. Spoglądając na kod w C i C++ to dochodzę do wniosku, że w C++ łatwiej stworzyć kod do użycia z inną rodziną mikrokontrolerów. Oczywiście zmieni się cała część odpowiedzialna za obsługę sprzętu, ale sam interfejs można przenosić. Patrząc na klasę Register to tak na prawdę, można rozbudować ten szablon tak by dało się obsługiwać rejestry z 16/32 bitowych mikroklocków.
Ściągnąłem avr-gcc w wersji 7.2 i nadal generuje 138 bajtów kodu w mojej wersji. Zmieniłem troszkę biblioteki (zrobiłem metody statyczne jak u Ciebie, zmieniłem nazewnictwo i użyłem using w klasie Gpio zamiast tworzenia zbędnych obiektów). Co ciekawe dzięki tym zmianom w Eclipse zaczęło działać autouzupełnianie. Poniżej pliki po zmianach:
Register.hpp

Kod: Zaznacz cały

#ifndef REGISTER_HPP_
#define REGISTER_HPP_

#include <stdint.h>

namespace Avr {
template<typename T, int regAddress>
class Register {
public:
   Register() = delete;
   static void setBit(const uint8_t& bitNum) {
      reg() |= (1 << bitNum);
   }
   static void clearBit(const uint8_t& bitNum) {
      reg() &= ~(1 << bitNum);
   }
   static void setWithMask(const T& mask) {
      reg() |= mask;
   }
   static void clearWithMask(const T& mask) {
      reg() &= ~mask;
   }
   static void toggleBit(const uint8_t& bitNum) {
      reg() ^= (1 << bitNum);
   }
   static void toggleWithMask(const T& mask) {
      reg() ^= mask;
   }
   static bool bitIsSet(const uint8_t& bitNum) {
      return reg() & (1 << bitNum) ? true : false;
   }
   static const T read() {
      return reg();
   }
   static void write(const T& data) {
      reg() = data;
   }
private:
   static volatile T& reg() {
      return *(volatile T*) (regAddress);
   }
};
}

#endif /* REGISTER_HPP_ */

Gpio.hpp

Kod: Zaznacz cały

#ifndef GPIO_HPP_
#define GPIO_HPP_

#define _SFR_ASM_COMPAT 1

#include <avr/io.h>
#include "Register.hpp"

namespace Avr {
namespace Gpio {
enum mode_t {
   IN, OUT
};
template<typename T, int d_addr, int p_addr, int i_addr>
class Gpio {
public:
   Gpio() = delete;
   using mode_t=Register<T, d_addr>;
   using output_t=Register<T, p_addr>;
   using input_t=Register<T, i_addr>;
};
template<typename PortID, mode_t pinDirection, uint8_t pinNum>
class Pin {
public:
   Pin() {
      pinDirection == OUT ? PortID::mode_t::setBit(pinNum) : PortID::mode_t::clearBit(pinNum);
   }
   bool get() const {
      return PortID::input_t::bitIsSet(pinNum);
   }
   void setHi() const {
      PortID::output_t::setBit(pinNum);
   }
   void setLo() const {
      PortID::output_t::clearBit(pinNum);
   }
   void toggle() const {
      PortID::output_t::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_ */

Led.hpp

Kod: Zaznacz cały

#ifndef LED_HPP_
#define LED_HPP_

#include <stdint.h>
#include "Gpio.hpp"

namespace Avr {
namespace Gpio {
enum ledMode_t {
   SINK_LED, SOURCE_LED
};
template<typename ledPort, uint8_t bitNumber, ledMode_t mode>
class Led {
public:
   Led() {
      ledPort::mode_t::setBit(bitNumber);
      off();
   }
   static void on() {
      mode == SOURCE_LED ?
            ledPort::output_t::clearBit(bitNumber) :
            ledPort::output_t::setBit(bitNumber);
   }
   static void off() {
      mode == SOURCE_LED ?
            ledPort::output_t::setBit(bitNumber) :
            ledPort::output_t::clearBit(bitNumber);
   }
   static void toggle() {
      ledPort::output_t::toggleBit(bitNumber);
   }
};
}
}

#endif /* LED_HPP_ */

main.cpp

Kod: Zaznacz cały

#include "Led.hpp"
#include <util/delay.h>

using namespace Avr::Gpio;
int main(void) {
   Led<PortB, PB0, SOURCE_LED> led;
   while (1) {
      _delay_ms(500);
      led.toggle();
   }
}

Generalnie nie wiedziałem, że można użyć słowa using wewnątrz klasy, a to pokazuje tylko jak wiele jeszcze nie wiem ;)

Awatar użytkownika
mokrowski
User
User
Posty: 190
Rejestracja: czwartek 08 paź 2015, 20:50
Lokalizacja: Tam gdzie Centymetro

Re: Prosta biblioteka do obsługi portów

Postautor: mokrowski » środa 18 paź 2017, 22:41

Trochę rozbudowałem obsługę portów równoległych. Kompilować w C++14. Niestety ale gcc jakie masz (4.9.*) obsługuje C++11 (i z tego co sprawdzałem z kilkoma błędami). Czas podciągnąć aktualność narzędzi :-)
Jeśli masz pytania, zadaj.
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
,,Myślenie nie jest łatwe, ale można się do niego przyzwyczaić" - Alan Alexander Milne: Kubuś Puchatek

Awatar użytkownika
ryba84
Posty: 21
Rejestracja: wtorek 05 kwie 2016, 22:38

Re: Prosta biblioteka do obsługi portów

Postautor: ryba84 » czwartek 19 paź 2017, 14:11

Ciekawie to wygląda po pierwszej analizie. Właśnie się zastanawiałem jak pisałeś o klasie Parallel jak rozwiązać częściowe wykorzystanie portu, a nie tylko całego. Samą ideę constexpr rozumiem. Tworzymy sobie funkcję, która w czasie kompilacji ma zostać wykonana i podstawić samą stałą do kompilowanego programu. Zastanawia mnie konstrukcja z tej linijki:

Kod: Zaznacz cały

constexpr ValueType countLeastZero(ValueType) = delete;
a dokładniej znaczenie tego delete. Ta część kodu też mnie zastanawia:

Kod: Zaznacz cały

constexpr auto countLeastZero(uint32_t value) -> decltype(value)
czemu nie zwracasz konkretnego typu?
Od tak zbudowanej klasy Parallel już niedaleko do 4bitowej obsługi wyświetlaczy z HD44780.

Awatar użytkownika
mokrowski
User
User
Posty: 190
Rejestracja: czwartek 08 paź 2015, 20:50
Lokalizacja: Tam gdzie Centymetro

Re: Prosta biblioteka do obsługi portów

Postautor: mokrowski » czwartek 19 paź 2017, 16:36

Ta linijka:

Kod: Zaznacz cały

constexpr ValueType countLeastZero(ValueType) = delete;

Powoduje że nie będę dozwolona konkretyzacja szablonu a jedynie wypisane poniżej specjalizacje. Można zamiast takiej konstrukcji dodać np. statyczną asercję. Ale jeszcze jej nie mam :-)
Tu..

Kod: Zaznacz cały

constexpr auto countLeastZero(uint32_t value) -> decltype(value)

.. jedynie sprawdziłem czy konstrukcja z auto oraz dedukcją typu z argumentu będzie działała. Mechanizm będzie wykorzystany przy przesyłaniu funkcji z nieznaną liczbą argumentów. Innego merytorycznego powodu nie ma. Ale nie ma także i przeciwskazań :-)

Z portem Parallel jest prosta sprawa. Klasy są różnicowane w naturalny sposób poprzez przesyłaną maskę :-)

Do drivera HD jeszcze trochę. Najpierw dobrze będzie zdefiniować timer, np. typowy przycisk z rejestracją funkcji oraz auto-repetycją.

Na szybko dodałem licznik. To tak generyczne rozwiązanie że przydać się może w bardzo wielu kontekstach.
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
,,Myślenie nie jest łatwe, ale można się do niego przyzwyczaić" - Alan Alexander Milne: Kubuś Puchatek

Awatar użytkownika
ryba84
Posty: 21
Rejestracja: wtorek 05 kwie 2016, 22:38

Re: Prosta biblioteka do obsługi portów

Postautor: ryba84 » piątek 20 paź 2017, 15:17

Musisz mi dać chwilę na przetrawienie tego kodu ;) Ale wygląda ciekawie.
No dobrze to wstępna analiza. Najpierw ports.hpp:
  • struct IOTraits - cechy portu (licznik bitów i ew. bufor do wykorzystania w przyszłości)
  • ReadWritePort8/16 i ReadOnlyPort8/16 - tutaj tylko zapisujemy/odczytujemy dane z rejestru
  • Później klasy do manipulacji bitami w powyższych rejestrach
  • Klasa Port łączy wszystkie rejestry (DDR, PIN i PORT) w komplety
  • Klasy Input i Output są nie skończone, więc na razie je pominę
Zastanawia mnie jedno, gdyby klasa ReadWritePort8/16 nie mogłaby dziedziczyć po klasie ReadOnlyPort8/16. Wtedy byśmy pominęli w niej metodę służącą do odczytu danych, oraz czemu jako ValueType ustawiasz uint8_t w portach 16bit? No i jeszcze konstrukcja

Kod: Zaznacz cały

void operator=(const uint8_t& value) { *ptr() = value; }
Wiem, że chodzi o wpisanie wartości do rejestru. Plik parallel.hpp wraz z plikiem util.hpp jest jasny.
Klasę Counter muszę jeszcze trochę przetrawić. Na pierwszy rzut oka nie wiem za bardzo jakie ma znaczenie

Kod: Zaznacz cały

explicit Counter(const ValueType& value_): value{value_} {}

Generalnie widzę, że powinienem zmienić podejście. Klasy powinny być bardziej wyspecjalizowane, a nie wyglądać tak jak moja klasa Register.


Wróć do „Programowanie AVR w C++”

Kto jest online

Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 4 gości