Le motoréducteur se contrôle via 2 broches, une où est transmise une PWM de plus de 20kHz avec un rapport cyclique de 0% à 100% afin de contrôler la vitesse de rotation du plateau et une afin de changer la direction de rotation du plateau.
*Cette implémentation utiliser les drivers Timer et GPIO faient durant le projet*
##### Calculs préliminaires
Le driver timer demande d'instancier une strcture qui contient les valeurs des futurs registres ARR et PSC. L'équation qui lie la fréquence du timer et celle du microcontrôleur est la suivante :
$$ f_{tim} = {f_µ \over ARR * PSC} $$
Sachant que la fréquence du microcontrôleur est de 72 MHz et que l'o souhaite une fréquence de 20 kHz, on peut facilement calculer les registres ARR et PSC.
Nous allons donc choisir de mettre ARR à 3599 (le registre compte à partir de 0) et PSC à 0, ce qui va à moindre échelle éviter au passage quelques calculs non nécessaire au microcontrôleur.
*Il faudra donc ensuite prévoir de gérer le rapport cyclique seulement entre 0 et 3599*
___
Il y a d'abord deux defines qui vont être utilisés pour simplifier la fonction de changement du sens de rotation :
```c
#define HORAIRE 0x1
#define ANTIHOR 0x0
```
3 fonctions ont été instanciées dans le fichier motoreducteur.h :
```c
void MyMotor_Init(void);
void MyMotor_ChangeSpeed(unsigned int DC);
void MyMotor_ChangeDirection(uint8_t Sens);
```
**void MyMotor_Init(void)**
```c
MyTimer_Struct_Typedef Timer;
MyGPIO_Struct_TypeDef Pin_Direction;
Timer.Timer = TIM3;
Timer.ARR = 3599;
Timer.PSC = 0;
Pin_Direction.GPIO = GPIOB;
Pin_Direction.GPIO_Pin = 1;
Pin_Direction.GPIO_Conf = Out_PullUp;
MyTimer_Base_Init(&Timer);
MyGPIO_Init(&Pin_Direction);
MyTimer_PWM(TIM3, 3);
MyTimer_DutyCycle(TIM3, 3, 0);
MyTimer_Base_Start(Timer);
MyMotor_ChangeDirection(ANTIHOR);
```
La fonction MyMotor_Init initialise le timer qu'elle va utiliser pour produire une PWM de fréquence 50 kHz puis initiliaser la broche sur laquelle va être transmise le sens de rotation voulu du plateau. Elle va ensuite démarrer le timer.
**void MyMotor_ChangeSpeed(unsigned int DC)**
```c
MyTimer_DutyCycle(TIM3, 3, DC);
```
La fonction MyMotor_ChangeSpeed sert principalement a avoir un meilleure nom et respecter le nommage des fonctions de cette implémentation, elle change la valeur du rapport cyclique du timer concerné (la valeur doit être comprise entre 0 et 10000).
**void MyMotor_ChangeDirection(uint8_t Sens)**
```c
if (Sens == HORAIRE)
MyGPIO_Set(GPIOB, 1);
if (Sens == ANTIHOR)
MyGpio_Reset(GPIOB, 1);
```
La fonction MyMotor_ChangeDirection met à un la broche qui gère la direction du plateau si le sens choisi est horaire et met à 0 si le sens choisi est antihoraire.
#### Test de l'implémentation
Le motoréducteur a été testé en simulation (vérification des registres, de l'avancement des timers, des signaux de PB1 et du channel 3 du timer 3) ansi que sur maquette en essayant des valeurs dans le code puis en connectant à l'implémentation de la télécommande.
En testant, nous nous sommes rendu compte qu'entre 0 et 1000, le moteur ne tournait pas car il n'avait pas assez de puissance.
Le servomoteur se contrôle via 1 broches où est transmise une PWM de période de 20 ms avec un rapport cyclique de 5% à 10% afin de contrôler l'angle de l'axe du servomoteur.
*Cette implémentation utiliser le driver Timer fait durant le projet*
##### Calculs préliminaires
Le driver timer demande d'instancier une strcture qui contient les valeurs des futurs registres ARR et PSC. L'équation qui lie la fréquence du timer et celle du microcontrôleur est la suivante :
$$ f_{tim} = {f_µ \over ARR * PSC} $$
Sachant que la fréquence du microcontrôleur est de 72 MHz et que l'o souhaite une fréquence de 20 kHz, on peut facilement calculer les registres ARR et PSC.
Nous allons donc choisir de mettre ARR à 3599 car il faut pouvoir gérer 180° possibles sur une seule des 20 ms de la période du timer (180 * 20 = 3600) et PSC à 399 pour avoir le division de 3600 * 499 = 1440000.
*Il faudra donc ensuite prévoir de gérer le rapport cyclique seulement entre 0 et 3599*
___
2 fonctions ont été instanciées dans le fichier servo.h :
La fonction MyServo_Init initialise le timer qu'elle va utiliser pour produire une PWM de fréquence 50 Hz puis met une valeur arbitraire qui sera testée sur maquette au rapport cyclique et démarre le timer.
**void MyServo_ChangeAngle(uint8_t Angle)**
```c
if (Angle > 180)
Angle = 180;
int DC = 500 + (Angle * 500 / 180);
MyTimer_DutyCycle(TIM2, 1, DC);
```
La fonction MyServo_ChangeAngle reçoit en paramètre l'angle souhaité pour le servomoteur et la fonction teste si ce dernier dépasse 180° (angle maximum de rotation du servomoteur) avant de calculer la valeur correspondante entre 5% et 10% du rapport cyclique et l'appliquer au timer.
#### Test de l'implémentation
Le servomoteur a été testé en simulation (vérification des registres, de l'avancement des timers et du channel 1 du timer 2) ansi que sur maquette en essayant des valeurs entre 0 et 180°.
Les tests ont permis de mettre en valeur le fait que la voile est complètement lache à 0° et tendu à 180°.
___
## Télécommande
La télécommande permet deux choses via l'IHM :
- Transmettre la vitesse de rotation demandé et le sens pour le voilier
- Afficher une transmission série d'informations importantes comme la tension de la batterie, si le voilier a chavirer et toutes les 3 secondes les données de l'accéléromètre
*Cette implémentation utiliser le driver Uart fait durant le projet*
___
3 fonctions ont été instanciées dans le fichier remote.h :
Cette structure contient les informations sur quel UART va être utilisé, son baudrate ainsi que des paramètres pour la transmission (longueur de la donnée, parité et nombre de bits de stop).
**void remote(uint8_t data)**
```c
MyUART_Send(&uartCool,data);
int8_t signedData = (int8_t)data;
if(signedData >= 0)
{
MyMotor_ChangeDirection(HORAIRE);
MyMotor_ChangeSpeed(signedData*100);
}
else {
MyMotor_ChangeDirection(ANTIHOR);
MyMotor_ChangeSpeed((-signedData)*100);
}
```
La fonction remote attend la réception d'une donnée de la télécommande (la valeur de la vitesse et le sens de rotation voulu du plateau) puis utilise l'implémentation motoreducteur afin de contrôler la rotation du voilier.
**void initRemote(void)**
```c
MyUART_InitGPIO(&uartCool);
MyUART_Init(&uartCool);
MyUART_Init_Periph(remote);
```
La fonction initRemote initialise l'UART qui va être utilisé afin de communiquer avec la télécommande du projet voilier. Elle initialiser donc les broches, puis les registres et enfin déclare la fonction remote comme la nouvelle *Interrupt request handler* de ce timer.
**void testRemote(void)**
```c
MyUART_Send(&uartCool,'s');
MyUART_Send(&uartCool,'a');
MyUART_Send(&uartCool,'l');
MyUART_Send(&uartCool,'u');
MyUART_Send(&uartCool,'t');
```
La fonction testRemote teste basiquement si la télécommande fonctionne en lui envoyant une chaîne de caractère.
#### Test de l'implémentation
Le fonctionnement de la télécommande et la réception de la donnée de vitesse et rotation du plateau à d'abord été testée en déboguage afin de connapitre le format de cette donnée. Les tests suivant sur maquette ont permis de vérifier le bon envoi de données ainsi que la réception de la commande et son application au plateau.
___
## Real Time Clock
La real time clock est un composant sur la carte du voilier qui permet de garder en mémoire le temps qui s'écoule précisément.
*Cette implémentation utiliser le driver I2C founit durant le projet*
___
2 fonctions ont été instanciées dans le fichier rtc.h :
La fonction MyRTC_Init utilise la librarie founie I2C afin d'initialiser l'I2C 2 du microcontrôleur et demande une priorité minimale à son interruption (cela ne pose pas de problème de ne pas avoir l'heure).
La fonction MyRTC_GetTime instancie une structure de donnée I2C en fournissant l'adresse esclave du composant RTC, l'adresse de la variable qui va contenir les données reçu mais également le nombre d'octet à recevoir.
Elle va ensuite utilisé le driver SPI pour lire les regsitres de l'adresse 0x00 à 0x06 (en 7 fois donc) et lire les secondes, minutes, heures, jour, date, mois et années puis les écrires dans les variables contenu par les pointeurs fournis en paramètres à la fonction.
Un simple affichage du contenu des variables après l'appel à cette fonction en mode déboguage à permis de vérifier que l'implémentation rtc fonctionne bien.
___
## Accéléromètre
L'accéléromètre est un composant qui est sur la carte du voilier et qui permet de connaître la position angulaire de ce dernier dans l'espace via de la lecture dans les regsitres à l'aide du SPI.
MySPI_Send(0x2D);//Registre Auto_sleep + Write + measure a 1
MySPI_Send(0x08);// d<>sactive
MySPI_Set_NSS();//CS HIGH
/*
MySPI_Clear_NSS(); //CS LOW
MySPI_Send(0xAD);// regisstre 0x2D en lecture
testReg = MySPI_Read(); // lecture de la valeur du registre
MySPI_Set_NSS();//CS HIGH
*/
MySPI_Clear_NSS(); //CS LOW
MySPI_Send(0X2C);//Registre power consuption + Write
MySPI_Send(0X1A);//Param<61>trage hors low consumption + 100Hz output data rate
MySPI_Set_NSS();//CS HIGH
MySPI_Clear_NSS(); //CS LOW
MySPI_Send(0x31);//registre Data format + write
MySPI_Send(0x17);//
MySPI_Set_NSS();//CS HIGH
```
La fonction Init_accelerometre utiliser d'abord la librarie SPI founit afin d'initialiser le SPI 1.
Elle paramètre ensuite l'accéléromètre comme souhaité pour désactiver le mode auto sleep, utiliser le mode de faible consommation et formater les données qui vont être ensuite lues.
*GX = dataX*0.004;//valeur du registre x pas accelerometre
dataY = MySPI_Read()<<8;
dataY |= MySPI_Read();
dataY &= 0x1FFF;
if (dataY > 511)
{
dataY -= 1024;
}
*GY = dataY*0.004;//valeur du registre x pas accelerometre
dataZ = ((MySPI_Read()<<8)|MySPI_Read());
dataZ &= 0x1FFF;
/* if (dataZ > 511)
{
dataZ -= 1024;
}
*/
*GZ = dataZ*0.004;//valeur du registre x pas accelerometre
MySPI_Set_NSS();//CS HIGH
```
La fonction Lecture_accelerometre renvoie dans des variables pointé en paramètres les valeurs de la force en X, en Y et en Z.
Le fonctionnement est comme suis :
- Envoi de l'adresse du resgistre désiré
- Lecture et mise dans une variable locale de la valeur du regsitre
- Masque et calcul afin de formater la données à renvoyer
Ces trois étapes sont faites pour fournir en X, Y et Z la gravité (donc ensuite l'angle) que chaque axe perçoit.
#### Test de l'implémentation
l'implémenation a été testée en mode débogage afion de savoir dans un premier temps si les données sont bien reçues puis dans un second temps des calculs ont été faits afin de vérifier si les données sont correctes.
Des test sur maquette sont en cours...
___
## Batterie
L'implémentation batterie va permettre de savoir si la tension de la batterie intégrée au projet voilier à une tension suffisante au bon fonctionnement du système et d'envoyer ensuite cette donnée vers l'UART connecté à la télécommande.
*Cette implémentation utiliser les drivers ADC et GPIO faient durant le projet*
___
Il y a d'abord un define qui permet de tester facilement la tension minimala acceptable pour la batterie intégré au voilier.
```c
#define MAX_BAT 1145
```
4 fonctions ont été instanciées dans le fichier battery.h :
```c
void battery(uint32_t data);
void getGauge(char gauge[], float percent);
void initBattery(void);
char isClose(uint32_t data, uint32_t compare, int precision);
```
**void battery(uint32_t data)**
```c
MyRTC_Struct_TypeDef rtcBattery;
MyRTC_GetTime(&rtcBattery);
if((actualMinutes == rtcBattery.minutes) && isClose(oldAdc,data,50)) //pas de precision/10 %
Des test sur maquette ont été dans un premier temps faits pour vérifier que l'ADC renvoyé une valeur correcte (changeante en fonction de la tension d'une alimentation émulant la batterie).
Des test sur maquette sont en cours...
___
## Girouette
*Cette implémentation utiliser les drivers Timer et GPIO faient durant le projet*
___
3 fonctions ont été instanciées dans le fichier girouette.h :
```c
int MyGirouette_Angle(TIM_TypeDef *TIMX);
void MyGirouette_Init(TIM_TypeDef *TIMX);
void MyGirouette_Init_IT_Z(uint8_t GPIO_Pin);
```
**void MyGirouette_Init(TIM_TypeDef* TIMX)**
```c
//configuration gpiob6 et gpiob7 en entrées In_Floating imposer par le timer4
TIMX->CCMR1 |= TIM_CCMR1_CC2S_0; //CC2S dans CCMR1 et pas CCMR2
TIMX->CCER &=~TIM_CCER_CC1P;
TIMX->CCER &=~TIM_CCER_CC2P;
TIMX->CCMR1&=~(0x0F<<4); //IC1F
TIMX->CCMR1&=~(0x0F<<12);//IC2F
TIMX->CR1 |= 1;
```
La fonction MyGirouette_Angle initialise d'abord les broches dont elle a besoin puis instancie une structure de timer et le paramètre en codeur incrémental.
**int MyGirouette_Angle(TIM_TypeDef* TIMX)**
```c
return (TIMX->CNT)/4;
```
La fonction MyGirouette_Angle retourne la valeur de l'angle de la girouette en calculant à partir du resgistre CTN du timer en codeur incrémental.
La fonction MyGirouette_Init_IT_Z paramètre une interruption extérieure sur broche afin de faire fonctionner le codeur incrémental et pour récuperer les données de la girouette.