Commande de robot par FPGA
Sommaire
Projet pour l'année scolaire 2013/2014
Le projet de l'année 2013/2014 et ce rapport correspondant ont été réalisés par les étudiants :
- GALLAND Quentin
- HENRY Guillaume
- KERDILES Guillaume
Présentation du robot arexx
Le Robot Arexx a été envisagé comme base robotique de ce projet. En voici une présentation vidéo :
Le robot Arexx a finalement été abandonné au profit du robot Digilent (suiveur de ligne).
Présentation du nouveau cahier des charges
Notre objectif est de partir d'un Robot commandé par une carte microcontrôleur 32 bits de type PIC32 et de le remplacer par une carte FPGA. La carte utilisée cette année sera une carte Basys2.
Information sur la Basys 2
La Basys2 est une carte FPGA conçue par Digilent. Elle permet la simulation de vrais circuits numériques. La programmation de circuits numériques se fait à l'aide du logiciel XilinX. Grâce à sa grande collection de périphériques d'entrées et de sorties, on peut simuler une grande gamme de circuits numériques.
Caractéristiques :
-Xilinx Spartan 3-E FPGA 100k ou 250k portes; -72Kbits de RAM et fréquence de transfert de 500MHz; -2 ports USB 2.0 pour les transferts de configurations et de données; -Fréquence de l'oscillateur (25,50 et 100MHz) réglable par l'utilisateur; -Trois régulateurs de tension intégrés (1,2V, 2,5V, 3,3V) permettent l'utilisation des sources d'alimentations externes 3.5V-5.5V; -8 LED, 4 Afficheurs sept segments, 4 boutons poussoirs, 8 interrupteurs à glissière, 1 ports PS/2 et 1 port VGA-8bits;
Lien : User Guide Basys2
Carte capteur à photo-transistors
Le CNY70 est un capteur à réflexion optique c'est à dire qu'il est composé d'un photo-transistor filtrant la lumière visible et d'une diode infrarouge. Il permet de détecter un contraste noir-blanc à faible distance (inférieur à 5mm).
On envisage l'implantation de 4 capteurs CNY70 au lieu de 2 vu précédemment dans le projet inter-semestre de 1ere année.
Montage et programmation du robot
Présentation de notre robot
Le robot est composé d'une plate-forme métallique sur laquelle est posée la carte Basys2, de 2roues de 3pouces, de 2moteurs contrôlés chacun par une carte PmodHB5, d'un support de 4piles et d'une bille porteuse permettant de maintenir le robot à plat.
Tout d'abord, nous étions censés programmer un robot suiveur de ligne grâce à des capteurs photo-transistors. Cependant, les capteurs n'étant pas fiable, le robot était dans l'incapacité de suivre une ligne noire. Donc, nous avons décidés de remplacer les capteurs par une manette Nunchuk permettant de gérer la direction du robot.
Dans les prochaines parties, nous allons expliquer la programmation des différentes parties du robot.
Programmation des moteurs PmodHB5
Création du composant HB5RefComp qui s'introduit dans l'architecture de l'io.
component HB5RefComp
port( ck : in STD_LOGIC; -- system clock (50MHz)
bitDirIn : in STD_LOGIC; -- User direction request
vecDfIn : in STD_LOGIC_VECTOR(7 downto 0); -- Duty factor value
bitEnOut : out STD_LOGIC;
-- Enable pin for Pmod HB5
bitDirOut : out STD_LOGIC); -- Dir pin for Pmod HB5
end component ;
Les lignes de code sont placées dans le process de l'iowr. Le port B gère le rapport cyclique du moteur gauche. Le port C gère le bit directionnel des 2 moteurs. Enfin le port D gère le rapport cyclique du moteur droit.
when X"38" => -- PORTB
s_vecDfIND <= I_DIN;
--L_LEDS <= not L_LEDS;
when X"35" => -- PORTC
s_bitDirING <= I_DIN(0);
s_bitDirIND <= I_DIN(1);
when X"32" => -- PORTD
s_vecDfING <= I_DIN;
Les signaux servent à transmettre les informations du composant HB5RefComp à l'io.
--moteur
signal s_vecDfIND : std_logic_vector( 7 downto 0);
signal s_vecDfING : std_logic_vector( 7 downto 0);
signal s_bitDirIND : std_logic;
signal s_bitDirING : std_logic;
Ceci est le port map du moteur droit du robot. Les bits (bitDirIn,bitDirOut,bitEnOut et vecDfIn) sont envoyés dans l'architecture de l'io grâce à des signaux (s_bitDirIND, DirOutD, EnOutD, s_vecDfIND) que l'on a créé qui font office de fil.
commandeD : HB5RefComp
port map( ck => I_CLK,
bitDirIn => s_bitDirIND,
bitDirOut => DirOutD,
bitEnOut => EnOutD,
vecDfIn => s_vecDfIND);
Programmation de la manette Nunchuk
Création du composant nunchukTop qui s'introduit dans l'architecture de l'io.
component nunchukTop
port( sys_clk : IN std_logic;
Init : IN std_logic;
readFIFO,i_continue : in std_logic;
O_data_nunchuk : out std_logic_vector(7 downto 0);
data_present, full, half_full : out std_logic;
sda : INOUT std_logic;
scl : INOUT std_logic);
end component;
commande Nunchuk du composant NunchukTop créé.
commandeNunchuk : nunchukTop
port map( O_data_nunchuk => s_odata,
data_present => s_datap,
full => s_full,
i_continue => s_icontinue,
readFIFO => s_readFIFO,
half_full => s_hf,
sys_clk => I_CLK,
Init => I_CLR,
sda => sda,
scl => scl);
Voici les lignes de commande qui permettent de commander la manette Nunchuk.
iord: process(I_ADR_IO, I_SWITCH,
U_RX_DATA, U_RX_READY, L_RX_INT_ENABLED,
U_TX_BUSY, L_TX_INT_ENABLED)
when X"23" =>
Q_DOUT <= s_odata(7 downto 0);
when X"56" =>
Q_DOUT <= "00000" & s_datap & s_hf & s_full;
iowr: process(I_CLK)
when X"56" =>
s_icontinue <= I_DIN(0);
Programme C
Ce programme est composé d'une boucle qui gère les différentes directions du robot. Nous avons fait quelques calculs pour déterminer à quelle position il fallait commencer à tourner droite ou à gauche ou encore avancer ou reculer. Il y a 4 sous-programmes qui gèrent le mode marche en avant, en arrière, à droite ou à gauche et un sous-programme qui lit les données envoyées par la manette Nunchuk.
- Calibrage Joystick analogique :
-gauche à fond : 0x20 -droite à fond : 0xE1 -repos : 0x7F -avant à fond : 0xE0 -arrière à fond : 0x25 -repos : 0x80
//#include "stdint.h"
#include "avr/io.h"
//#include "avr/pgmspace.h"
#undef F_CPU
#define F_CPU 25000000UL
#include "util/delay.h"
#define DATAPRESENT 2
#define PORTA _SFR_IO8(0x1B)
void readFIFOandAskForNextData(unsigned char nunchukDATA[]);
void enAvant(unsigned char droite, unsigned char gauche);
void enArriere(unsigned char droite, unsigned char gauche);
void tourneDroite(unsigned char gauche);
void tourneGauche(unsigned char gauche);
int main(int argc, char * argv[])
{
unsigned char nunchukDATA[6],vitesseDroite,vitesseGauche,avant;
while(1){
readFIFOandAskForNextData(nunchukDATA);
if (!(nunchukDATA[5] & 1)) {// Z button
if (nunchukDATA[1]>0xA0) {vitesseDroite=vitesseGauche=nunchukDATA[1]-0x80; avant=1;}
else if (nunchukDATA[1]<0x60) {vitesseDroite=vitesseGauche=0x80-nunchukDATA[1];avant=0; }
else {vitesseGauche=vitesseDroite=0x00;}
if (nunchukDATA[0]>0xA0) vitesseDroite = vitesseDroite -nunchukDATA[0]+0x80;
else if (nunchukDATA[0]<0x60) vitesseGauche = vitesseGauche + nunchukDATA[0]-0x80;
if (avant) enAvant(vitesseGauche,vitesseDroite); else enArriere(vitesseGauche,vitesseDroite);
} else if (!(nunchukDATA[5] & 2)) {// C button
if (nunchukDATA[2]>0xB0) tourneDroite(0x55);
else if (nunchukDATA[2]<0x55) tourneGauche(0x55);
else if (nunchukDATA[3]>0x90) enAvant(0x55,0x55);
else if (nunchukDATA[3]<0x80) enArriere(0x55,0x55);
else enAvant(0x00,0x00);
}
else enAvant(0x00,0x00);
PORTA = vitesseGauche;
_delay_ms(100);
}
return 0;
}
void readFIFOandAskForNextData(unsigned char nunchukDATA[]){
unsigned char i;
while(!(TWCR & (1<<DATAPRESENT))); // attente données dans le FIFO
for (i=0;i<6;i++) {//lecture du Nunchuk du FIFO dans tableau
nunchukDATA[i]=TWDR;
//PORTA=TWDR;
}
// on demande une nouvelle lecture par le pérphérique : toutes données seront pour la prochaine fois
TWCR |= (1<<0); //depart
TWCR &= ~(1<<0);// arret pour attendre la prochaine fois : voir machine d'états pour comprendre
}
void enAvant(unsigned char droite, unsigned char gauche) {
// par sécurité toute fonction commence par l'arrêt des moteurs
PORTD = 0x00;
PORTB = 0x00;
_delay_ms(10);
// les deux moteurs en marche avant
// choix de la direction : en avant : voir code iotimer.vhd
PORTC = 0x02;
PORTD = droite;
PORTB = gauche;
}
void enArriere(unsigned char droite, unsigned char gauche) {
// par sécurité toute fonction commence par l'arrêt des moteurs
PORTD = 0x00;
PORTB = 0x00;
_delay_ms(10);
// les deux moteurs en marche arriere
// choix de la direction : en avant : voir code iotimer.vhd
PORTC = 0x01;
PORTD = droite;
PORTB = gauche;
}
void tourneDroite(unsigned char gauche) {
// par sécurité toute fonction commence par l'arrêt des moteurs
PORTD = 0x00;
PORTB = 0x00;
_delay_ms(10);
// les deux moteurs en marche avant
// choix de la direction : en avant : voir code iotimer.vhd
PORTC = 0x02;
PORTB = gauche;
}
void tourneGauche(unsigned char droite) {
// par sécurité toute fonction commence par l'arrêt des moteurs
PORTD = 0x00;
PORTB = 0x00;
_delay_ms(10);
// les deux moteurs en marche avant
// choix de la direction : en avant : voir code iotimer.vhd
PORTC = 0x02;
PORTD = droite;
}
Voir aussi
Ce chapitre rédigé par les étudiants est complété dans un livre de la Wikiversité rédigé par l'enseignant tuteur :