diy:projets:tl_partieemetteur
Différences
Ci-dessous, les différences entre deux révisions de la page.
| Les deux révisions précédentesRévision précédenteProchaine révision | Révision précédente | ||
| diy:projets:tl_partieemetteur [2018/05/25 08:21] – [Interface avec le gpio] sdurand | diy:projets:tl_partieemetteur [2018/07/02 09:34] (Version actuelle) – [Le programme principal et la fonction qui gère la led] sdurand | ||
|---|---|---|---|
| Ligne 40: | Ligne 40: | ||
| echo V > / | echo V > / | ||
| </ | </ | ||
| - | avec **V** qui peut valoir **1 pour éteindre la led** ou **0 pour allumer la led** (cela vous semble peut | + | avec **V** qui peut valoir **1 pour éteindre la led** ou **0 pour allumer la led** (**ceci dépend de la façon |
| - | être contre intuitif, mais c'est comme ça) et **X** qui est toujours le **numéro du pin de la led**. | + | dont vous avez branché la led au raspberry**, notre branchement |
| + | est toujours le **numéro du pin de la led**. | ||
| - | Et ça va se passer de la même manière en C++ ! Vous pouvez très bien **ouvrir ces fichiers dans votre code** pour y | + | Quand vous aurez fini de jouer, vous devrez dé-exportez la led. Là aussi, c'est très simple: |
| + | < | ||
| + | echo numero_du_pin_de_la_led > / | ||
| + | </ | ||
| + | Et voilà, le pin de la led est de nouveau " | ||
| + | |||
| + | Tout va se passer de la même manière en C++ ! En effet, vous pouvez très bien **ouvrir ces fichiers dans votre code** pour y | ||
| **effectuer toutes les opérations** dont je viens de vous parler, c'est comme ça que je vous recommande de procéder. | **effectuer toutes les opérations** dont je viens de vous parler, c'est comme ça que je vous recommande de procéder. | ||
| J'ai personnellement choisi de **créer une classe** pour encapsuler ces opérations. Le code de cette classe est | J'ai personnellement choisi de **créer une classe** pour encapsuler ces opérations. Le code de cette classe est | ||
| - | disponible | + | disponible |
| tout réécrire vous même. | tout réécrire vous même. | ||
| Ligne 66: | Ligne 73: | ||
| Si vous êtes arrivés jusqu' | Si vous êtes arrivés jusqu' | ||
| ==== Les changements d' | ==== Les changements d' | ||
| + | |||
| + | Dans cette partie, nous allons écrire un **main** qui va utiliser notre **API** pour allumer/ | ||
| + | en fonction de la valeur de **chaque bit** de **chaque caractère** d'un message. Nous avons décidé d' | ||
| + | **chaînes de caractères comme données à transmettre**, | ||
| + | visualiser les **erreurs de bits** sur des caractères que sur les valeurs des pixels d'une image ou tout autre | ||
| + | type de données qu'on aurait pu transmettre. | ||
| + | |||
| + | En concevant le code que je vais vous présenter, j'ai prévu **deux chaînes de caractères** particulières | ||
| + | qui **agirait comme des commandes**. Ces deux commandes sont // | ||
| + | proprement et **libérer les ressources** // | ||
| + | une séquence particulière de 0 et de 1 (qui sont en fait des **" | ||
| + | cyclique de 0 et de 1 va nous permettre de visualiser d' | ||
| + | |||
| + | Voyons à quoi ressemble **la boucle de notre main**: | ||
| + | <code C++> | ||
| + | int main() { | ||
| + | // GPIO_PIN est définie via un define par la valeur du pin de la led | ||
| + | | ||
| + | | ||
| + | |||
| + | | ||
| + | // On récupère le premier message à envoyer, on n' | ||
| + | | ||
| + | |||
| + | // Tant qu'on a pas saisi le message d' | ||
| + | | ||
| + | std::cout << " | ||
| + | | ||
| + | // On vérifie si on a reçu la séquence de test, si c'est le cas, on envoie un certains nombre de U | ||
| + | if(message == "/ | ||
| + | // Attention, suivant la fréquence que vous sélectionnerai pour les bits, cette séquence peut être très longue | ||
| + | | ||
| + | } | ||
| + | else { | ||
| + | // Si on a un message lambda, on le trasnmet | ||
| + | | ||
| + | } | ||
| + | std::cout << " | ||
| + | } | ||
| + | |||
| + | | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Pour vous pouvez le voir, rien de bien compliqué là aussi. On récupère simplement des messages et on | ||
| + | les traite en conséquence. Notez que si vous souhaitez ajouter **plus de commandes**, | ||
| + | utiliser un // | ||
| + | |||
| + | Passons maintenant au coeur du programme, la **conversion de bits en signaux lumineux**. Nous allons commencer | ||
| + | par créer la boucle nous permettant d' | ||
| + | **la norme C++14**, vous pouvez faire ça comme suit: | ||
| + | <code C++> | ||
| + | for(const char& c: message) { | ||
| + | // ... | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Nous devons ensuite itérer //"sur les bits"// | ||
| + | bit à bit (via l' | ||
| + | à un (ex: 10000000, 01000000, 001 ...). J'ai donc déclaré un tableau constant en variable globale | ||
| + | qui contiendra tout ces octets. | ||
| + | <code C++> | ||
| + | const unsigned char powers_of_two[8] = {128, 64, 32, 16, 8, 4, 2, 1}; | ||
| + | </ | ||
| + | |||
| + | Nous pouvons maintenant tester pour tout les caractères (en utilisant une boucle) de cette façon: | ||
| + | <code C++> | ||
| + | const bool lightUp = c & powers_of_two[i]; | ||
| + | </ | ||
| + | |||
| + | Au final, **votre code devrait ressembler a quelque chose comme ça**: | ||
| + | <code C++> | ||
| + | void transmit(LedController& | ||
| + | // On initialise la variable timer comme le temps référence de notre transmission | ||
| + | auto timer = std:: | ||
| + | // On allume la led pour signaler au récepteur le début de la transmission | ||
| + | | ||
| + | |||
| + | // On parcourt le message | ||
| + | | ||
| + | // On parcourt les bits de chaque caractère | ||
| + | for(unsigned int i = 0; i < 8; i++) { | ||
| + | // On incrémente le timer d'un pas pré-calculé (dépendant le la fréquence de transmission choisie) | ||
| + | timer += time_step; | ||
| + | // On regarde avant la pause si on doit allumer ou non la led | ||
| + | const bool lightUp = c & powers_of_two[i]; | ||
| + | // On attend | ||
| + | | ||
| + | |||
| + | // On positionne la led dans le bon état | ||
| + | | ||
| + | } | ||
| + | } | ||
| + | |||
| + | | ||
| + | | ||
| + | |||
| + | // On attend suffisamment pour que le récepteur reçoive un octet de zéros pour marquer la fin de transmission | ||
| + | | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | En ce qui concerne **les pauses**, je vous invite simplement à consulter le code ci-dessus, | ||
| + | vous avez sans doute remarquer que le " | ||
| + | détaillées. | ||
| ---- | ---- | ||
| ===== Sources du projet ===== | ===== Sources du projet ===== | ||
| + | |||
| + | ==== La classe pour contrôler le gpio ==== | ||
| + | |||
| + | <code C++> | ||
| + | #include " | ||
| + | |||
| + | |||
| + | // Private method that export the choosen gpio | ||
| + | void LedController:: | ||
| + | std:: | ||
| + | |||
| + | if(!exportFile.is_open()) { | ||
| + | std::cerr << " | ||
| + | throw ERR_EXPORT; | ||
| + | } | ||
| + | |||
| + | exportFile << this-> | ||
| + | exportFile.close(); | ||
| + | } | ||
| + | |||
| + | |||
| + | // Private method that unexport the previously opened gpio | ||
| + | void LedController:: | ||
| + | std:: | ||
| + | |||
| + | if(!unexportFile.is_open()) { | ||
| + | std::cerr << " | ||
| + | throw ERR_UNEXPORT; | ||
| + | } | ||
| + | |||
| + | unexportFile << this-> | ||
| + | unexportFile.close(); | ||
| + | } | ||
| + | |||
| + | |||
| + | // Constructor of our class: | ||
| + | // numero: numero of the gpio's pin we want to open | ||
| + | // dirOut: boolean indicating if the direction we want is out or not (as it is a LedController, | ||
| + | // litUp: boolean indicating if the led should be initially on or off | ||
| + | LedController:: | ||
| + | this-> | ||
| + | |||
| + | export_gpio(); | ||
| + | // Adding a little delay to make sure that the files created when exporting exists before we start working on them | ||
| + | std:: | ||
| + | |||
| + | std:: | ||
| + | |||
| + | if(!directionFile.is_open()) { | ||
| + | unexport_gpio(); | ||
| + | |||
| + | std::cerr << " | ||
| + | throw ERR_EXPORT; | ||
| + | } | ||
| + | |||
| + | // Setting the direction according to the boolean dirOut | ||
| + | directionFile << (dirOut ? " | ||
| + | directionFile.close(); | ||
| + | |||
| + | if(!litUp) { | ||
| + | turnOff(); | ||
| + | } | ||
| + | else { | ||
| + | turnOn(); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | |||
| + | // Destructor that will unexport the gpio previously opened | ||
| + | LedController:: | ||
| + | unexport_gpio(); | ||
| + | } | ||
| + | |||
| + | |||
| + | // Public method lighting up the led | ||
| + | void LedController:: | ||
| + | std:: | ||
| + | |||
| + | value.open(std:: | ||
| + | |||
| + | if(!value.is_open()) { | ||
| + | unexport_gpio(); | ||
| + | |||
| + | std::cerr << " | ||
| + | throw ERR_VALUE; | ||
| + | } | ||
| + | |||
| + | value << " | ||
| + | |||
| + | value.close(); | ||
| + | } | ||
| + | |||
| + | |||
| + | // Public method turning off the led | ||
| + | void LedController:: | ||
| + | std:: | ||
| + | |||
| + | value.open(std:: | ||
| + | |||
| + | if(!value.is_open()) { | ||
| + | unexport_gpio(); | ||
| + | |||
| + | std::cerr << " | ||
| + | throw ERR_VALUE; | ||
| + | } | ||
| + | |||
| + | value << " | ||
| + | |||
| + | value.close(); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ==== Son header ==== | ||
| + | |||
| + | <code C++> | ||
| + | #ifndef LEDCONTROLLER_HPP | ||
| + | #define LEDCONTROLLER_HPP | ||
| + | |||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | |||
| + | // Refer to the constructor source code for more info on this define | ||
| + | #define EXPORT_DELAY 400 | ||
| + | |||
| + | // A few define to make errors handling clearer | ||
| + | #define ERR_EXPORT -2 | ||
| + | #define ERR_UNEXPORT -3 | ||
| + | #define ERR_VALUE -4 | ||
| + | #define ERR_DIRECTION -5 | ||
| + | |||
| + | |||
| + | |||
| + | class LedController { | ||
| + | public: | ||
| + | LedController(int numero, bool dirOut = true, bool litUp = false); | ||
| + | ~LedController(); | ||
| + | |||
| + | void turnOn(); | ||
| + | void turnOff(); | ||
| + | |||
| + | private: | ||
| + | void export_gpio(); | ||
| + | void unexport_gpio(); | ||
| + | int numero; | ||
| + | }; | ||
| + | |||
| + | #endif | ||
| + | </ | ||
| + | |||
| + | ==== Le programme principal et la fonction qui gère la led ==== | ||
| + | |||
| + | <code C++> | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include " | ||
| + | |||
| + | #define GPIO_PIN 21 | ||
| + | #define BITRATE 20 | ||
| + | #define BITTIME static_cast< | ||
| + | |||
| + | #define ALL_GOOD 0 | ||
| + | |||
| + | |||
| + | const auto time_step = std:: | ||
| + | // We are asuming here that a char is always an octet, and we will assume in every part of the code | ||
| + | const unsigned char powers_of_two[8] = {128, 64, 32, 16, 8, 4, 2, 1}; | ||
| + | |||
| + | |||
| + | // Function that iterates over message and switch on and off the led of lc according to the bits | ||
| + | void transmit(LedController& | ||
| + | // Initializing timer as the reference time | ||
| + | auto timer = std:: | ||
| + | // Turning on the light to tell the receptor we're starting to emit | ||
| + | lc.turnOn(); | ||
| + | |||
| + | // Iterating over the message | ||
| + | for(const char& c: message) { | ||
| + | // Iterating over the bits of c (again, we're assuming that a char is an octet long) | ||
| + | for(unsigned int i = 0; i < 8; i++) { | ||
| + | // Incrementing the timer, computing wether we should lightUp the led or not, and sleeping until it's time to change the state of the led | ||
| + | timer += time_step; | ||
| + | const bool lightUp = c & powers_of_two[i]; | ||
| + | std:: | ||
| + | |||
| + | lightUp ? lc.turnOn() : lc.turnOff(); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | std:: | ||
| + | lc.turnOff(); | ||
| + | |||
| + | // Sleeping long enough to make sure that the receptor received at least a full octet of zeros (it will mark the end of transmission for it) | ||
| + | std:: | ||
| + | } | ||
| + | |||
| + | // This main simply get the input from stdin and act in consequence | ||
| + | int main() { | ||
| + | LedController lc(GPIO_PIN); | ||
| + | std:: | ||
| + | |||
| + | // Getting the first string | ||
| + | std::cout << " | ||
| + | std:: | ||
| + | |||
| + | // Sending message if it is different of "/ | ||
| + | // "/ | ||
| + | while(message != "/ | ||
| + | // I feel like it's ok to not use a switch because I don't plan to add more commands, but if you plan to, you should definitely use one | ||
| + | if(message == "/ | ||
| + | // The test sequence is a sequence of upper case " | ||
| + | transmit(lc, | ||
| + | } | ||
| + | else { | ||
| + | transmit(lc, | ||
| + | } | ||
| + | |||
| + | std::cout << " | ||
| + | |||
| + | std::cout << " | ||
| + | std:: | ||
| + | } | ||
| + | |||
| + | return ALL_GOOD; | ||
| + | } | ||
| + | </ | ||
| ---- | ---- | ||
| - | // | + | // |
diy/projets/tl_partieemetteur.1527236512.txt.gz · Dernière modification : de sdurand
