Documente Academic
Documente Profesional
Documente Cultură
Carles Agorreta
Alfonso Arranz
Ramon Estalella
Jesús Fuente Porta
Proyecto Integrador
Contenido
1 Introducción ........................................................................................................... 1
2 Proyecto ................................................................................................................. 1
2.1 Descripción ............................................................................................................. 1
2.2 Estado del proyecto ................................................................................................ 2
2.3 Componentes ......................................................................................................... 4
2.3.1 Interfície CAN a nivell de Hardware ............................................................................................... 4
2.3.2 Placa dsPICDEM MC1...................................................................................................................... 5
2.3.3 Driver motor de Baja Potencia ....................................................................................................... 5
2.3.4 Motor Hurt ..................................................................................................................................... 5
3 MATLAB ................................................................................................................. 6
3.1 Vehicle Network Toolbox ........................................................................................ 6
3.2 Arxius DBC .............................................................................................................. 6
3.3 Interfície gràfica ...................................................................................................... 8
3.4 Interfície gràfica ...................................................................................................... 9
3.4.1 Consideracions a l’hora de fer la interfície ................................................................................... 12
3.4.2 Funcionament del codi de l’aplicació ........................................................................................... 13
1 Introducción
En esta última entrega vamos a intentar omitir el máximo posible el proceso seguido,
partes modificadas, etc. que ya se ha explicado en los otros informes. Nos vamos a
centrar en los resultados obtenidos, como ha quedado cada una de las partes y como
queda el conjunto.
2 Proyecto
2.1 Descripción
Estas dos placas están montadas con un dsPIC30F y un dsPIC33F, y tienen soporte CAN
incorporado, es decir, no necesitan ni controlador ni transceptor externo. La placa de
potencia dsPICDEM MC1 sirven para controlar el motor BLDC, mientras que el
microcontrolador que gestiona el control y la comunicación CAN de este nodo es el
dsPIC30F. Como el bus CAN es un bus multi-master que tiene su fuerte respecto a otros
buses en la facilidad para añadir nodos al bus, hemos conectado un dsPIC33F que
controla unos LEDs.
Se podría decir que cada nodo del bus equivaldría a una ECU de un coche. Estos pueden
comunicarse entre ellos con facilidad. Las ventajas de uso de un bus CAN y sus
características se mencionan en el apartado 4.
Página 1 de 66
Proyecto Integrador
• Hemos conseguido enviar y recibir datos en el bus can con el dsPIC30F, que es el
mismo microcontrolador que usamos para controlar el motor BLDC. Ya hemos
conseguido también hacer funcionar el bus CAN con Matlab con el dsPIC30F.
• El envío y recepción de los parámetros del motor mediante CAN todavía falta
hacerlo funcionar correctamente.
Conexiones de las Fases del motor Brushless DC con las salidas del módulo de baja
potencia de Microchip:
Fase C
Fase A
Fase B
Página 2 de 66
Proyecto Integrador
Verde Tierra
R Fase C
Y Fase B
B Fase A
En el código de ejemplo, podemos controlar el duty cycle (y por tanto las rpm) del motor
mediante un potenciómetro y una lectura del ADC.
Ahora nos falta hacer funcionar el motor con el código PID desarrollado (idealmente
determinar las constantes con el método de Ziegler-Nichols) y enviar y recibir los
parámetros a través del CAN, para lo cual ya hemos implementado un código closed-
source. En un principio, exploramos la posibilidad de implementar en CAN OBD2, que es
un protocolo de alta capa según el modelo OSI, en el que básicamente enviamos los
mensajes a través de CAN con unas normas públicas, que son las mismas que usan los
automóviles en su puerto OBD2, que es un puerto de diagnóstico que, por ejemplo, a
partir de este año se va a usar para las pruebas de la ITV o que ya se usa en los talleres
de automóviles para detectar averías. Incluso es posible a través de este puerto no solo
leer la comunicación CAN, sino también transmitir al bus CAN, ya que las
comunicaciones dentro de este BUS en los automóviles no están cifradas. De este modo,
algunas empresas externas (3rd parties) han conseguido implementar funciones de
autoconducción en automóviles.
Página 3 de 66
Proyecto Integrador
2.3 Componentes
Utilitzem el Kvaser per a fer d’interfície entre l’ordinador i la resta de dispositius els
quals volem fer comunicacions amb protocol CAN amb ells. Això és degut a que
l’ordinador no permet la connexió
Taula 1.
Página 4 de 66
Proyecto Integrador
Podem comprovar amb la Figura 2 que efectivament haurien de ser compatibles sense
cap adaptador
Página 5 de 66
Proyecto Integrador
3 MATLAB
La Vehicle Network Toolbox (VNT) és una eina de MATLAB que facilita treballar amb
busos CAN. De les “key features” que trobem a la pàgina web de MATLAB:
• MATLAB functions for transmitting and receiving CAN and J1939 messages
• Vector CAN database (.dbc) file and A2L description file support
• Signal packing and unpacking for simplified encoding and decoding of CAN
messages and J1939 parameter groups
• Vehicle CAN Bus Monitor app to configure devices and visualize live CAN network
traffic
Bàsicament podrem usar funcions ja fetes per MATLAB que ens permetran tractar la
creació de missatges CAN, creació de canals (virtuals o que comuniquin a un hardware
extern), enviament i recepció de missatges CAN, monitorització de canals CAN.
Són arxius de bases de dades que faciliten el crear, enviar i rebre missatges.
Hem trobat la informació de com editar manualment els fitxers .DBC per tal de poder
usar missatges personalitzats: http://socialledge.com/sjsu/index.php/DBC_Format.
Explicarem un missatge creat per nosaltres per tal de deixar-ho més clar:
• “Blink” és el nom del missatge, es pot usar dins de MATLAB per a crear un
missatge d’aquest tipus i usar-lo.
• “0|1”: el “0” indica el bit d’inici i “1” indica la mida de la senyal en bits.
• El que està entre cometes en el codi original és les unitats, en aquest cas com
que la senyal només la usem per a encendre o apagar els intermitents
d’emergència, ho indiquem amb “0/1”.
• “Intermitents” és el receptor.
Les dades del missatge quedarien tal com es veu a continuació en la Figura 5.
Figura 5
Página 7 de 66
Proyecto Integrador
Per a la interfície gràfica també usem eines de MATLAB. Teníem dos opcions, GUIDE
(Graphical Interface Development Enviroment) i App Designer. Aquest últim és una eina
més nova i té més opcions a l’hora de posar elements gràfics, i semblava més fàcil de
treballar. Així que ens vam decantar per a usar l’App Designer.
Ja havent acabat el desenvolupament de l’aplicació amb l’app Designer hem sembla una
eina molt fàcil d’usar si es tenen coneixements de programació en MATLAB i es busca en
l’extensa documentació el que un no sap fer. Si s’ha de fer una tasca repetidament que
involucri els mateixos o similars càlculs amb diferents paràmetres d’entrada una molt
bona opció és programar-se una eina amb l’App Designer.
Sembla bastant ineficaç i sense sentit adjuntar el codi generat amb l’AppDessigner o
comentar-lo detalladament, ja que la manera de visualitzar-lo a través del MATLAB és
molt més còmoda i amena. El codi s’ha intentat que estigues el millor comentat possible
així que fent a la memòria final una explicació general del funcionament i amb els
comentaris no hauria d’haver cap problema si en el futur algú vol entendre/usar el codi
de MATLAB creat en aquest treball.
En principi la interfície gràfica ens permetrà interactuar amb tots els elements instal·lats
en el bus CAN, inclòs el motor del qual s’encarrega l’altre grup (el que representaria la
tracció del vehicle).
Per al motor hem parlat amb l’altre grup i ens han dit que poden implementar és el %
del cicle de treball que li apliquen al motor, per tant estaria bé implementar un element
gràfic que representés l’accelerador d’un vehicle.
Si implementessin un control, podríem tenir una opció de velocitat creuer que ens
permetés mantenir una velocitat fixa sense haver l’usuari de regular la potencia
constantment a través del panell.
També ens poden enviar per comunicacions CAN la velocitat de gir del motor. Per tant
seria fàcil posar un indicador d’aquesta velocitat, o bé extrapolar la velocitat lineal
suposant un lliscament roda-calçada nul i mostrar aquesta última.
Si ells poguessin mesurar paràmetres com la potència que està absorbint en cada
moment el motor, estat de la bateria.... podríem complimentar amb altres indicadors
que mostressin la potència que revés el motor en el temps i l’estat de la bateria.
A més amb la informació recopilada hem pensat que seria interessant intentar fer
gràfiques de velocitat-temps, distància recorreguda, energia usada... . També estaria bé
implementar una funció per a guardar aquestes dades i poder analitzar-les més tard. En
MATLAB això no hauria de ser gaire problema ja que es podrien guardar quan l’usuari de
Página 8 de 66
Proyecto Integrador
la nostra aplicació volgués en un fitxer de dades de MATLAB (.mat) i tenir una aplicació o
script que permetés analitzar les dades de la manera desitjada.
En quant als altres elements que implementarà el nostre grup, hem pensat usar diversos
LEDs de les plaques que tenim per tal de simular llums, intermitents etc.. Per tant hem
afegit al panell de control els elements necessaris per a controlar i indicar els estats
d’intermitents i llums externs del vehicle.
El motor, teòricament amb les llibreries creades en C pot controlar-se (des de la placa de
Microchip) en mode llaç tancat de velocitat o posició de manera senzilla. Per a donar un
us realista des de el punt de vista de la aplicació en MATLAB pensarem que es una
finestra o un seient del qual controlem la posició. Aquesta posició de consigna es
representarà entre 0 i 100, que serà el valor que la placa de Microchip rebrà per CAN. La
posició real també s’enviarà per CAN de la placa de Microchip a la aplicació de MATLAB
per tal de mostrar aquesta informació a l’usuari.
Tenim dues pestanyes separades una per al panell de control i l’altra per a la
visualització de les dades recopilades. A la Figura 6 podem veure la pestanya “panell” i a
la Figura 7 podem veure la pestanya “Dades”.
Página 9 de 66
Proyecto Integrador
A la part inferior dreta tenim el control dels llums i intermitents. Hi ha tant els selectors
de en quin estat es vol tenir els llums i intermitents com els indicadors que indiquen si
està un llum encès o no.
A l’esquerra tenim el que seria l’accelerador que mentre modifiquem la seva posició va
actualitzant el mòdul de control de potència del motor de tracció per tal de que aquest
proporcioni més o menys energia a la tracció. També hi ha l’opció programada per a
enviar una consigna de velocitat i deixar que el mòdul de tracció intenti seguir-la. El
comportament d’aquesta segona part consisteix en que primer s’ha de seleccionar
mode creuer i després seleccionar la velocitat desitjada, aquesta velocitat pot canviar-se
en qualsevol moment. Si es mou l’accelerador es surt del mode de velocitat creuer i
s’entra una altra vegada en mode de control de la tracció a través de l’accelerador.
Página 10 de 66
Proyecto Integrador
“Rest” el que fa és tornar a inicialitzar tots els vectors on guardem les dades i elimina el
que hi havia representat a les gràfiques.
“Save” crea un fitxer de dades de MATLAB i guarda en ell totes les dades recopilades fins
el moment. El nom del fitxer és la data i hora en la que s’han guardat les dades.
Página 11 de 66
Proyecto Integrador
A l’hora de fer la interfície s’ha intentat posar èmfasi en fer un codi que robust, de
manera que independentment del que faci l’usuari l’aplicació no deixi de funcionar.
També s’ha intentat comentar el més bé possible i de forma entenedora.
Un punt que hem va ser una mica problemàtic és que tal com s’havia plantejat la
recepció de missatges de CAN amb el MATLAB, cada vegada que es rep un missatge
hauria de cridar-se una funció en concret. En principi aquesta funció només estava
pensada per a processar la informació d’un sol missatge, però hem vaig fixar que a
vegades quan s’entrava a la funció de recepció ja hi havia més d’un missatge. Llavors es
va adaptar el codi per a funcionar diferent si havien arribat més d’un missatge i intentar
mitigar els problemes que això podia suposar, com per exemple perdre informació de la
velocitat en diversos enviaments, per al velocímetre només suposaria un retard puntual
però per a mesurar la posició suposaria un error que s’aniria acumulant. En principi això
s’ha arreglat del tot i no hauria de ser un problema ni empitjorar el funcionament del
programa, de totes maneres quan succeeix s’avisa amb un missatge per el “command
window” de MATLAB.
Página 12 de 66
Proyecto Integrador
Evidentment el bus CAN i el Kvaser són la part física. El filtre ja està dins de l’aplicació en
MATLAB.
Quan es rep un missatge salta una interrupció i s’executa una funció de recepció.
Aquesta en funció del missatge rebut executa un codi o un altre i tracta cada missatge
de forma diferent. Principalment té dues funcions, actualitzar els indicadors amb les
noves dades rebudes i guardar les dades que sabem que l’aplicació necessitarà
internament més endavant, com per exemple les de la velocitat que es guarden per a
poder mostrar les gràfiques i si es vol l’usuari pot triar guardar-les en un fitxer extern
per a posterior anàlisi.
El Reset eliminarà les dades guardades fins al moment, deixarà les variables
inicialitzades tal com estaven al principi de l’execució de l’aplicació per tal que la resta
del codi no tingui cap problema i borarà les gràfiques.
El botó de Start/Stop simplement inicia o para el timer que crida a la funció d’actualitzar
les gràfiques.
Página 13 de 66
Proyecto Integrador
Els efectes de la interfície gràfica com els indicadors de que tenim els llum encesos són
molt senzills i es fan en el mateix “callback”. Els indicadors dels intermitents es fan tant
en el “callback” com en una funció cridada per un timer que ens permet indicar que
s’encenen i apaguen periòdicament. De totes maneres per a no enviar més missatges
del necessari pel bus CAN, qui s’encarrega de fer la temporització dels intermitents reals
és el mòdul que els controla (en el nostre cas una placa Microchip).
En la Figura 9 podem veure un codi que simplement fa l’envio d’un missatge can en funció de
com s’ha tocat l’element gràfic a la interfície. En canvi a laFigura 10 es veu un codi que també
actualitza un indicador.
Página 14 de 66
Proyecto Integrador
4 Comunicación CAN
4.2 Avantatges del bus CAN en comparació amb d’altres (SPI, Ethernet...):
Per una banda, els protocols de comunicació serial, UART, SPI i I2C estudiats son busos amb
limitacions de longitud del cable d’uns pocs metres (al voltant dels 5 metres) mentre que el
protocol Ethernet en xarxa local té una limitació de 30 metres. El bus CAN permet comunicació a
1 Mbit/s a una distància de 40 metres, fins a 50 kbit/s a una distància de 1000 metres.
Página 15 de 66
Proyecto Integrador
Una altra avantatge del bus CAN és l’estalvi de cablejat i facilitat de cablejat en comparació amb
altres busos de dades.
Exemple de funcionament de les dues línies del bus CAN, CAN_HIGH i CAN_LOW. Com és típic en
els busos de transmissió de dades, aquests dos cables van trenats per protegir-se del soroll, i
normalment van amb un cable apantallat, que és el que limita la velocitat màxima del bus CAN.
En estat dominant (0 lógic) es dóna una diferència de tensió de 1.5 V a 3 V entre les dues línies.
En estat recessiu (1 lògic) els dos cables es troben en voltatge en mode comú. Aquest bus, com
molts d’altres, funciona amb tensions diferencials per protegirse de tensions en mode comú a
massa que poden generar grans interferències.
Aquest gràfic mostra la trama sencera enviada per CAN. “Data” és el que nosaltres anomenem
“missatge”, però hem de tenir en compte que la trama total per missatge no és de 1 a 8 bytes,
sinó de 47 a 55 bytes. Si per exemple, a ull, volem enviar 10 missatges cada 10 ms on cada trama
és de 55 bytes, necessitaríem enviar 55 bytes / ms, osigui que necessitaríem com a mínim una
velotat d’enviament de dades en el bus CAN de 55 kbytes/s, que equivaldria a 440 kbits/s.
Página 16 de 66
Proyecto Integrador
Això sense tenir en compte l’enviament de dades en una xarxa més complexa, on els càlculs
serien més complexos.
1) High Speed CAN: soporta baud rates de 40 Kbit/s a 1Mbit/s, variant la distància del cable
(40 metres per 1 Mbit/s, 500 metres per 125 kbit/s).
Per a
El protocol CAN està basat en dues senyals compartides per tots els nodes de la xarxa, el CAN_H
i el CAN_L, que normalment estàn protegits per a que siguin robustos mecànicament i
apantallats per evitar interferències electromagnètiques, que proporcionen una senyal
diferencial que permet la detecció de col·lisió. Enmig dels dos cables es col·loca una resistència
de 120 ohms per obtenir una caiguda de tensió diferencial entre els dos cables. Com més alta
sigui aquesta resistència, menor serà la corrent necessària que hi passi, estalviant energia. Però
sense fer-la massa gran, perquè sinó la qualitat de la senyal disminueix massa, així que cal
arribar a una solució de compromís. Si les dues línies estàn en HIGH, significa que dos o més
nodes estan tractant d’enviar dos senyals diferents, és a dir, hi ha una col·lisió. Aquesta col·lisió
Página 17 de 66
Proyecto Integrador
Amb un cable típic de CAT5 s’obté un bon slew-rate de 50 nanosegons a 1 Mbit/s però si
augmentéssim la velocitat del bus l’slew-rate es dispararia.
També existeixen dos motius, primer per millorar la qualitat de la senyal (evitar l’efecte ringing) i
evitar reflexions d’alta frequencia (estudiades a equips electrònics). D’altra banda s’utilitza per
definir l’1 lògic, el valor recessiu.
El bus CAN és un bus industrial que transmet una senyal digital, 0 o 1’s. Per obtenir una senyal
definida (1 lògic) es necessita un resistor, en configuració pull-up o pull-down, depenent del
tipus de CAN implementat (SWC, LS-CAN...).
Per maximitzar la qualitat de la senyal, es necessari utilitzar un parell trenat per anular les
interferències electromagnètiques dels cables i apantallar-lo per protegir-lo del soroll extern. El
parell trenat assegura la mateixa longitud del cable, el que permet que la impedància sigui la
mateixa. El fet que la impedància sigui la mateixa és vital per una bona qualitat de la senyal.
Al final vam decantar-nos per implementar un protocol de comunicació ad hoc, per un tema de
simplicitat. Vam establir el protocol en una fulla excel per interaccionar el Matlab (PC) amb el
dsPIC30F (“ECU”).
Protocol OBDII
El port de diagnòstic OBD2 té també un connector propi, present a tots els cotxes. És possible
utilitzar el kvaser per comunicarse amb la xarxa CAN del cotxe a través del DATA LINK connector
amb un adaptador D-sub.
Página 18 de 66
Proyecto Integrador
El connector Data-Link també permet connectarse a altres busos de dades com el bus LIN.
Aquest és un esquema del model OSI adaptat al bus CAN, on s’organitza OBD2 i CAN.
OBD3 és un protocol en desenvolupament, que pretén connectar els cotxes sense fils (a partir
de 2018 les revisions de la ITV inclouran la revisió de les ECUs del cotxe, pel que la comunicació
amb CAN és necessària, però l’accés al bus CAN intern requereix de fils). Es presenten reptes per
a aquesta nova versió del protocol, com la seguretat, l’encriptació de les dades... Es pretén
connectar una xarxa CAN local amb internet. Les avantatges i desavantatges són òbvies:
permetria la monitorització en llargues distàncies dels cotxes, el control remot dels cotxes, però
el risc d’un error o un atac informàtic tiraria per sobre la robustesa del bus CAN.
Página 19 de 66
Proyecto Integrador
Aquest és un exemple pràctic de com funcionaria una comunicació CAN amb el protocol OBD2
implementat.
Quan volguéssim fer una requisició, s’hauria de transmetre un missatge amb l’identifier (ID) =
0x7DF, quan responguem, necessitaríem la ID = 0x7E8. Noti’s que la ID de requisició és més
petita que la de resposta, ergo té més 0, ergo és dominant en cas de col·lisió. Això té sentit,
perquè en cas de col·lisió no tindria sentit que hi hagués una resposta si no s’hagués fet cap
pregunta.
Primer de tot, tenir en compte que el missatge d’exemple es mostra en hexadecimals, dos
hexadecimals son 1 byte (8 bits).
El primer byte del missatge indica el nombre de bytes del missatge (OBD2 envia sempre un
missatge de 8 bytes, els utilitzi per envia informació o no, en cas de no utilitzar tots els bytes
disponibles, la resta de bytes es consideren “dummy loads”.)
El segon byte indica el mode de funcionament requerit. El mode 01 indica que es requereix que
es mostri la dada actual.
Els missatges es codifiquen tenint en compte els PIDs. El tercer byte indica el PID requerit, en
aquest cas, 0C indica que requereix la dada actual de les RPM del cotxe.
Página 20 de 66
Proyecto Integrador
Protocol Ad Hoc
Per tal de facilitar la comunicació dels nodes amb el bus CAN, es va descartar implementar el
protocol OBD2. Vam decidir fer un excel que estableix el nostre protocol propi de comunicació:
Per simplicitat, vam decidir enviar 1 byte de dades (un número de 0 a 255) per cada trama
(recordar, però, que cada trama enviada en CAN va de 47 bytes a 55 bytes, sent només variable
el tamany del missatge a enviar de 1 a 8 bytes). Utilitat i aplicacions del bus CAN: ECU’s en
automoció
El bus CAN, és l’estàndard en la indústria de l’automòvil, també en tancs militars i camions (però
cadascun amb estandards diferents).
Altres protocols que també estan basats en CAN a banda de l’anteriorment mencionat OBDII són
el J1939 (camions), el MilCAN (vehicles militars) el CANopen (automatització industrial).
En automoció, el bus CAN es com una medul·la espinal del cotxe que permet que tots els nodes
connectats a aquest bus estiguin comunicats. Cada ECU (unitat electrònica de control)
constitueix un node i pot dir la seva. En el nostre cas, tindríem 3 ECUS, l’ordinador amb Matlab
(que té la ID més baixa, i per tant té preferència per sobre de la resta), el dsPIC30F que controla
el motor i un altre dsPIC que controla les llums.
La ventatja del bus CAN es que permet connectar múltiples ECUs entre sí amb un cablejat simple
(només necessita el CAN_H, el CAN_L i massa). Això representa un estalvi important en cablejat,
que suposa una reducció en el pes del cotxe, el que fa augmentar l’eficiència del vehicle. Un
cotxe modern pot arribar a tenir 100 ECUs. Altres exemples d’ECUs són el control centralitzat de
finestres o els airbags.
Página 21 de 66
Proyecto Integrador
- Baix cost: els ECUs es comuniquen amb una única interfície CAN reduint error, pes i
cost.
- Bus centralitzat: Permet configurar tots els ECUs i permet centralitzar la diagnosis
d’errors (OBDII)
- Robustesa: Si un dels subsistemes falla, la resta contínua funcionant. Està protegit per
interferències electromagnètiques. No té problemes de col·lisió, ja que el seu protocol
està basat en missatges amb una ID jeràrquica que elimina aquest problema (que sí té
per exemple Ethernet, tot i ser més ràpid)
És a dir, que amb 500 Kbits/s, tenint en compte que una trama d’un missatge CAN ocupa com a
molt 440 bits, podríem enviar trames amb un període de 880 microsegons, és a dir, una
frequencia de 1.137 kHz (osigui es podria propagar, a ull, aproximadament 1 missatge cada
milisegon pel bus CAN a tots els nodes tenint en compte la latència, la resolució de col·lisions, el
filtratge local, etc).
Página 22 de 66
Proyecto Integrador
Per configurar el CAN en el dsPIC30F, vam consultar el manual del family datasheet a la Section
23 (veure referència) on s’explica què fa cada registre de configuració disponible. En el cas del
dsPIC33F i el PIC24F, hi ha codis d’exemple per fer funcionar ràpidament el CAN, però amb el
dsPIC30F el vam haver de configurar nosaltres mateixos.
Alhora d’ajuntar les rutines CAN del dsPIC30F amb el CAN del Matlab, s’ens van presentar dos
problemes. Un, que els bytes enviats i rebuts en el dsPIC30F s’enviaven i es rebien al revés, pel
que vam haver de dissenyar un algoritme per resoldre-ho. Més endavant, vam treure aquest
codi perquè només enviàvem i rebíem un sol byte, ja que rebíem una consigna del 0 al 100% (8
bits permet codificar de 0 a 255) i la tractàvem internament en el dsPIC30F.
Vam haver de fer una reconfiguració de la rutina IniCAN perquè la teníem en un mode
anomenat Remote Transmission Request (no havíem configurat el registre TXIDE directament).
Això creava problemes, perquè hi havia una incompatibilitat en la configuració que feia que el
bus CAN deixés de funcionar al cap d’una estona, ja que codificava les IDs amb extended, és a dir
29 bits en comptes dels 11 bits que havíem configurat (òbviament al haver establer una xarxa
molt simple amb 3 nodes no ens calia un protocol extended, que pot ser una solució òptima per
una xarxa de domòtica o en un entorn d’automatització industrial, per exemple).
Vam haver d’afegir a la transmissió (sortida) TXIDE = 0 per establir el CAN estandard (IDs a 11
bits) i SRR = 0 per desactivar el mode Remote Request.
Página 23 de 66
Proyecto Integrador
La seguent rutina serveix per enviar un missatge a la xarxa CAN. Envia un missatge amb Data de
1 byte codificat com a enter. Com ja s’ha mencionat, també s’ha provat d’enviar més bytes
alhora, resolent el problema dels bytes enviats creuats. Com que no ens calia enviar més
informació perquè hem volgut simplificar el procés, hem simplificat la rutina. Deixem per a
futurs projectes que vulguin implementar OBD2 el codi per enviar 8 bytes de dades, tot i que no
té més misteri que llegir la Section 23 del family datasheet, tractar les dades amb màscares i/o
desplaçaments de bit i enviar cada byte al seu registre corresponent (B1, B2, B3 i B4).
Página 24 de 66
Proyecto Integrador
Configuració del timer de la ISR (interrupció periòdica). Timer que s’activa cada 500 ms, que es
el període d’enviament desitjat. S’ha escollit aquest període per no saturar la capacitat de càlcul
del DSP, ja que si establíem una interrupció cada 1 ms (i enviar cada 500 execucions) només pel
fet d’interrompre masses cops per segon el programa, el DSP es saturava i el PID i la LCD de la
placa deixaven de funcionar correctament.
ISR que tracta el valor de la posició del motor del 0% al 100% (el PID està configurat per donar
10 voltes, i és controlar en graus, és a dir, va de 0 a 3600º). Osigui que el 50% significa que el
motor està en la posició 1800º, asumint un error degut a que la precisió és en graus i no s’envien
decimals, un error en contínua, un temps d’establiment, etc. També es va implementar el PID
per a control de velocitat.
Després tenim la gestió dels LEDs. Estàn definits a les capsaleres els registres corresponents a
cada LED. El programa funciona tal que el programa de Matlab pot encendre o apagar dos leds, i
apagar o fer-ne pampallugues amb els altres dos leds (aprofitant el timer de 500ms, és visible
Página 25 de 66
Proyecto Integrador
També tractem els leds parpadejant a led’s blinking, ja que necessitem que parpadejin,
reaprofitem la ISR del missatge periòdic, com hem dit.
Página 26 de 66
Proyecto Integrador
Per últim, la rutina que llegeix el missatge CAN. És similar a la rutina que envia CAN, ja que
només s’ha de rebre un missatge de 1 byte. El registre SID conté la ID del missatge, que és
guardat en una variable, tot i que es podria definir a la capsalera per fer el codi més eficient
(també es podria fer una rutina amb ensamblador, però això ho deixem per a futurs projectes).
Com estem treballant amb molts pocs nodes, no ens cal utilitzar un protocol de missatges
complex com l’OBDII. Si a la xarxa CAN tinguéssim 50 nodes diferents, cada ID hauria d’estar
codificada d’una manera que es pogués identificar qui està fent la petició i quina és la petició. En
el protocol OBD2 això es resol amb els Paramets IDs (PIDs no confondre amb el controlador!)
que estàn al primer byte del missatge, és a dir a B1 (no confondre missatge que en aquest cas
ens referim a les dades amb la trama total del missatge, que inclou tota la configuració de nivell
baix amb la que no hem treballat al projecte, més que a la recerca indicada al comensament de
l’article).
Página 27 de 66
Proyecto Integrador
Página 28 de 66
Proyecto Integrador
Con el fin de simplificar la programación se ha optado por crear una librería que recoja
las funcionalidades necesarias de la placa de demostración dsPICDEM MC1.
• Estructura: MOTOR_INFO;
Esta estructura almacena la información básica del motor. Sus miembros son solo de
lectura y se actualizan en las funciones que se describen más adelante:
• Speed: Velocidad del motor en RPM calculada mediante el encoder del motor.
Puede coger valores positivos o negativos indicando el sentido del giro
• Position: Posición del motor en grados calculada mediante el encoder del motor.
Puede coger valores positivos o negativos indicando el sentido del giro
• Dcycle: Valor del duty-cycle en %. Puede coger valores positivos o negativos
indicando el sentido del giro
• Ecount: Pulsaciones totales generadas por el Encoder en un mismo sentido de giro.
Este valor sirve como base para el cálculo de la posición y puede coger valores
positivos o negativos indicando el sentido del giro.
• Running: 1 Indica si el motor está en marcha, 0 si está parado.
• Fault: 1 Indica si el driver del motor está en fallo, 0 si está correcto.
• Direction: 1 Indica si el motor gira en modo inverso, 0 si es en directo.
Se encarga de configurar los puertos como entrada o salida adecuando su estado a los
elementos de la placa conectados en él. Además, asegura que los puertos configurados
como salida se inicializan sin tensión.
Página 29 de 66
Proyecto Integrador
La placa dispone de 4 pulsadores identificados como S4, S5, S6 y S7. Esta función se
encarga de inicializar las interrupciones asociadas a estos pulsadores.
La lectura del valor se puede hacer mediante la función macro VR2Value() la cual
devuelve el valor del potenciómetro en %.
Donde FCY es la frecuencia de trabajo del procesador (en nuestro caso configurado a
14.75MHz). Así por ejemplo tendríamos que un retardo aproximado de 20 ms vendría
expresado por:
Página 30 de 66
Proyecto Integrador
Página 31 de 66
Proyecto Integrador
Estos comandos se codifican en un byte que pueden ser pasados a la función a través de
su parámetro cmd. En el manual del driver Hitachi HD44780 se puede encontrar la lista
completa de instrucciones disponibles.
Se encarga de escribir un comando en el registro de datos del LCD. Este dato representa
el código de carácter a escribir en la posición actual del cursor, según una tabla que
guarda el driver en su memoria ROM interna.
Página 32 de 66
Proyecto Integrador
Se encarga de borrar el contenido del LCD y posicionar el cursor al inicio. Esta función
ejecuta la escritura del registro de instrucciones descrito anteriormente con los
comandos adecuados para realizar estas acciones.
El texto es pasado a la función como un puntero al primer carácter del array. Esta
función ejecuta la escritura del registro de datos descrito anteriormente enviando
secuencialmente cada uno de los caracteres que forman el array.
Para dar formato al número, se tiene que especificar el número máximo de caracteres
que lo representará (len), y cuantos decimales se tienen que mostrar (dec). Hay que
tener en cuenta a la hora de definir la longitud máxima (len), que el símbolo decimal “.”
representa un carácter y por lo tanto ocupa una posición.
Página 33 de 66
Proyecto Integrador
5.4.1 Inicialización del sensor de posición del eje del motor (Hall)
Esta función se encarga de inicializar las interrupciones asociadas a las tres entradas
donde están conectados los sensores Hall que indican la posición del eje del motor en
cada momento.
• Llamada: OutMngr();
Mediante la lectura de la posición del eje del motor gracias a los sensores Hall, calcula la
siguiente posición de los seis transistores que alimentan al motor.
La función se encarga de unificar en un solo valor el estado de las 3 entradas de Hall
mediante la función macro: #define HallValue() (PORTD & 0x0700) >> 8;
Este valor representará el índice dentro del array de posiciones para ambos sentidos de
giro que será escrito en el registro de control del PWM.
Página 34 de 66
Proyecto Integrador
Esta función se encarga de inicializar el control PWM del motor integrado en el dsPIC. La
frecuencia de trabajo viene establecida en la constante FPWM siendo de 16KHz según
recomendaciones del manual para el driver de potencia del motor.
El control PWM modula solo los transistores de la alimentación positiva, dejando los de
la parte negativa fijos según la posición definida por la función OutMngr() descrita en el
apartado anterior.
El control es inicializado para desactivar todas sus salidas ante una señal de fallo del
driver. Esta señal que llega al dsPIC a través de su entrada FLTA genera una interrupción
la cual puede ejecutar una función asociada definida en el parámetro
void(*AttnFunction)() .
Señalar que esta señal de FLTA se puede activar también mediante el pulsador TRIP
disponible en la placa, permitiendo así simular fallos.
Esta función se encarga de arrancar el control PWM del motor integrado en el dsPIC
siempre que no exista una señal de fallo en la entrada FLTA.
Esta función se encarga de parar el control PWM del motor integrado en el dsPIC.
Esta función se encarga de enviar la señal de rearme de fallo al driver del motor durante
50 ms. El driver tiene protecciones que gestionan directamente posibles fallos del motor
o de la línea. Estos fallos son indicados a través de la señal FLTA provocando que el
driver permanezca desactive la alimentación del motor hasta recibir la señal de rearme.
Página 35 de 66
Proyecto Integrador
Esta posibilidad facilita la regulación de la posición del motor en lazo cerrado mediante
un control PID.
Donde RPM son las máximas del motor, y IPR las interrupciones por revolución del
encoder. De esta manera y seleccionando el filtro adecuado dentro del rango disponible
en el dsPIC, podemos evitar contar pulsos inferiores a 6.5us.
Esta función además, habilita la detección de paso por cero que se genera cada vez que
el contador interno pasa por este punto en cualquier sentido de giro. Este paso por cero
provoca una interrupción la cual puede ejecutar la función asociada definida en el
parámetro void(*AttnFunction)().
Página 36 de 66
Proyecto Integrador
El temporizador está configurado está configurado en 500 ms. El cual provoca una
interrupción la cual puede ejecutar la función asociada definida en el parámetro
void(*AttnFunction)().
Página 37 de 66
Proyecto Integrador
Como parte del diseño se pretende poder controlar el motor en lazo abierto y en lazo
cerrado. En este último caso, se pretende además poder tener el control por velocidad o
por posición del eje.
• Estructura: PID_DATA;
Página 38 de 66
Proyecto Integrador
Página 39 de 66
Proyecto Integrador
Señalar que la estrategia de control se basa en un PID simple, y por lo tanto los
controles por velocidad y posición son incompatibles entre ellos. Por lo tanto el control
predominante será el último que se inicialice. Es decir, solo se ejecutará el algoritmo PID
de la última función InixxxxLoop(); llamada.
Destacar además, que por las inercias del motor, se ha limitado la salida máxima del PID
de posición a ±8% consiguiendo así un sobrepico prácticamente nulo y una mayor
precisión en la posición final.
Página 40 de 66
Proyecto Integrador
7 Librería CAN
Como parte del diseño se pretende poder controlar el motor en lazo abierto y en lazo
cerrado. En este último caso, se pretende además poder tener el control por velocidad o
por posición del eje.
Lee el buffer de entrada del puerto CAN1 4 bytes y devuelve un dato de tipo float.
Lee el buffer de entrada del puerto CAN1 4 bytes y devuelve un dato de tipo Long.
Lee el buffer de entrada del puerto CAN1 2 bytes y devuelve un dato de tipo Int.
Lee el buffer de entrada del puerto CAN1 1 bytes y devuelve un dato de tipo Char.
Página 41 de 66
Proyecto Integrador
Se encarga de enviar a través del puerto CAN1 un dato de tipo float con una longitud de
4 bytes. La función, además del dato a enviar espera el parámetro SID que representa el
identificador standard (11 bits) de mensaje.
Se encarga de enviar a través del puerto CAN1 un dato de tipo long con una longitud de
4 bytes. La función, además del dato a enviar espera el parámetro SID que representa el
identificador standard (11 bits) de mensaje.
Se encarga de enviar a través del puerto CAN1 un dato de tipo int con una longitud de 2
bytes. La función, además del dato a enviar espera el parámetro SID que representa el
identificador standard (11 bits) de mensaje.
Se encarga de enviar a través del puerto CAN1 un dato de tipo char con una longitud de
4 bytes. La función, además del dato a enviar espera el parámetro SID que representa el
identificador standard (11 bits) de mensaje.
Página 42 de 66
Proyecto Integrador
8 Rutina principal
La rutina principal se apoya en las librerías descritas anteriormente para conseguir una
estructura muy simplificada. Aunque no se ha utilizado todo su potencial, permite
mostrar las posibilidades que ofrecen.
Para ello se ha generado un código muy sencillo por el que se controla la posición de un
motor entre 0 y 3600º. La consigna del algoritmo PID es recibida a través de puerto
CAN1, y a su vez la posición real es enviada a intervalos de 500ms.
El código cuenta además con interface por el cual se muestra a través del display LCD los
siguientes datos en tiempo real:
s xxxx.x o xxx.x
posic: xxxx.x g
Página 43 de 66
Proyecto Integrador
9 Anexos
//Frecuencia trabajo
#define FCY 14745600 // xtal = 7.3728 Mhz; PLLx8 FCY=Fosc/4
//LEDs
#define LED_D6 LATAbits.LATA9
#define LED_D7 LATAbits.LATA10
#define LED_D8 LATAbits.LATA14
#define LED_D9 LATAbits.LATA15
//Pulsadores
#define PBUTTON_S4 PORTGbits.RG6
#define PBUTTON_S5 PORTGbits.RG7
#define PBUTTON_S6 PORTGbits.RG8
#define PBUTTON_S7 PORTGbits.RG9
//LCD
#define LCDRS LATCbits.LATC3 //LCD Reg. Select
#define LCDENA LATDbits.LATD13 //LCD enable data
#define LCDRW LATCbits.LATC1 //LCD Read/write
#define LCDD0 PORTDbits.RD0 //LCD DB4
#define LCDD1 PORTDbits.RD1 //LCD DB5
#define LCDD2 PORTDbits.RD2 //LCD DB6
#define LCDD3 PORTDbits.RD3 //LCD DB7
// Retardos LCD aplicables a funcion wait()
#define Delay_20ms (FCY * 0.020000/14)
#define Delay_5ms (FCY * 0.005000/14)
#define Delay_10ms (FCY * 0.010000/14)
#define Delay_1ms (FCY * 0.001000/14)
#define Delay_200us (FCY * 0.000200/14)
#define Delay_5us (FCY * 0.000005/14)
//HALL
#define CAP1 PORTDbits.RD8
#define CAP2 PORTDbits.RD9
#define CAP3 PORTDbits.RD10
//PWM
#define FPWM 16000 //Fecuencia PWM 16 KHz
#define PHA1L LATEbits.LATE0
#define PHA1H LATEbits.LATE1
#define PHA2L LATEbits.LATE2
#define PHA2H LATEbits.LATE3
#define PHA3L LATEbits.LATE4
#define PHA4H LATEbits.LATE5
#define DRVFAIL !PORTEbits.RE8 //Fallo Driver
#define DRVRST LATEbits.LATE9 //Reset Fallo Driver
#define PFC LATDbits.LATD5
#define Delay_RST (FCY * 0.05/14) //Retardo Reset Driver
//ENCODER
#define PPR 250.0 //Pulsos por revolución
#define RPMMAX 4000.0 //RPM máximas (calc veloc)
#define IPR 1000.0 //Interrup. por revolución
#define SPESTM 60.0/RPMMAX //Tiempo muestreo velocidad (seg)
#define LED_FR LATDbits.LATD7 //LED FWD/REV
#define QEINDX PORTBbits.RB3 //INDX
#define QEA PORTBbits.RB4 //QEA
#define QEB PORTBbits.RB5 //QEB
// MACROS
#define VR2Value() (ADCBUF0*100.0)/1023.0; //ADC en %
#define HallValue() (PORTD & 0x0700) >> 8;
// TIPOS
typedef struct {
float speed, //Velocidad del motor
position, //Posición del motor
dcycle; //Valor duty-cycle
long ecount; //Contaje Encoder
char running, //Motor activo
direction, //Sentido de giro del motor
fault; //Driver en fallo
}MOTOR_INFO; //Guarda Info del motor
Página 44 de 66
Proyecto Integrador
//LCD
void IniLCD(); //Inicializa LCD (Driver HD44780)
char BusyLCD(); //Estado ocupado/libre del LCD
void WriteCmdLCD(char cmd); //Escribe un comando en elregistro de instrucciones del LCD
void WriteDataLCD(char data); //Escribe un comando en el registro de datos del LCD
void DeleteLCD(); //Borra el LCD
void PosLCD(char col,char row); //Posiciona curson LCD en columna, fila
void WriteTxtLCD(char *buffer, char col, char row); //Escribe texto en LCD
void WriteNumLCD(float number,int len,int dec,char col,char row); //Escribe numero en LCD
//MOTOR
void IniHall(void(*AttnFunction)()); //Inicializa sensor Hall
void IniPWM(void(*AttnFunction)()); //Inicializa control PWM
void OutMngr(); //Gestiona la activacion de las
salidas al motor
void StartPWM(); //Arranca PWM
void StopPWM(); //Para PWM
char RstDriver(); //Reset Fallo Driver
void SetDC(float val); //Escribe valor Duty-Cycle
//ENCODER
void IniQEI(void(*AttnIndxFunc)()); //Inicializa Encoder
void PosUpdate(); //Actualiza posicion Absoluta
void RstQEI(); //Reset posicion inicial Encoder
//AUXILIAR
void iniTimerBlink(void(*AttnFunction)()); //Inicializa Timer para Intermitencia
unsigned int POSCNT_PRE = 0; //Guarda cont previo encoder para calc veloc.
int Sp_Acc_Revol = 0; //Guarda revoluc. acumuladas para calc veloc.
int Po_Acc_Revol = 0; //Guarda revoluc. acumuladas para calc posic.
MOTOR_INFO Motor;
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Configura puertos
void SetupPorts(void)
{
// ============= Port A ==============
// absent
// absent
// absent
// absent
// absent
// absent
Página 45 de 66
Proyecto Integrador
// absent
// absent
// absent
// RA9 VREF- O LED1 (Active high)
// RA10 VREF+ O LED2 (Active high)
// absent
// absent
// absent
// RA14 INT3 O LED3 (Active high)
// RA15 INT4 O LED4 (Active high)
LATA = 0x0000;
TRISA = 0x39FF;
LATB = 0x0000;
TRISB = 0xFFFF;
// absent
// RC1 T2CK O LCD R/W'
// absent
// RC3 T4CK O LCD RS
// absent
// absent
// absent
// absent
// absent
// absent
// absent
// absent
// absent
// RC13 EMUD1/SOSC2/CN1 EMUD1
// RC14 EMUC1/SOSC1/T1CK/CN0 EMUC1
// RC15 OSC2/CLKO
LATC = 0x0000;
TRISC = 0xFFF5;
// RD12 IC5
// RD13 IC6/CN19 O LCD ENA
// RD14 IC7/CN20
Página 46 de 66
Proyecto Integrador
// RD15 IC8/CN21
LATD = 0x0000;
TRISD = 0xD70F;
// absent
// absent
// absent
// absent
LATE = 0x0000;
TRISE = 0xFD00;
// absent
// absent
// absent
// absent
LATF = 0x0000;
TRISF = 0xFED5;
// absent
// absent
// RG6 SCK2/CN8 I Button 1 (S4) (Active low)
// RG7 SDI2/CN9 I Button 2 (S5) (Active low)
// absent
// absent
// absent
// absent
LATG = 0x0000;
TRISG = 0xFFF0;
Página 47 de 66
Proyecto Integrador
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Inicializa interrupciones pulsadores
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Interrupcion Generada por pulsadores
if (PBFn) PBFn(PBnum,PBsts);
PORTG_PRE = PORTG; // Guarda estado anterior
_CNIF = 0; // Borra Flag Interrupcion
}
Página 48 de 66
Proyecto Integrador
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Inicializa entrada analógica AN7 conectada a potenciometro VR2
void IniVR2(void)
{
ADPCFG |= 0xFF7F; // RB7/AN7=Analog
ADCON1 = 0x00E0; // SSRC=111 Internal counter ends sampling and starts conversion
ADCON2 = 0x0004; // Interrupcion cada 2 muestras
ADCON3 = 0x0F00; // Sample time = 15Tad, Tad = internal Tcy/2
ADCHS = 0x0007; // Conecta AN7 al CH0
ADCSSL = 0; // Inputs no scanned
_ASAM = 1; // Auto start sampling
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Genera tiempo de Espera
// ciclos = (14 x count) + 24 , Tiempo=ciclos/FCY
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Genera secuencia de arranque especifica para el driver HD44780
// Se configura para 4 bits de datos, dos lineas, 5x8 puntos/caracter
void IniLCD(void)
{
Wait(Delay_20ms);
LATD = (LATD&0xFFF0)|0x0003; //Funcion set interface 8 bits
TRISD = TRISD&0xFFF0; //Puerto de datos como salida
LCDRW = 0; //R/!W escribir
LCDENA = 1; //Alterna señal enable
Wait(Delay_200us);
LCDENA = 0;
Wait(Delay_10ms);
LATD = (LATD&0xFFF0)|0x0003; //Funcion set interface 8 bits
LCDENA = 1; //Alterna señal enable
Wait(Delay_200us);
LCDENA = 0;
Wait(Delay_1ms);
LATD = (LATD&0xFFF0)|0x0003; //Funcion set interface 8 bits
LCDENA = 1; //Alterna señal enable
Wait(Delay_200us);
LCDENA = 0;
Wait(Delay_1ms);
LATD = (LATD&0xFFF0)|0x0002; //Funcion set interface 4 bits
LCDENA = 1; //Alterna señal enable
Wait(Delay_200us);
LCDENA = 0;
Página 49 de 66
Proyecto Integrador
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Retorna estado de ocupado del LCD
char BusyLCD(void)
{
char fbusy;
TRISD = TRISD|0x000F; // Puerto de datos como entrada
LCDRS = 0;
LCDRW = 1; // R/!W leer
Wait(Delay_200us);
LCDENA = 1;
Wait(Delay_200us); // Espera datos del LCD
LCDENA = 0;
Wait(Delay_200us);
LCDRW = 0; // R/!W escribir
return fbusy;
}
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Escribe un comando al registro de instrucciones del LCD
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Escribe un comando al registro de datos del LCD
Página 50 de 66
Proyecto Integrador
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Borra el LCD
void DeleteLCD(void)
{
while(BusyLCD()); // Esperar lcd listo
WriteCmdLCD(0x01); // Borra LCD
WriteCmdLCD(0x80); // Cursor al inicio
}
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Posiciona curson en columna, fila
// col: posicion columna inicio
// row: posicion fila inicio
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Escribe texto en LCD empezando por columna, fila
// buffer: cadena de caracteres
// col: posicion columna inicio
// row: posicion fila inicio
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Escribe numero en LCD con formato especificado
// number: numero a escribir
// len: longitud
// dec: numero decimales
// col: posicion columna inicio
// row: posicion fila inicio
Página 51 de 66
Proyecto Integrador
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Inicializa posicion Hall
// AttnFunction: funcion de atencion a la interrupcion (opcional)
void IniHall(void(*AttnFunction)())
{
HallFn=AttnFunction; //Función de atención a la irq
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Interrupciones Generadas posiciones Hall
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Inicializa control PWM con control de fallo FLTA por interrupcion
// AttnFunction: funcion de atencion a la interrupcion de fallo (opcional)
void IniPWM(void(*AttnFunction)())
{
DrvFailFn=AttnFunction; //Función de atención a la irq
SetDC(0); // DutyC a 0%
if (DRVFAIL)
{
Motor.fault=1; //Flag Motor en fallo
if (DrvFailFn) DrvFailFn(); //Si hay funcion configurada se ejecuta
}
Página 52 de 66
Proyecto Integrador
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Interrupcion Generadas fallo Driver FLTA
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Gestiona la activacion de las salidas al motor
void OutMngr(void)
{
unsigned int HV=HallValue(); //Lee posicion actual
PFC=1;
}
}
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Arranca PWM
void StartPWM(void)
{
if (!DRVFAIL) //Si no está en fallo
{
OutMngr();
PWMCON1 = 0x0777; // Salidas independientes habilitadas para PWM
Motor.running=1; // Flag Motor en marcha
}
}
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Para PWM
void StopPWM(void)
{
PWMCON1 = 0x0700; // Salidas desactivadas
Motor.running=0; // Flag Motor parado
}
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Resetea fallo Driver motor
// Retorna 1 si se ha reseteado con exito
char RstDriver(void)
{
DRVRST=1;
//Wait(Delay_RST);
DRVRST=0;
if (!DRVFAIL)
{
Motor.fault=0; //Flag Motor en fallo
_FLTAIF = 0; //Borra Flag Interrupcion FLTA
_FLTAIE = 1; //Activa interrupciones FLTA
return 1;
}
else return 0;
}
Página 53 de 66
Proyecto Integrador
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Establece valor de Duty-Cycle
// DC: Valor de Duty-Cycle en % (+ giro directo, - giro inverso)
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Inicializa encoder con control de fallo por interrupcion
// AttnIndxFunc: funcion de atencion a la interrupcion de paso por INDX (opci)
void IniQEI(void(*AttnFunction)())
{
EncIndxFn=AttnFunction; //Función de atención a la irq
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Interrupcion Generadas ENCODER (POR CNTERR O INDEX)
if (EncIndxFn) EncIndxFn();
Página 54 de 66
Proyecto Integrador
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Interrupcion Generadas T1
// Calcula velocidad instantanea RPM y la posicion en grad
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Actualiza posicion absoluta Encoder
void PosUpdate(void)
{
float count;
//Calculo posicion en grados
Motor.ecount=POSCNT+(Po_Acc_Revol*IPR);
count=(float)Motor.ecount;
Motor.position=(360.0*count)/IPR;
}
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Resetea posicion inicial Encoder
void RstQEI(void)
{
POSCNT = 0;
Po_Acc_Revol=0;
Motor.ecount=0;
Motor.position=0;
}
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Inicializa Timer 4 para intermitencia (500 ms)
void iniTimerBlink(void(*AttnFunction)()){
//prescalado a 256
//tiempo muestreo 500 ms;
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Interrupcion Generada por temporizador T4 (500 ms)
Página 55 de 66
Proyecto Integrador
typedef struct
{
float SP; //Setpoint %
float PV; //Process Value %
float MV; //Manipulated Value %
void IniSpeedLoop();
void PIDSetSP(float SP,PID_DATA *PID);
float PIDLoop(float PV,PID_DATA *PID);
PID_DATA SpeedLoop;
PID_DATA PositLoop;
char LoopType;
void IniSpeedLoop()
{
SpeedLoop.EUmax=4000.0;
SpeedLoop.EUmin=0.0;
SpeedLoop.MVmax=100.0;
SpeedLoop.MVmin=-100.0;
SpeedLoop.KP=1.0; //Proporcional
SpeedLoop.KI=0.5; //Integral (seg)
SpeedLoop.KD=0.0; //Derivativa (seg)
SpeedLoop.Tupdt=SPEPIDTM;
SpeedLoop.action=PIDACTION_REV;
SpeedLoop.mode=PIDMODE_MAN;
SpeedLoop.MV=0.0;
PIDSetSP(0.0,&SpeedLoop);
LoopType=TYPE_SPEED;
Página 56 de 66
Proyecto Integrador
void IniPositLoop()
{
PositLoop.EUmax=3700.0;
PositLoop.EUmin=-3700.0;
PositLoop.MVmax=8.0;
PositLoop.MVmin=-8.0;
PositLoop.KP=2; //Proporcional
PositLoop.KI=1.0; //Integral (seg)
PositLoop.KD=0.05; //Derivativa (seg)
PositLoop.Tupdt=POSPIDTM;
PositLoop.action=PIDACTION_REV;
PositLoop.mode=PIDMODE_MAN;
PositLoop.MV=0.0;
PIDSetSP(0.0,&PositLoop);
LoopType=TYPE_POSIT;
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Establece valor de SP
//Normalizamos SP en %
SPScale = 100*(SP - PID->EUmin)/(PID->EUmax - PID->EUmin);
//Acotamos límites
if (SPScale<0.0) SPScale=0.0;
if (SPScale>100.0) SPScale=100.0;
PID->SP=SPScale;
}
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Lazo PID
//Normalizamos PV en %
PVScale=100*(PV - PID->EUmin)/(PID->EUmax - PID->EUmin);
//Acotamos límites
if (PVScale<0.0) PVScale=0.0;
if (PVScale>100.0) PVScale=100.0;
if (PID->mode == PIDMODE_AUTO)
{
//Calculo error
if (PID->action == PIDACTION_REV) //Accion Inversa
{
err=(PID->SP - PVScale);
err_old=(PID->SP - PID->PV);
}
else //Accion Directa
{
Página 57 de 66
Proyecto Integrador
err=(PVScale - PID->SP);
err_old=(PID->PV - PID->SP);
}
//Algoritmo PID
Tp= err*PID->KP;
Td=(1000.0 * (err-err_old) * PID->KD)/(PID->Tupdt);
//Anulacion Integral si es 0
if (PID->KI > 0.0) Ti=(PID->Acc_err * PID->Tupdt)/(1000.0 * PID->KI);
else Ti=0;
MV = Tp+Ti+Td+ PID->Bias;
//Control Antisaturacion
if (!WINDUP)
{
PID->Acc_err += err;
}
}
else
{
PID->Acc_err=0;
PID->Bias=PID->MV;
}
PID->PV=PVScale;
return PID->MV;
}
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Interrupcion Generadas T2
// Lazo PID velocidad
Página 58 de 66
Proyecto Integrador
#include <xc.h>
void IniCAN(void(*AttnFunction)())
{
CANFn=AttnFunction; //Función de atención a la irq
// Transmision de salida
C1TX0CON = 3; // misstge d'altra prioritat
C1TX0EID = 0; // ID Mensaje estandard
C1TX0SIDbits.TXIDE = 0;
C1TX0SIDbits.SRR = 0;
// Interrupciones
IFS1bits.C1IF=0; // Borra Flag Interrupcion
IEC1bits.C1IE=1; // Habilitació d'interrupció del CAN 1
C1INTF=0; // Borra Flag Interrupcion
C1INTE=0xFF; // Habilitem les interrupcions
//Arranque CAN
C1CTRLbits.REQOP=0; // Modo Normal
}
Página 59 de 66
Proyecto Integrador
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Lee dato tipo Float del buffer de lectura CAN
float ReadCANFloat()
{
float *pfloat;
pfloat=&C1RX0B1;
return *pfloat;
}
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Escribe dato tipo Float del buffer de lectura CAN
*pfloat=value;
C1TX0SIDbits.SID5_0= SID;
C1TX0SIDbits.SID10_6 = SID>>6;
C1TX0DLC=4<<3; //Longitud de datos 4 bytes
C1TX0CONbits.TXREQ=1;
}
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Lee dato tipo Int del buffer de lectura CAN
int ReadCANInt()
{
int *pint;
pint=&C1RX0B1;
return *pint;
}
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Escribe dato tipo int del buffer de lectura CAN
*pint=value;
C1TX0SIDbits.SID5_0 = SID;
C1TX0SIDbits.SID10_6 = SID>>6;
C1TX0DLC=2<<3; //Longitud de datos 2 bytes.
C1TX0CONbits.TXREQ=1;
}
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Lee dato tipo Char del buffer de lectura CAN
char ReadCANChar()
{
char *pint;
pint=&C1RX0B1;
return *pint;
}
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Escribe dato tipo char del buffer de lectura CAN
*pint=value;
C1TX0SIDbits.SID5_0 = SID;
C1TX0SIDbits.SID10_6 = SID>>6;
C1TX0DLC=1<<3; //Longitud de datos 1 bytes.
C1TX0CONbits.TXREQ=1;
}
Página 60 de 66
Proyecto Integrador
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Lee dato tipo long del buffer de lectura CAN
long ReadCANLong()
{
long *plong;
plong=&C1RX0B1;
return *plong;
}
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Escribe dato tipo long del buffer de lectura CAN
*plong=value;
C1TX0SIDbits.SID5_0= SID;
C1TX0SIDbits.SID10_6 = SID>>6;
C1TX0DLC=4<<3; //Longitud de datos 4 bytes
C1TX0CONbits.TXREQ=1;
}
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Interrupcion Generada por Bus CAN1
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// Inicializa temporizador para envio datos a Matlab (cada 500 ms))
void iniTimerCAN(){
//prescalado a 256
//tiempo muestreo 500 ms;
Página 61 de 66
Proyecto Integrador
#include "PICSettings.h"
#include "dsPICDEM_MC1.h"
#include "PID.h"
#include "CAN.h"
#include <xc.h>
#include <dsp.h>
void PB_OnChange(char PBnum, char PBsts); //Función de atención a las irq de pulsadores
void Driver_Fail(); //Función de atención a la irq del Fallo Driver
void Encoder_Home(); //Función de atención a la irq de Encoder en INDX
void CANrx(); //Función de atención a la irq del CAN
void Blink(); //Función de atención a la irq del Timer Blink
float posSP=0.0;
int blinking=0;
int main(void)
{
SetupPorts(); //Configura puertos
IniPB(PB_OnChange); //Inicializa interrupcion pulsadores
IniLCD(); //Inicializa LCD (Driver HD44780)
IniHall(0); //Inicializa control posicion Hall
IniPWM(Driver_Fail); //Inicializa control PWM
IniQEI(Encoder_Home); //Inicializa Encoder
IniCAN(CANrx); //Inicializa CAN
IniPositLoop();
iniTimerCAN();
iniTimerBlink(Blink);
//Reset Driver
while(!RstDriver());
StartPWM();
PositLoop.mode=PIDMODE_AUTO;
RstQEI();
while (1)
{
if (!Motor.fault)
{
WriteNumLCD(posSP,6,1,2,0);
if (Motor.direction) WriteNumLCD(-1*Motor.dcycle,5,1,11,0);
else WriteNumLCD(Motor.dcycle,5,1,11,0);
WriteNumLCD(Motor.position,6,1,7,1);
}
}
return 0;
}
//*************************************************
void CANrx()
{
switch(CAN1RXSID){
case 620: //ID 620 correspon a comanda de consigna matlab
{
posSP=ReadCANChar()*POSPMAX;
PIDSetSP(posSP,&PositLoop);
break;
}
case 500: //Matlab demands LEDs ON or OFF
{
switch(ReadCANChar()){
case 0: //Luces apagadas
{
LED_D6=0;
LED_D7=0;
break;
}
case 1: //Luces cortas
{
LED_D6=1;
LED_D7=0;
Página 62 de 66
Proyecto Integrador
break;
}
case 3: //Luces cortas
{
LED_D6=1;
LED_D7=1;
break;
}
}
break;
}
case 550: //Matlab demands LED BLINKING
{
blinking=ReadCANChar();
break;
}
}
}
//*************************************************
void PB_OnChange(char PBnum, char PBsts)
{
if (PBsts==1)
{
switch(PBnum){
case 4:
{
RstDriver();
StartPWM();
PositLoop.mode=PIDMODE_AUTO;
break;
}
case 5:
{
break;
}
case 6:
{
break;
}
case 7:
{
break;
}
}
}
//*************************************************
void Blink()
{
switch(blinking){
case 0: //OFF
{
LED_D8=0;
LED_D9=0;
break;
}
case 1: //Both
{
if (LED_D8==0)
{
LED_D8=1;
LED_D9=1;
}
else
{
LED_D8=0;
LED_D9=0;
}
break;
}
case 2: //Right
{
LED_D9=0;
if (LED_D8==0) LED_D8=1;
else LED_D8=0;
break;
}
case 3: //Both
{
if (LED_D8==0)
{
LED_D8=1;
Página 63 de 66
Proyecto Integrador
LED_D9=1;
}
else
{
LED_D8=0;
LED_D9=0;
}
break;
}
case 4: //Left
{
LED_D8=0;
if (LED_D9==0) LED_D9=1;
else LED_D9=0;
break;
}
case 5: //Both
{
if (LED_D8==0)
{
LED_D8=1;
LED_D9=1;
}
else
{
LED_D8=0;
LED_D9=0;
}
break;
}
}
}
//*************************************************
Página 64 de 66
Proyecto Integrador
Per finalitzar, proposem per a futurs cursos d’aquesta assignatura una continuació del nostre
projecte. El nou projecte continuista es basaria en adaptar el bus CAN implementant el protocol
OBDII, per tal de poder-se comportar com un bus CAN real d’un cotxe. També proposem
realitzar un programa amb Matlab i Kvaser que pugui llegir els Parameter Identifiers (PIDs) dels
cotxes o fins i tot interaccionar amb ells. Existeixen productes similars en el mercat actualment
com el ELM327, que es pot comprar en preus que oscil·len dels 3€ als 15€. Aquests productes es
connecten al port OBDII dels cotxes i s’hi comuniquen amb ell, i serveis per detectar falles o
mostrejar la velocitat, les rpm del motor, la contaminació del vehicle...
A més a més, també proposem afegir un nou microcontrolador al bus CAN que permeti
connectar el bus sense cables, amb wifi, i inclús que aquest microcontrolador inclogui un
servidor web, permitint poder comunicar-se amb el bus CAN a través d’internet (enllaç amb
Ethernet sense fils). Per fer-ho proposem que s’utilitzi el microcontrolador ESP32, un
microcontrolador amb plaques de desenvolupament disponibles a preus molt competitius (5€) i
que inclou suport per a control CAN (caldria afegir un transceptor compatible amb 3.3V) que
només necessita un transceptor, com el MCP2515 per funcionar en una xarxa CAN.
La interacció del bus CAN sense fils i llur connexió a internet està actualment en
desenvolupament pels fabricants alemanys de cotxes de gama alta, en el protocol que han
anomenat OBDIII, on busquen que les ECU’s del cotxe conectades a la xarxa local CAN puguin
interaccionar amb l’exterior. OBDIII ha de lidiar amb grans problemes com la seguretat
informàtica. L’ESP32 també permet encriptar dades i té molt de suport en la comunitat a
internet.
També proposem continuar desenvolupant la part del motor BLDC, implementant el PID amb el
mètode experimental de Ziegler-Nichols, i mostrant per Matlab (enviat per CAN) la resposta a
una entrada esglaó i/o rampa, calculant els paràmetres d’error en contínua, temps
d’establiment, sobrepic... Així com també poder ajustar el PID de forma manual per l’usuari a
través de l’aplicació de Matlab i també implementar una auto-calibració del PID que s’ajusti a
uns valors de temps d’establiment, sobrepic i error en contínua proporcionats per l’usuari (en
aquest cas seria més fàcil fer-ho amb una simulació de Simulink i comparar el model teòric amb
les mesures reals).
11 Bibliografia
Referències bibliogràfiques:
“Control i Monitorització d’un motor elèctric auxiliar d’un vehicle”. Aleix Maixé Sas.
Referències web:
Página 65 de 66
Proyecto Integrador
https://en.wikipedia.org/wiki/CAN_bus
https://knowledge.ni.com/KnowledgeArticleDetails?id=kA00Z0000019LzHSAU
https://barrgroup.com/Embedded-Systems/How-To/CAN-vs-SPI
https://www.csselectronics.com/screen/page/simple-intro-to-can-bus/language/en
https://www.csselectronics.com/screen/page/simple-intro-obd2-explained/language/en
http://ww1.microchip.com/downloads/en/DeviceDoc/70070D.pdf
Página 66 de 66