-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcapitol_6.tex
1113 lines (864 loc) · 74.6 KB
/
capitol_6.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
% \part{Temes avançats}
% \label{part:avançats}
\chapter{Gestió d'excepcions}
\label{ch:exceptions}
Sovint treballant amb sistemes encastats ens trobem amb errors d'origen desconegut que es poden provocar per múltiples causes. Així, per exemple, una divisió per zero, un accés incorrecte a una zona de memòria o un accés a una posició de memòria fora de rang faran que el processador es reiniciï \cite[102]{DesignersGuide}\cite[318]{ARMsdg}\cite{KielHardFault}.
Aquests casos poden ser molt difícils de trobar si són casos esporàdics, però l'arquitectura ARM té unes característiques que ajuden a detectar-los i trobar-los. En síntesi, el cortex-M llença una interrupció molt prioritària anomenada {\bf HardFault\_Handler()}\index{HardFault\_Handler()} quan succeeix un problema greu del que el processador no pot recupera-se, com una divisió per zero, un accés il·legal a memòria, etc. Abans de cridar a l'excepció, la CPU guarda tot de valors claus a diferents registres, i així per exemple en el registre {\bf PC} s'hi emmagatzema l'adreça de la instrucció executada, així que, en principi, només cal anar a aquella posició de memòria per veure quin ha estat el codi que ha causat el problema. També s'emmagatzema el valor de retorn (la instrucció següent a l'executada que ha causat l'error) al registre {\bf LR} \cite{BlogHardFalut}.
Així doncs, es pot reescriure la ISR per obtenir les dades que ens informi sobre què ha passat per ajudar-nos a obtenir pistes de quin codi està fallant \cite{ARMHandler}.
\section{Exemple detectant errors greus}
A l'\href{https://github.com/mariusmm/cursembedded/tree/master/Simplicity/ErrorHandling}{exemple del repositori} hi ha un codi que genera diferents errors segons la funció que es cridi i una implementació de {\bf HardFault\_Handler()}\index{HardFault\_Handler()}. Aquesta funció està escrita en assemblador, però el que cal veure és que es crida a la funció {\bf my\_HardFault\_Handler()}\index{my\_HardFault\_Handler()} que es qui en realitat fa tota la feina i és la que cal entendre \cite{EFM32HardFault}.
\index{my\_HardFault\_Handler()}
\begin{lstlisting}[style=customc,caption=Codi HardFault\_Handler,label=HardFaultHandler_1]
void my_HardFault_Handler(uint32_t *stack) {
printf("Error Handler\r\n");
printf("SCB->HFSR = 0x%08lx\r\n", (uint32_t) SCB->HFSR);
if ((SCB->HFSR & (1 << 30)) != 0) {
printf("Forced Hard Fault\r\n");
printf("SCB->CFSR = 0x%08lx\r\n", SCB->CFSR);
if ((SCB->CFSR & 0x02000000) != 0) {
printf("Divide by zero\r\n");
}
if ((SCB->CFSR & 0x01000000) != 0) {
printf("Unaligned\r\n");
}
if ((SCB->CFSR & 0x00010000) != 0) {
printf("Undefined\r\n");
}
...
}
\end{lstlisting}
A la primera part (veure Llistat~\ref{HardFaultHandler_1}) de la \gls{ISR} es treu per la consola de {\em debug} la causa de l'excepció ({\em bus fault}, {\em memory access}, {\em divide by zero}, etc.).
Tot seguit es treu per la mateixa consola els valors dels registres que hi ha a l'\gls{stack} per tenir dades que ens permetin localitzar l'error (Llistat~\ref{HardFaultHandler_2}).
\begin{lstlisting}[style=customc,caption=Codi HardFault\_Handler (continuació),label=HardFaultHandler_2]
void my_HardFault_Handler(uint32_t *stack) {
...
printf("sp = 0x%08lX\r\n", (uint32_t) stack);
printf("r0 = 0x%08lX\r\n", stack[0]);
printf("r1 = 0x%08lX\r\n", stack[1]);
printf("r2 = 0x%08lX\r\n", stack[2]);
printf("r3 = 0x%08lX\r\n", stack[3]);
printf("r12 = 0x%08lX\r\n", stack[4]);
printf("lr = 0x%08lX\r\n", stack[5]);
printf("pc = 0x%08lX\r\n", stack[6]);
printf("psr = 0x%08lX\r\n" stack[7]);
...
}
\end{lstlisting}
\begin{figure}
\centering
\fbox{\color{ocre}\includegraphics[width=0.65\textwidth, keepaspectratio]{imatges/HardFault_Console.png}}
\caption{Debugger aturat a la instrucció DEBUG\_BREAK i el {\em dump} els registres}
\label{fig:HardFaultDump}
\end{figure}
Per últim, es crida la macro {\bf DEBUG\_BREAK}\index{DEBUG\_BREAK}, que està definida com una instrucció en assemblador ({\bf BKPT \#01}) que posa el {\em core} en mode {\em Debug} i atura l'execució en aquest punt. Així, si tenim un {\em debugger} connectat, veurem com l'execució s'atura en aquest punt i torna el control a la nostra eina (veure Figura~\ref{fig:HardFaultDump}).
\begin{figure}
\centering
\fbox{\color{ocre}\includegraphics[width=0.65\textwidth, keepaspectratio]{imatges/HardFault_Dissassembly.png}}
\caption{Codi assemblador a la posició de memòria indicada pel registre {\bf PC}}
\label{fig:HardFaultDis}
\end{figure}
Si anem a la finestra {\em Disassembly} i anem a la posició de memòria que indica el registre {\bf PC} (0x6AE a l'exemple), veurem que apunta a una instrucció assemblador {\em sdiv}, que es corresponent amb una divisió. Si mirem el codi anterior, podem deduir que a la posició de memòria {\bf R7+0x8} (corresponent a la variable {\em b}) s'hi ha emmagatzemat un 0 (instruccions a 0x6A6 i 0x6A8) i aquesta variable es fa servir a la divisió com a divisor, causant l'error (veure Figura~\ref{fig:HardFaultDis}).
També cal comentar que les diferents funcions que generen errors són les següents:
\begin{itemize}
\item {\bf WrongfunctionDiv0()}\index{WrongfunctionDiv0()} causa una divisió per zero.
\item {\bf WrongfunctionAlign()}\index{WrongfunctionAlign()} causa un error d'accés a memòria fora d'alineament.
\item {\bf WrongfunctionWrongMemory()}\index{WrongfunctionWrongMemory()} causa un error per accés fora dels límits de la memòria.
\item {\bf fp()}\index{fp()} causa un intent d'executar a la posició 0x0000\_0000 de memòria.
\end{itemize}
\chapter{{\em Shadow Registers}}
En algunes arquitectures i en perifèrics d'alguns fabricants poden llegir que es fan servir {\em shadow registers}. S'anomenen així a registres que contenen una còpia d'un altre registre i que son els que es poden llegir per part d'altres dispositius o perifèrics.
Així per exemple, trobem {\em shadow registers} a alguns processadors de manera que quan la CPU entra a una interrupció es passa a treballar amb un banc separat de registres de propòsit general. Això es fa per evitar un sobrecost a la crida de la ISR, ja que si es tenen aquests registres s'han de guardar els valors actuals de tots els registres a la pila abans de poder executar el codi de la ISR. En canvi, si es tenen aquests registres, la CPU passa a treballar amb un banc diferent (els {\em shadow registers}) durant l'execució de la ISR i no cal salvaguardar cap valor dels registres originals. Un cop se surt de la ISR la CPU torna a treballar amb el banc de registres originals. En el cas dels Cortex-M no es treballa amb aquesta mena de {\em shadow registers} i, per tant, caldrà que les ISR salvin els valors dels registres de propòsit general que sobreescriguin durant la seva execució.
Una altra lloc on ens podem trobar {\em shadow registers} és en alguns perifèrics que treballen valors grans repartits en diversos registres. Si aquests registres s'actualitzessin entremig d'una lectura per part del Firmware, aquest podria tenir una inconsistència a les dades. Per això, és habitual que un valor determinat s'emmagatzemi a {\em shadow registers} mentre els registres ``amagats'' s'actualitzen de forma normal. Aquests {\em shadow registers} seran els que el firmware pot llegir i s'actualitzaran tots de cop una vegada s'hagin llegit tots pel firmware.
\begin{remark}
A tots ens ha passat o tenim un company que ha perdut una tarda sencera intentant llegir uns registres d'aquesta mena sense seguir bé l'ordre i rebent valors dolents sense caure en el problema amb els {\em shadow registers}.
\end{remark}
Un exemple d'això últim succeeix amb els registres de data i temps del RTC dels microcontroladors d'ST (veure~\fullref{sub:RTC}). Aquest perifèric conté uns {\em shadow registers} on es copien cada 2 cicles els registres reals amb la data, el temps i els segons del RTC (Figura~\ref{fig:ShadowRegisters}). Quan es llegeix el registre amb el temps o amb els segons es bloqueja la còpia de tots els tres registres perquè la lectura dels demès no doni cap incoherència. Si no hi fossin, podria passar que es llegís el temps (per exemple les 23:59:59 del dia 1) i poc després al llegir la data ja hagués passat el segon i la data ja fos el dia 2, resultant en que enlloc de llegir les 23:59:59 del dia 1 s'hauria llegir les 23:59:59 del dia 2. En aquest cas sembla que és molt millor llegir la data correcte i que es tingui un error d'un segon a tenir un dia sencer d'error (!).
Per tant, en aquest perifèric, primer cal llegir el registre amb el temps o els segons i després el registre amb la data \cite[800-805]{STM32F4RM}. Així si només es vol llegir el temps del RTC perquè no interessa la data del sistema, no es poden fer lectures consecutives del temps sense llegir també, encara que no interessi, la data del RTC. La API del fabricant en aquest cas no ho gestiona, però si que ho adverteix a la seva documentació \cite[719]{STM32UM1725}.
\begin{figure}
\centering
\includegraphics[width=0.65\textwidth, keepaspectratio]{imatges/ShadowRegisters.png}
\caption{{\em Shadow registers} del perifèric RTC dels STM32 \cite[800]{STM32F4RM}}
\label{fig:ShadowRegisters}
\end{figure}
\chapter{Baix cosum}
\label{ch:low-power}
Un dels temes més habituals de trobar-se quan es tracten temes amb microcontroladors és el del baix consum. Gràcies a la tecnologia de fabricació dels microxips i els avenços en les arquitectures dels microcontroladors, aquests han arribat a unes fites de consum molt baixes, permeten desenvolupar aplicacions on el sistema pugui anar alimentat per bateries o altres fonts d'alimentació alternatives a l'alimentació general. En aquest capítol veurem les característiques actuals dels microcontroladors en aquest aspecte, com treure tot el partit a aquestes característiques i, per últim, com adaptar els \gls{RTOS} per treballar amb baix consum.
Cal repassar uns quants conceptes sobre el consum d'energia abans d'introduir-nos de ple en el tema.
\section{Consideracions prèvies}
\label{sec:lowpowerintro}
Per la pròpia natura dels circuits digitals, aquests consumeixen sobretot quan el seu rellotge principal està actiu. Això fa que l'estratègia principal per reduir el consum d'un circuit és desactivar-li precisament el rellotge o reduir la seva freqüència, ja que el consum és proporcional a la velocitat de rellotge.
\begin{remark}
Donat que el consum és quasi proporcional a la freqüència de rellotge, els fabricants acostumen a donar el consum per MHz (típicament $\mu$A/MHz).
\end{remark}
També cal tenir en compte que qui més consumeix en un microcontrolador és el propi {\em core} o CPU i que, per tant, serà el mòdul que caldrà tenir apagat el màxim de temps possible.
\section{Modes d'{\em sleep}}
\label{sec:sleepmodes}
Els diferents fabricants de microcontroladors basats en Cortex-M ofereixen diferents modes d'sleep, això és, diferents combinacions de perifèrics que estan actius a cada mode per tal de reduir el consum.
Així, els microcontroladors de Silicon Labs tenen 4 modes d'sleep\footnote{A més, hi ha el mode normal, on la CPU està a ple rendiment} \cite[6]{EFM32GRM}:
\begin{itemize}
\item EM0 - {\em Energy Mode 0}: Tot el sistema està actiu incloent-hi tots els perifèrics.
\item EM1 - {\em Energy Mode 1}: La CPU està desactiva i la resta de perifèrics estan disponibles.
\item EM2 - {\em Energy Mode 2}: La CPU està desactivada i només els perifèrics de baix consum estan disponibles (UART, RTC, TIMER, Watchdog)
\item EM3 - {\em Energy Mode 3}: Tot el sistema està desactivat, només es manté la RAM activada i certes interrupcions
\item EM4 - {\em Energy Mode 4}: Tot el sistema està desactivat, només es pot fer un {\em reset} al sistema.
\end{itemize}
En canvi, els microcontroladors de ST tenen només 3 modes de baix consum\footnote{Versions de Cortex-M0+ tenen algun mode més} \cite[126]{STM32F4RM}:
\begin{itemize}
\item {\em Run mode}: Tot el sistema està actiu incloent-hi tots els perifèrics.
\item {\em Sleep mode}: La CPU està desactiva i la resta de perifèrics estan disponibles.
\item {\em Stop mode}: Tot el sistema està desactivat, només es manté la RAM activada i certes interrupcions
\item {\em Standby mode}: Tot el sistema està desactivat, només es pot fer un {\em reset} al sistema.
\end{itemize}
\begin{table}
\caption{Consum d'energia de diferents fabricants i modes (per un Cortex-M0+) \cite{EFM32ZG108DS}\cite{STM32L01}}
\centering
\begin{tabular}{|c|c|c|}
\hline
{\bf Processador} & {\bf STM32} & {\bf EFM32}\\
{\bf SleepMode} & & \\
\hline
{\bf EM0 - {\em Run mode}} & 76 $\mu$A/Mhz & 114 $\mu$A/MHz\\
\hline
{\bf EM1 - {\em Sleep mode}} & ~42 $\mu$A/MHz & 48 $\mu$A/MHz\\
\hline
{\bf EM4 - {\em Standby mode}} & 230 nA & 20 nA \\
\hline
\end{tabular}
\label{tb:bin_size}
\end{table}
Els {\em core} Cortex-M es poden posar en mode de baix consum fent servir dues instruccions {\bf WFI} i {\bf WFE}. El primer que cal fer és configurar a quin mode d'adormir es vol posar el microcontrolador i després executar la instrucció que pertoqui. La CPU es quedarà en l'estat de baix consum que s'hagi configurat fins que es generi una \gls{IRQ} per algun perifèric o generat per un senyal extern.
\section{Estratègies de baix consum}
\label{sec:lowpowerstrategies}
Vist tot l'anterior, l'estratègia bàsica per tenir un baix consum serà la de preparar els perifèrics per a que facin la funcionalitat d'entrada/sortida necessària de manera que llencin una \gls{IRQ} quan finalitzin, posar en un dels modes de baix consum on la CPU està desactivada a l'espera de les interrupcions; a continuació, la CPU processarà les dades o esdeveniments que hagin succeït i es tornarà a configurar els perifèrics i es tornarà a posar la CPU en mode baix consum, etc.
Per tant, quan es desenvolupa una aplicació per ser de baix consum, s'acostuma a treballar basant-se en interrupcions (Veure~\fullref{ch:IRQ}) i tenint la CPU el màxim de temps en algun dels modes de baix consum.
\subsection{Exemple de baix consum}
\href{https://github.com/mariusmm/cursembedded/tree/master/Simplicity/ADC_1_LP}{L'exemple que es veurà} farà servir l'\gls{ADC} per convertir una entrada analògica a un valor digital, com ja es a fer a l'exemple \fullref{sub:ADC_example}. En el cas de baix consum, es configura el perifèric de la mateixa forma però s'hi afegeix l'opció que generi una \gls{IRQ} quan acaba de fer una conversió. Així, el nostre codi al bucle principal engegarà la conversió, entrarà en el mode de baix consum {\bf EM1} perquè la CPU es quedi en repòs mentre l'ADC fa la seva feina i es desperti per la \gls{IRQ} de finalització; tot seguit es llegeix i es mostra la dada convertida.
\index{main()}\index{ADC\_Start()}\index{EMU\_EnterEM1()}\index{ADC\_DataSingleGet()}
\begin{lstlisting}[style=customc,caption={Bucle principal amb funcions de baix consum}, label=ADC_LP]
void main() {
...
while (1) {
ADC_Start(ADC0, adcStartSingle);
EMU_EnterEM1();
ADCvalue = ADC_DataSingleGet(ADC0);
printf("ADC Value %lu\r\n", ADCvalue);
}
...
}
\end{lstlisting}
Podem fer una mesura del temps que està la CPU en el mode EM1 posant un pin a '1' quan s'entra al mode i posar-lo a '0' quan se'n surt, tal com es veu al projecte d'exemple.
Si usem l'analitzador lògic per mesurar els temps, veiem la imatge de la Figura~\ref{fig:adc_logic} que les mesures diuen que 44,29 microsegons de 52.21 la CPU està en mode de baix consum (el 84.84\% del temps).
\begin{figure}
\centering
\includegraphics[width=0.85\textwidth, keepaspectratio]{imatges/ADC_LP1_Measurement.png}
\caption{Captura de les mesures de temps amb l'analitzador lògic}
\label{fig:adc_logic}
\end{figure}
\section{{\em Timers} de baix consum}
\label{sub:letimer_example}
Un mode que es fa servir sovint en sistemes de baix consum és el de tenir un {\em timer} configurat perquè desperti el sistema cada cert temps. Així per exemple, en un sistema que ha de llegir un sensor cada 30 segons, el {\em timer} seria l'únic perifèric en funcionament actiu i estaria configurat per generar una \gls{IRQ} cada 30 segons; la resta del microcontrolador podria estar en un mode de baix consum que el permeti consumir molt poca energia mentre espera a ser despertat per una \gls{IRQ}.
Al \href{https://github.com/mariusmm/cursembedded/tree/master/Simplicity/LETIMER_LP}{projecte del repositori} hi ha un exemple d'aquest tipus. Es fa servir un {\bf LETIMER}, que és un {\em timer} de baix consum i baixa freqüència que pot funcionar mentre la resta del microcontrolador està en el mode EM2 (o EM3 segons la configuració que es faci servir) \cite[294]{EFM32TGRM}. Aquest {\bf LETIMER} es pot alimentar amb el rellotge extern de baixa freqüència a 32.768 Hz ({\bf LFXO}) o bé amb l'oscil·lador intern a 1.000 Hz ({\bf ULFRCO}) (al codi es pot triar segons es defineixi o no la macro {\bf USE\_ULFRCO}). El rellotge que s'hagi triat es pre-escala per un factor suficient per tenir un comptador prou lent, ja que cal tenir en compte que aquest comptador és de només 16 bits i, per tant, si tenim una freqüència de funcionament elevada no podrem comptar gaire temps.
Tot seguit el {\em timer} es configura per generar una interrupció quan arribi a 0 (és un comptador decreixent) i el seu valor {\bf TOP} (al valor al que es reinicia després d'arribar a 0) es posa en funció de la freqüència de funcionament i el temps que es vol tenir el sistema en baix consum, a l'exemple del repositori es posa a 4 segons. Un resum del codi de l'exemple es veu a Llistat~\ref{LETIMER_example}.
\index{LETIMER0\_IRQHandler()}\index{main()}\index{LETIMER\_IntGet()}\index{LETIMER\_IntClear()}
\index{GPIO\_PinOutToggle()}\index{CMU\_ClockSelectSet()}\index{CMU\_ClockDivSet()}
\index{LETIMER\_CompareSet()}\index{EMU\_EnterEM3()}
\begin{lstlisting}[style=customc, caption={Exemple ús de {\bf LETIMER}}, label=LETIMER_example]
#define PRESCALER cmuClkDiv_1
#define EFECTIVE_CLK_FREQ (1000/PRESCALER)
#define SLEEP_SECONDS 4
#define TOP_VALUE (EFECTIVE_CLK_FREQ * SLEEP_SECONDS)
void LETIMER0_IRQHandler(void) {
uint32_t flags;
/* Clear flag for LETIMER0 */
flags = LETIMER_IntGet(LETIMER0);
LETIMER_IntClear(LETIMER0, flags);
/* Toggle LED ON/OFF */
GPIO_PinOutToggle(gpioPortD, 7);
}
void main(void) {
...
/* ULFRCO is 1,000 kHz */
CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_ULFRCO);
CMU_ClockDivSet(cmuClock_LETIMER0, PRESCALER);
...
LETIMER_CompareSet(LETIMER0, 0, TOP_VALUE);
...
while (1) {
/* nothing to do here */
EMU_EnterEM3(true);
}
}
\end{lstlisting}
Aquest és un exemple senzill que fa servir un {\em timer} especial de la família EFM32 de Silicon Labs. Altres fabricants proporcionen {\em timers} similars. Així ST té un {\em timer} força similar, anomenat LPTIMER (\cite{ST_ANS4865}) i Fresscale te el LPTMR amb característiques similars \cite{Kinetis_LPTMR}. Els {\em timers} de ST i de Silicon Labs poden generar senyals tipus \gls{PWM} mentre el microcontrolador està en modes de baix consum (veure \fullref{sub:PWM}).
\section{Baix consum i RTOS}
\label{sec:lowpwerRTOS}
Quan treballem amb un RTOS funcionant en el nostre microcontrolador, hi ha diferents estratègies per aconseguir disminuir el consum energètic.
Bàsicament hi ha dues estratègies:
\begin{itemize}
\item Aprofitar la tasca {\em Idle} per posar al microcontrolador en un mode de baix consum.
\item Passar a un sistema sense {\em tick} (també dit {\em tickless}).
\end{itemize}
En qualsevol cas, l'avantatge de que sigui el SO qui s'encarregui de gestionar el baix consum és que les tasques no s'han de preocupar per aquesta gestió.
\subsection{Tasca {\em Idle} per baix consum}
\label{sub:idlelowpower}
L'estratègia més senzilla és la d'activar un mode de baix consum quan s'executa la tasca {\em Idle}. Com que aquesta tasca s'executa quan no hi ha cap altra tasca preparada per agafar el microcontrolador, té sentit pensar en aturar el microcontrolador i esperar a que una tasca estigui disponible. Quan succeeixi el proper {\em tick}, el microcontrolador sortira del mode d'{\em sleep} i tornarà a executar el planificador, que, si segueix sense haver cap tasca disponible (en estat {\em Ready}) per executar tornarà a executar la tasca {\em Idle} que tornarà a adormir la CPU i es repetirà el cicle \cite{FreeRTOSLP}.
\begin{remark}
Cal recordar que quan el {\em core} està en algun mode de baix consum, el {\em SysTick} també es desactiva. Per tant, per poder tenir un {\em tick} quan el {\em core} està en un mode de baix consum caldrà fer servir un altre Timer que si que funcioni en aquests modes de baix consum.
\end{remark}
Cal pensar que tot i que aquest mètode és molt senzill d'implementar, té la limitació de que a cada {\em tick} es treu la CPU del mode de baix consum per comprovar si hi ha alguna tasca en estat {\em Ready}. Podem imaginar-nos una aplicació que llegeixi d'un sensor cada 200 ms i processant les dades, com l'aplicació d'exemple XXXXX. Si es té en compte que el {\em tick} pot ser de 1000 Hz, és fàcil d'observar que es despertarà molts cops al {\em core} perquè tant sols el planificador vegi que no hi ha cap tasca {\em Ready} i torni a adormir el processador.
%% manual break a la ultima macro pq latex no ho fa be
Aquesta característica es pot activar a FreeRTOS editant el fitxer ``FreeRTOSConfig.h'' i fixant a '0' la definició {\bf configUSE\_TICKLESS\_IDLE} i triant el valor '1' per {\bf configUSE\_SLEEP\_MODE\\\_IN\_IDLE}. En el cas de Silicon Labs, el microcontrolador es posa en el mode EM2 (veure \fullref{sec:sleepmodes}) i deixant en funcionament tant sols el RTC (veure \fullref{sub:RTC}) i les \gls{IRQ} dels GPIOs que l'usuari hagi configurat (veure \fullref{ch:IRQ}).
\subsection{FreeRTOS sense {\em tick}}
\label{sub:tickless}
L'altre estratègia per disminuir encara més el consum, és desactivar el {\em tick} durant cert temps. En una aplicació on totes les tasques estan bloquejades (i que entraria la tasca {\em Idle}) es pot calcular el temps en que alguna tasca es desbloquejarà (perquè alguna tasca estigui bloquejada perquè ha cridat la funció vTaskDelay()\index{vTaskDelay()}). Es pot desactivar el {\em Tick} i programar el Timer perquè generi una interrupció en aquell temps calculat. Si mentre està el sistema adormit esperant aquell temps hi ha algun esdeveniment extern (interrupció), es despertarà i es podrà reprendre l'execució normal i tornar a activar el {\em Tick}.
Amb aquesta estratègia es maximitza el temps en que el {\em core} està en algun dels modes de baix consum i per tant es pot reduir dràsticament el consum d'una aplicació (veure \fullref{ch:low-power}).
En el cas de FreeRTOS, el port disponible per Cortex-M ja incorpora aquesta característica, i es pot configurar editant el fitxer ``FreeRTOSConfig.h'', concretament fixant el valor '1' a la macro {\bf configUSE\_TICKLESS\_IDLE}. En el cas de Silicon Labs, el microcontrolador es posa en el mode EM2 igual que en cas amb {\em ticks} i es programa el RTC perquè generi una \gls{IRQ} en el temps adequat.
En ambdós casos el codi que gestiona el baix consum i els {\em ticks} en el port FreeRTOS està al fitxer {\bf low\_power\_tick\_management.c} a la funció {\bf vPortSetupTimerInterrupt()}\index{vPortSetupTimerInterrupt()}.
\chapter{Documentant el codi}
\label{sec:documentant}
Un tema recurrent en temes d'enginyeria del software és com documentar el codi font que es desenvolupa per tal d'afavorir, sobretot, el manteniment del codi durant el temps i algú altre (o nosaltres mateixos) haguem de modificar, re-uilitzar o arreglar algun problema. No farem aquí una discussió sobre els beneficis de documentar, quan fer-ho, etc.
Hi diferents tècniques i mètodes de documentar el codi, aquí veurem només una, basada en Doxygen. Aquest programa processa la documentació inserida dins el propi codi font i genera diferents sortides, la més habitual és una carpeta html amb tota la documentació ben bonica i accessible amb un navegador (té altres formats de sortida, com .pdf, .doc, etc.). Per documentar el nostre codi, el que cal que fem és escriure la documentació dins el propi codi com a comentaris de codi seguint unes normes i {\em tags} molt senzills propis de Doxygen. (veure Figura~\ref{fig:doxygencode}). Aquest mètode de documentar ha esdevingut un estàndard de facto i es troba arreu. Per documentar-se sobre com treballar amb Doxygen, la seva pàgina web està força bé amb exemples de tots tipus \cite{Doxygen}.
\begin{figure}
\centering
\fbox{\color{ocre}\includegraphics[width=0.85\textwidth, keepaspectratio]{imatges/Doxgen3.png}}
\caption{Comentari per doxygen dins un codi}
\label{fig:doxygencode}
\end{figure}
A simplicity (i de fet, a qualsevol IDE basat en Eclipse), podem activar Doxygen com l'eina de documentació, i d'aquesta manera l'editor ens ajudarà alhora d'escriure-la, ja que, per exemple, en escriure «/**» davant una funció ens inserirà automàticament el codi Doxygen per documentar-la (incloent-hi tots els paràmetres), simplificant molt la nostra feina.
Una bona opcio és afegir un directori on ficar-hi el fitxer de configuració del Doxygen (directori /Doc) i on es genera el codi html (directori /Doc/html). El Doxygen s'executa dins del directori /Doc i es genera el codi html (o pdf, o rtf, o el que calgui). Si al fitxer Doxygen li posem l'extensió .doxyfile el propi simplicity el reconeix com a fitxer de documentació i podem executar Doxygen pitjant el botó amb una arroba de color blau a la barra d'eines (Figura~\ref{fig:doxygenbutton}).
\begin{figure}[h!]
\centering
\fbox{\color{ocre}\includegraphics[width=0.35\textwidth, keepaspectratio]{imatges/Doxygen_button.png}}
\caption{Botons de Simplicity, l'arroba blava permet executar Doxygen}
\label{fig:doxygenbutton}
\end{figure}
També podrem editar de forma visual el fitxer de configuració fent-hi doble-click i veure el resultat obrint dins del Simplicity el fitxer /Doc/html/index.html (Figura~\ref{fig:doxygenconfig}).
\begin{figure}
\centering
\fbox{\color{ocre}\includegraphics[width=0.85\textwidth, keepaspectratio]{imatges/Doxygent_configuration.png}}
\caption{Configuració de Doxygen dins de Simplicity}
\label{fig:doxygenconfig}
\end{figure}
Hi ha un exemple complet al projecte FreeRTOS Queue (veure \fullref{sub:cues_exemple}). En aquest cas, l'explicació del projecte (la secció principal anomenada mainpage en Doxygen) està al final del fitxer main.c. També hi ha la possibilitat de posar aquesta secció en un fitxer a part, normalment un fitxer README.md. Si ho fem així, aquest fitxer README.md github el presenta a la pàgina principal del projecte. El fitxer generat també es pot obrir dins el propi Simplicity Studio (Figura~\ref{fig:doxygeneclipse}).
A més, si configurem com cal github, podem pujar el codi html generat per Doxygen al repositori i veure'l a un adreça de github. La de l'exemple està a \href{https://mariusmm.github.io/cursembedded/Simplicity/FreeRTOS_1/Doc/html/}{aquí} \cite{GITHUBPages} i es pot obrir des d'un navegador qualsevol,
\begin{figure}
\centering
\fbox{\color{ocre}\includegraphics[width=0.85\textwidth, keepaspectratio]{imatges/DoxygenEclipse.png}}
\caption{Pàgina web de documentació vista dins de Simplicity Studio}{Pàgina web de documentació vista dins de Simplicity Studio, en aquest cas es visualitza un dels fitxers locals}
\label{fig:doxygeneclipse}
\end{figure}
\chapter{CMSIS}
\label{ch:CMSIS}
\gls{CMSIS} és una proposta d'ARM per unificar les diferents biblioteques dels fabricants sota una sola especificació, de manera que un disseny es pugui migrar a un altre fabricant de Cortex sense gaires problemes. Hi ha diferents subconjunts d'aquesta proposta, anem a veure'ls un a un.
\section{CMSIS-Core}
\label{sec:CMSIS-Core}
Aquesta part de l'especificació fixa la forma de comunicar-se amb les parts més {\em core} de la CPU, com son: el mapa de memòria (\fullref{sub:memory-mapped}), el sistema d'excepcions (\fullref{ch:exceptions}), els registres de control de la CPU, el gestor d'interrupcions (\fullref{ch:IRQ}), el Systick (\fullref{sec:systick}) i les {\em caches} \cite{CMSIS-CORE}. En aquesta biblioteca s'inclouen també els fitxers d'inicialització de cada microcontrolador en concret (\fullref{sub:boot}).
Així, i a tall d'exemple, les funcions que ja hem fet servir per controlar interrupcions com {\bf NVIC\_EnableIRQ()}\index{NVIC\_EnableIRQ()} a \fullref{ch:IRQ} no són pròpies de cap fabricant si no que són funcions definides per {\bf CMSIS-core}. També la manera en que es defineixen estructures per accedir als diferents perifèrics ve marcada per l'especificació {\bf CMSIS-Core} (veieu \fullref{devinfo}).
\section{CMSIS-Driver}
\label{sec:CMSIS-Driver}
Aquesta especificació defineix una \gls{API} per tot un seguit de perifèrics per tal que els fabricants puguin implementar el {\em driver}
corresponent i els desenvolupadors no hagin de dependre de llibreries pròpies de cada fabricant. Aquesta especificació inclou els següents perifèrics \cite{CMSIS-DRIVER}:
\begin{itemize}
\item \gls{CAN}
\item Ethernet
\item \gls{I2C}
\item \gls{MCI}
\item NAND
\item \gls{FLASH}
\item \gls{SAI}
\item \gls{SPI}
\item Storage
\item \gls{USART}
\item \gls{USB}
\end{itemize}
Hi ha una implementació d'alguns dels mòduls feta per l'autor al repositori \href{https://github.com/mariusmm/CMSIS_Drivers}{GitHub} pels dispositius de Silicon Labs i de ST.
És interessant veure com s'ha organitzat el codi per part del dissenyador. El que fa és proporcionar un fitxer .h per cada driver on el que hi ha és una definició d'
una estructura que guarda tot de punters a cada funció del {\em driver} ({\em Initialize()}, {\em Uninitialize()}, {\em MasterTransmit()}, etc.). En aquest mateix fitxer incorpora
la documentació de totes les funcions en format Doxygen. A més, el dissenyador demana que hi hagi una estructura per cada perifèric real del dispositiu,
de manera que si tenim un microcontrolador amb 2 controladors I2C, cal que la implementació publiqui dos instàncies de l'estructura: I2C0 i I2C1 (o potser I2C1 i I2C2).
Això dona la possibilita d'escriure unes funcions adaptades a cada fabricant amb les seves biblioteques en un fitxer .c que queda ``ocult'' a l'usuari del {\em driver}.
En el cas de la implementació de l'autor aquestes estructures criden a funcions particulars per cada instància (I2C0\_Initialize(), I2C1\_Initialize(), etc.) que criden a
una única funció EFM32\_I2C\_Initialize() que rep com a paràmetre una estructura pròpia de cada perifèric (I2C0\_Resources o I2C1\_Resources de tipus EFM32\_I2C\_RESOURCES).
Aquesta estructura guarda la informació d'estat per cada perifèric (configuració de pins, de transmissió i recepció, funció de {\em callback} registrada, etc.).
Un cop fet això, cal escriure una funció per cada una de les funcionalitats que ha de proporcionar el {\em driver} i que rebi els paràmetres prestablers i a més l'estructura d'estat.
Aquestes funcions poden fer ús de les biblioteques dels fabricants o accedir directament als perifèrics per tal d'implementar la funcionalitat que toqui. Tot això, no obstant, queda
amagat cap a l'usuari del {\em driver}, que tant sols tindrà la instància del {\em driver} i les funcions estàndards publicades, com es veu al Llistat~\ref{CMSISDriverexample}.
\index{Driver\_I2C0.Initialize()}
\index{Driver\_I2C0.PowerControl()}
\index{Driver\_I2C0.MasterTransmit()}
\begin{lstlisting}[style=customc, caption=Exemple d'ús del {\em driver CMSIS I2C}, label=CMSISDriverexample]
#include "Driver_I2C.h"
...
Driver_I2C0.Initialize(i2c_event);
Driver_I2C0.PowerControl(ARM_POWER_FULL);
...
Driver_I2C0.MasterTransmit(dev_addr, tx_buff, 1, true);
...
\end{lstlisting}
\section{CMSIS-DSP}
\label{sec:CMSIS-DSP}
Aquesta biblioteca inclou totes les funcions específiques de tipus \gls{DSP} dels Cortex-M més avançats (Cortex-M4 i Cortex-M7) i funcions que treballen amb punt flotant per tot tipus de Cortex-M. Si el Cortex-M amb el que treballem suporta punt flotant, la biblioteca farà les operacions per HW, i les farà per SW en cas contrari \cite{CMSIS-DSP}\cite{AN0051}.
\section{CMSIS-RTOS}
\label{sec:CMSIS-RTOS}
Aquesta biblioteca defineix un conjunt de funcions i crides per ``amagar'' el sistema operatiu que es pugui fer servir, de manera que es pugui intercanviar el \gls{RTOS} sense afectar al codi d'aplicació \cite{CMSIS-RTOS}.
D'aquesta manera es tenen crides estàndard per les funcions habituals (crear tasques, semàfors, cues, etc., enviar dades a la cua, etc.) i així es pot, en principi, intercanviar el RTOS sense haver de canviar res del codi d'usuari. Fent servir aquesta API no cal conèixer les interioritats i particularitats de cada RTOS que es vulgui fer servir, ja que quedaran amagades i pre-configurades per la biblioteca.
Així tenim que ST proporciona un \gls{wrapper} de CMSIS-RTOS per FreeRTOS que s'integra fàcilment al seu IDE \cite{ST-CMSIS-RTOS}. Silicon Labs no proporciona suport per aquesta biblioteca, però es pot fer servir el \gls{wrapper} de codi obert disponible a \href{https://github.com/labapart/polymcu/tree/master/RTOS/FreeRTOS/cmsis}{GitHub}.
A part, es va crear una implementació de CMSIS-RTOS anomenada CMSIS-RTOS-RTX (o també Keil RTX) per part de Keil (empresa propietat d'ARM) \cite{Keil-RTX}.
\section{CMSIS-DAP}
\label{sec:CMSIS-DAP}
Més que una biblioteca, aquesta part de CMSIS és una definició de com ha de treballar un dispositiu que faci de pont entre un port USB i el port de configuració dels microcontroladors Cortex. Això possibilita que, per exemple, la placa de prototipat tingui un port USB i el puguem fer servir per programar el microcontrolador, tenir la consola de {\em debug} (SWO), poder inspeccionar registres de la CPU, etc. \cite{CMSIS-DAP}.
\section{CMSIS-NN}
\label{sec:CMSIS-NN}
Aquesta biblioteca està composta d'un seguit de funcions i algorismes per implementar xarxes neurals a processadors Cortex-M i queda fora de l'objectiu d'aquest llibre \cite{CMSIS_NN_paper}\cite{CMSIS-NN}.
\chapter{Normes de codificació}
\label{sec:GuiesProgramacio}
Per tal d'unificar estils de codi i per evitar possibles errors, és habitual seguir algun conjunt de normes de codificació quan es desenvolupa un projecte. Aquest costum de normes acostumen a ser una llista de recomanacions d'estil sobre l'escriptura del codi, normes sobre coses prohibides o no recomanades, etc, Per cara regla, s'acostuma a donar una breu explicació del motiu. Aquests conjunts de normes acostumen a ajudar a evitar {\em bugs} de difícil detecció.
\begin{remark}
Tot i que un conjunt de normes de codificació ajuda a no inserir {\em bugs}, les normes per si soles no poden garantir que no es generin {\em bugs} en un sistema complex. Cal sempre seguir les bones pràctiques de Test.% (veure \fullref{part:test}).
\end{remark}
Normes generals n'hi ha moltes i alguna de les mes populars és la coneguda com ``The Power of 10: Rules for Developing Safety-Critical Code'' (``El poder del 10: regles per desenvolupar codi crític'' \cite{powerof10}. En aquest document es presenten tant sols només 10 regles per ajudar a escriure codi més segur i menys propens a errors.
En àmbits molt específics hi ha normes i estàndards propis, com el DO-178 per l'àmbit aeri i espacial; IEC 61508, ISO 26262 o SAE J3061 per automoció o IEC 62304 per l'industria mèdica. Per l'àmbit espacial el JPL ({\em Jet Propulsion Laboratory}) té publicada una norma pròpia \cite{JPLCProgramming}.
També hi ha normes genèriques, que no es centren a cap àmbit concret. Les normes genèriques més habituals i conegudes són MISRA-C \cite{MISRAHomepage} i ``Embedded C Coding Standard'' \cite{BARRGuidelines}. Per espai, la \gls{ESA} fa servir el document ``C and C++ Coding Standards'' \cite{BSSC}.
Es descriuen breument als apartats següents.
\section{\em The Power of 10: Rules for Developing Safety-Critical Code}
Aquest conjunt de només 10 regles es va escriure per ajudar a l'anàlisi estàtic del codi i la revisió per desenvolupadors. Es poden resumir en:
\begin{itemize}
\item Evitar construccions complexes com {\em goto} i l'ús de recursivitat.
\item Tots els bucles han de tenir fitada la seva longitud.
\item Evitar l'ús de memòria dinàmica.
\item Restringir la llargada d'una funció a 60 línies.
\item Fer servir un mínim de dos comprovacions en temps d'execució per cada funció.
\item Restringir la vida de les dades el més possible.
\item Comprovar el valor de retorn de totes les funcions que retornen un valor.
\item Poc ús del pre-processador.
\item Limitar l'ús de punters a una sola indirecció i no usar punters a funcions.
\item Compilar amb tots els {\em warnings} activats. Resoldre sempre tots els {\em warnings} abans de publicar el codi.
\end{itemize}
\section{MISRA-C}
\label{sec:MISRA}
MISRA C és un conjunt de normes i guies per programar en codi C per sistemes encastats. Es va proposar per primer cop el 1997 per l'associació MISRA (sigles de {\em Motor Industry Software Reliability Association}) i ha tingut diverses revisions, la tercera i última es va publicar el 2012 \cite{MISRAHomepage}\cite{MISRAC2012}.
Aquestes especificacions cal comprar-les (la versió digital costa 15 lliures) i no es poden redistribuir lliurement, però si podem tenir accés a algun addenda per veure com són aquestes normes \cite{MISRAAmend}.
Aquestes normes es divideixen en 3 classificacions segons el grau d'obligatorietat:
\begin{itemize}
\item {\em Mandatory} són normes que s'han de complir sense cap excepció
\item {\em Required} són normes a complir però es poden incomplir si hi ha una explicació racional (anomenada {\em Deviations}
\item {\em Advisory} que són normes optatives, però no cal complir-les, tot i que es recomana fer-ho.
\end{itemize}
Les normes consten d'una frase dient què s'ha de fer o no s'ha de fer, una explicació del perquè de la norma i un exemple de l'ús correcte.
Així si mirem a l'addenda 1 \cite[4]{MISRAAmend} (que és de lliure distribució i accés), la regla 21.14 diu que la funció {\bf memcmp()}\index{memcmp()} no s'ha de fer servir en altre cosa que no siguin cadenes acabades en NULL ('\textbackslash 0'). Aquesta norma evita que es puguin fer servir {\em buffers} d'una mida superior a la cadena de text que guarden i provoqui errors que poden ser molt complexes de trobar.
Existixen eines que automàticament comproven la conformitat d'un projecte o codi a les normes MISRA. Entre aquestes eines, algun compilador fa la comprovació en temps de compilació (ho fan els compiladors d'IAR i de TI).
Per últim, cal dir que hi ha força controvèrsia amb d'idoneïtat de seguir les normes MISRA, donat les limitacions que provoca al desenvolupador i les suposades avantatges que proporciona.
\section{\em Embedded C Coding Standard}
Aquestes normes són de lliure accés i escrites pel Barr Group. Conté regles tant d'estil de text (número de caràcters per línia, on posar els '\{', etc.) com regles de sintaxi en C, com per exemple quan i on usar la paraula reservada {\em volatile}, etc. Segons el mateix document, aquestes regles són més laxes que les normes MISRA \cite{BARRGuidelines}.
En aquest cas, cada regla consta de l'explicació de la regla en si mateixa, el raonament que hi ha per definir la regla, quan pot haver-hi una excepció i com aplicar-la.
També hi ha eines per comprovar que el codi escrit segueix aquestes normes.
\section{\em JPL Institutional Coding Standard for the C Programming Language}
Aquesta normes de codificació venen d'un laboratori del JPL per tal d'aconseguir millor seguretat i qualitat en el software que s'escriu a les sondes espacials d'aquesta institució \cite{JPLLARS}.
Les normes de codificació son una ampliació de les normes MISRA per afegir-hi sistemes multi-tasca \cite{JPLCProgramming}. Es defineixen nivells d'acompliment amb les normes, anant des de LOC-1 fins a LOC-4 amb un total de 120 regles. La majoria de regles son equivalents a algunes de les normes MISRA. Els dos últims nivells d'acompliment (LOC-5 i LOC-6) consisteixen a acomplir amb totes les regles obligatòries o opcionals de les normes MISRA.
Així, com a diferència de regles que es poden trobar a d'altres normes de codificació, aquestes afegeixen regles com la Regla 6, que demana que sempre es facin servir mecanismes IPC per comunicar tasques entre si, i que cap tasca ha d'accedir a dades o executar codi d'altres tasques. La Regla 7 demana que les tasques no se sincronitzin fent servir {\em delay}.
\chapter{DSP}
\label{ch:DSP}
Com ja s'ha comentat, els Cortex-M4 i Cortex-M7 suporten instruccions addicionals de tipus \gls{DSP} \cite[173]{GuideCortexM3M4}\cite[255]{DesignersGuide}:
\begin{itemize}
\item Instruccions tipus \gls{SIMD}
\item Instruccions de saturació
\item Instruccions addicionals de multiplicació i \gls{MAC}
\item Instruccions de empaquetar i desempaquetar
\item Opcionalment, instruccions de punt flotant
\end{itemize}
Aquestes instruccions s'afegeixen al conjunt d'instruccions màquina de la CPU i permeten que els processadors Cortex-M puguin implementar algorismes de DSP de forma prou eficient. Com que moltes d'aquestes instruccions i nous tipus de dades no són estàndard dins els compiladors de C més habituals, \gls{ARM} proporciona la biblioteca CMSIS-DSP (veure \fullref{sec:CMSIS-DSP}). Aquesta biblioteca, curiosament, es pot fer servir tant en Cortex-M4 i M7, com en Cortex-M3 i M0 que no tenen instruccions específiques de DSP.
Per fer-la servir cal fer, almenys, dues passes:
\begin{enumerate}
\item Definir un símbol de compilació segons el processador amb el que estiguem treballant ({\bf ARM\_MATH\_CM0}, {\bf ARM\_MATH\_CM3}, {\bf ARM\_MATH\_CM4}).
\item Afegir la biblioteca pre-compilada al nostre projecte (er això cal afegir també el {\bf PATH} on està situada la biblioteca) tal com es veu a la Figura~\ref{fig:EnableDSP}.
\end{enumerate}
\begin{figure}
\centering
\includegraphics[width=0.85\textwidth, keepaspectratio]{imatges/EnablingDSP_Lib.png}
\caption{Configuració del Simplicity Studio afegint-hi la biblioteca CMSIS-DSP}
\label{fig:EnableDSP}
\end{figure}
La documentació de la biblioteca proporciona totes les funcions implementades així com un conjunt d'exemples dels usos més comuns \cite{CORE-DSP}. SiliconLabs també proporciona documentació en un {\em Application Note} sobre la biblioteca \cite{AN0051}.
\chapter{C++ vs C}
\label{ch:CvsCPP}
En aquest llibre s'ha treballat exclusivament en llenguatge C (versió C99) i no s'ha parlat res de C++. Anem a fer-ho ara en aquest capítol.
La discussió sobre usar o no C++ en sistemes encastats deu ser tant antiga com l'aparició d'aquest llenguatge orientat a objectes. Si bé als seus inicis el llenguatge presentava força problemes, ja fa molts anys que és un llenguatge estable i candidat a ser usat en sistemes encastats. Tot i això, la seva popularitat ha estat desigual i encara hi ha molts equips de desenvolupadors de sistemes encastats que treballen exclusivament en C.
Els problemes habituals que s'ha acusat al C++ per no fer-lo servir en sistemes encastats són els següents \cite{CXX_1}:
\begin{itemize}
\item codi més llarg: si bé això pot ser veritat, les mides de les memòries \gls{FLASH} dels microcontroladors és cada cop més gran i els compiladors moderns generen codi força optimitzat, a més que es poden desactivar opcions del llenguatge que no es fan servir.
\item més lent: això era cert amb els primers compiladors de C++, però actualment el codi generat és de la mateixa qualitat que el generat pels compiladors de C.
\item més {\em stack}: seguint les mateixes normes que amb C, és possible tenir codi C++ que faci un ús correcte de l'{\em stack}
\end{itemize}
En canvi, els avantatges que ens pot proporcionar treballar amb C++ poden ser:
\begin{itemize}
\item comprovació de tipus en temps de compilació. C és força laxe en aquest tema, i això pot conduir a errors. C++ és capaç de fer comprovacions en temps de compilació per avaluar la correcció de les conversions.
\item {\em namespaces}, que permeten classificar i organitzar el codi d'una forma intuïtiva i senzilla.
\item constructors i destructors permeten inicialitzar i destruir o netejar estructures de forma automàtica.
\item orientació a objectes, l'organització del codi en objectes pot ajudar a ordenar i simplificar el codi.
\item sobrecàrrega d'operadors, fent que operacions entre objectes sigui senzilla amb un codi resultant força senzill.
\end{itemize}
També cal recordar que no cal fer servir totes les noves capacitats de C++ respecte a C de cop, si no que es poden anar incorporant poc a poc al nostre codi conforme anem guanyant experiència i coneixements.
Dues de les característiques de C++ que ocupen força memòria són el \gls{RTTI} i el control d'excepcions. RTTI dona informació del tipus de classes polimòrfiques (que tenen almenys un mètode virtual) i és una característica que es faci servir gaire en sistemes encastats. El control d'excepcions permet l'execució d'un mètode i capturar l'error que es pugui generar i tractar-lo fora de la funció i de forma controlada.
Aquestes dues característiques de C++ afegeixen força codi a qualsevol projecte amb el que treballem, fent que, per exemple, no puguem compilar un simple ``Hello World embedded'' per la nostra placa de desenvolupament ja que ocupa massa FLASH. Les opcions per deshabilitar aquestes funcions al compilador GNU (que és el compilador utilitza Simplicity Studio) son:
\begin{verbatim}
-fno-rtti -fno-exceptions
\end{verbatim}
\begin{figure}
\centering
\includegraphics[width=0.85\textwidth, keepaspectratio]{imatges/CXX_options.png}
\caption{Configuració Simplicity Studio per deshabilitar RTTI i les excepcions}
\label{fig:CXX_RTT}
\end{figure}
i es configura tal com es veu a la Figura~\ref{fig:CXX_RTT}.
\section{Primer exemple en C++}
\label{sec:CXX_example}
\href{https://github.com/mariusmm/cursembedded/tree/master/Simplicity/CXX_1}{L'exemple CXX\_1} és el típic ``Hello World'' per sistemes encastats escrit en C++.
Aquest exemple fa servir dues classes dins el {\em namespace} {\bf BSP}.
\subsection{LED}
Com el seu nom indica, serveix per controlar l'únic \gls{LED} de la \gls{PCB} de prototipat. Està basada en una classe amb tres mètodes senzills per controlar un sol LED (LED::On(), LED::Off(), LED::Toggle())\index{LED::On()}\index{LED::Off()}\index{LED::Toggle()}.
Dins el constructor s'activa el rellotge pel perifèric \gls{GPIO} i es configura el pin corresponent al LED de la PCB (Llistat~\ref{LED_class}).
\begin{lstlisting}[caption={Part del codi de la classe LED},style=customc,label=LED_class]
LED::LED() {
CMU_ClockEnable(cmuClock_GPIO, true);
GPIO_PinModeSet(gpioPortD, 7, gpioModePushPullDrive, 0); /* LED */
}
...
void LED::On() {
GPIO_PinOutSet(gpioPortD, 7);
}
\end{lstlisting}
\subsection{Button}
Aquesta classe gestiona el valor d'una entrada del \gls{GPIO} d'una fora senzilla, la classe {\bf Button} emmagatzema els paràmetres d'un pin d'E/S i abstreu les crides a la biblioteca {\bf emlib} de Silicon Labs (veure Llistat~\ref{Button_class}).
\begin{lstlisting}[caption={Part del codi de la classe LED},style=customc,label=Button_class]
Button::Button(GPIO_Port_TypeDef port, int pin, bool pull, bool pullup) {
CMU_ClockEnable(cmuClock_GPIO, true);
m_port = port;
m_pin = pin;
m_pull = pull;
m_pullup = pullup;
if (m_pull == false) {
GPIO_PinModeSet(port, pin, gpioModeInput, 0);
} else {
if (m_pullup == true) {
GPIO_PinModeSet(port, pin, gpioModeInputPull, 1);
} else {
GPIO_PinModeSet(port, pin, gpioModeInputPull, 0);
}
}
}
bool Button::getValue() {
unsigned int pin_value;
pin_value = GPIO_PinInGet(m_port, m_pin);
if (pin_value == 0) {
return false;
} else {
return true;
}
}
\end{lstlisting}
\subsection{Un {\em Hello World} ``més C++''}
A continuació modifiquem l'exemple per donar-li una volta més i que sigui més ``estil C++'' (està al \href{https://github.com/mariusmm/cursembedded/tree/master/Simplicity/CXX_2}{repositori}). El que s'ha fet ha estat crear una nova classe {\bf Pin} que abstrau la informació d'un pin GPIO d'EFM32. La classe {\bf Button} fa servir {\bf Pin} per obtenir les característiques del GPIO a controlar.
\subsection{Mida dels executables}
\label{CXX_size}
A \href{https://github.com/mariusmm/cursembedded/tree/master/Simplicity/CXX_1}{l'exemple CXX\_1} tenim el ``Hello World embedded'' fet en C++ de manera bàsica. A \href{https://github.com/mariusmm/cursembedded/tree/master/Simplicity/CXX_2}{l'exemple CXX\_2} s'ha fet una implementació ``més C++'' amb la mateixa funcionalitat. A la Taula~\ref{tb:CXX_size} es pot veure la quantitat de memòria de tot tipus que necessiten les dues aplicacions així com l'exemple bàsic en C.
\begin{table}[!htbp]
\caption{Ocupació de memòria de ``Hello World embedded '' en C i C++ (tots els projectes compilats amb optimització -O2).}
\centering
\begin{tabular}{|c|c|c|c|}
\hline
{\bf Aplicació} & {\bf text} & {\bf data} & {\bf bss}\\
\hline
{\bf GPIO\_1} & 972 & 108 & 28 \\
\hline
{\bf CXX\_1} & 1836 & 112 & 32 \\
\hline
{\bf CXX\_2} & 2076 & 112 & 32 \\
\hline
\end{tabular}
\label{tb:CXX_size}
\end{table}
Com a curiositat, l'ús de {\em std::cout} de la biblioteca {\em iostream} i l'operador {\bf <{}<} afegeix uns 150KB de codi FLASH (!!!), fent que sigui poc recomanable o impossible de fer servir en un sistema encastat actual.
\section{Un {\em driver} en C++}
Com hem vist al llarg del llibre, bona part del codi són {\em drivers} per controlar els diferents perifèrics o dispositius del nostre sistema encastat. Si treballem en C++, caldrà que aquest {\em drivers} els fem també en C++. Veurem ara un exemple amb la \gls{UART}, escrivint un {\em driver} i un exemple igual al vist a \fullref{sec:UART_example_2}.
En \href{https://github.com/mariusmm/cursembedded/tree/master/Simplicity/CXX_UART}{aquest exemple} tenim una classe \gls{UART} que és la implementació del {\em driver} per la UART que es va veure a l'exemple de la Secció \ref{sec:UART_example_2}. Aquesta classe {\bf UART}\index{UART class} fa servir {\em buffers} circulars per emmagatzemar les dades que es reben o s'han d'enviar per la UART i té els mètodes {\bf AvailableData()}, {\bf GetData()} i {\bf SendData()}\index{UART::AvailableData()}\index{UART::GetData()}\index{UART::SendData()} com ja tenia el mòdul UART de l'exemple en C. Aquests mètodes tant sols accedeixen al {\em buffer} circular adequat (de transmissió o recepció) que està implementat a la classe {\bf CircularBuffer} \index{CircularBuffer class}.
Tal com es veu al Llistat~\ref{operator_UARTCXX} s'ha sobrecarregat l'operador {\bf<{}<} per fer més fàcil l'ús de la classe a l'hora d'enviar dades i poder escriure codi com el del Llistat~\ref{operator_UARTCXX_example}.
\index{UART::<{}<}
\begin{lstlisting}[style=customc,caption=Ús de l'operador <{}< de la classe UART,label=operator_UARTCXX_example]
my_uart << "Testing" << " C++ string style";
\end{lstlisting}
\index{UART::<{}<}\index{UART class}\index{UART::Tx()}
\begin{lstlisting}[style=customc,caption=Implementació de l'operador <{}< per la classe UART,label=operator_UARTCXX]
class UART {
...
UART& operator<<(char* str) {
for(char* it = str; *it; ++it) {
this->Tx(*it);
}
return *this;
}
UART& operator<<(std::string str) {
for(std::string::iterator it = str.begin(); it != str.end(); ++it) {
this->Tx(*it);
}
return *this;
}
...
void UART::Tx(unsigned char c) const {
USART_Tx(m_uart, c);
}
...
}
\end{lstlisting}
La resta del codi és prou autoexplicatiu a excepció de l'implementació de les \glspl{ISR} de la UART. En aquest cas ens trobem que les \glspl{ISR} haurien d'estar encapsulades dins la pròpia classe UART\index{UART class} però això no és possible, donat que la classe no és estàtica, i per tant ``no existeix'' fins que no es crea instanciant un objecte d'aquest tipus \cite{ISRCXX}\cite{ISRCXX_2}. Una possible solució a aquest problema és el que es veu al codi~\ref{ISR_UARTCXX}: es té el codi pròpiament dit de la \gls{ISR} a uns mètodes privats de la classe del {\em driver} (en aquest cas la classe UART) i en algun altre lloc del codi (en aquest exemple al fitxer {\em main}\index{main()}) s'insereix la construcció que es veu al Llistat~\ref{main_ISR_UARTCXX}. D'aquesta manera les \glspl{ISR} criden als mètodes adequats de la classe pertinent.
\index{UART::USART1\_TX\_IRQHandler()}\index{UART::USART1\_RX\_IRQHandler()}\index{UART class}
\begin{lstlisting}[style=customc,caption=Implementació de les ISRs en C++,label=ISR_UARTCXX]
void UART::USART1_TX_IRQHandler(void) {
USART_IntClear( USART1, USART_IEN_TXC);
Send();
}
void UART::USART1_RX_IRQHandler(void) {
char data;
if (USART1->IF & LEUART_IF_RXDATAV) {
data = USART_Rx(USART1);
m_RX.PushData(data);
USART_IntClear( USART1, USART_IEN_RXDATAV);
}
}
class UART {
...
friend void USART1_TX_IRQHandler();
friend void USART1_RX_IRQHandler();
private:
void USART1_TX_IRQHandler(void);
void USART1_RX_IRQHandler(void);
...
}
\end{lstlisting}
\index{USART1\_TX\_IRQHandler()}\index{USART1\_RX\_IRQHandler()}\index{UART class}
\begin{lstlisting}[style=customc,caption=Part del fitxer UART.cpp de l'exemple d'us del {\em driver} en C++ per la UART,label=main_ISR_UARTCXX]
static UART* helper_uart;
void USART1_TX_IRQHandler() {
helper_uart->USART1_TX_IRQHandler();
}
void USART1_RX_IRQHandler() {
helper_uart->USART1_RX_IRQHandler();
}
\end{lstlisting}
\subsection{Ocupació de memòria}
De nou, anem a analitzar l'espai de memòria necessari per aquest exemple comparat amb l'exemple escrit en C amb la mateixa funcionalitat.
El codi en C++ es compila amb 3 variants:
\index{UART::<{}<}
\begin{itemize}
\item Sobrecarregant l'operador {\bf <{}<} que pugui rebre dades de tipus {\em char}.
\item Sobrecarregant l'operador {\bf <{}<} que pugui rebre dades de tipus {\em std::string}.
\item Sense sobrecarregar l'operador.
\end{itemize}
Els resultats es mostren a la Taula~\ref{tb:UAR_CXX_size_O2}. Es pot veure que l'ús de l'operador que suporta {\em std::string} afegeix força codi ROM (segona columna a la Taula, uns 2 KB) i que, en general, l'ús de C++ afegeix un sobrecost en espai ROM al nostre codi. Potser el més destacable és que la quantitat de RAM necessària no s'incrementa de manera significativa, sent aquest recurs el més escàs en un microcontrolador.
% \begin{table}[!htbp]
% \caption{Ocupació de memòria d'exemple amb la UART en C i C++ (tots els projectes compilats sense optimització -O0).}
% \centering
% \begin{tabular}{|c|c|c|c|}
% \hline
% {\bf Aplicació} & {\bf text} & {\bf data} & {\bf bss}\\
% \hline
% {\bf Sense operador <{}<} & 5436 & 120 & 40 \\
% \hline
% {\bf Amb operador <{}< i char} & 5452 & 120 & 40 \\
% \hline
% {\bf Amb operador <{}< i std::string} & 7812 & 128 & 168 \\
% \hline
% {\bf Original C} & 4424 & 116 & 184\\
% \hline
% \end{tabular}
% \label{tb:UAR_CXX_size_O0}
% \end{table}
\begin{table}[!htbp]
\caption{Ocupació de memòria d'exemple amb la UART en C i C++ (tots els projectes compilats amb optimització -O2, en KB).}
\centering
\begin{tabular}{|l|c|c|c|}
\hline
{\bf Aplicació} & {\bf text} & {\bf data} & {\bf bss}\\
\hline
{\bf Sense operador <{}<} & 4636 & 120 & 40 \\
\hline
{\bf Amb operador <{}< i char} & 4644 & 120 & 40 \\
\hline
{\bf Amb operador <{}< i std::string} & 6796 & 128 & 168 \\
\hline
{\bf Original en C} & 2620 & 116 & 184\\
\hline
\end{tabular}
\label{tb:UAR_CXX_size_O2}
\end{table}
\section{Conclusions}
Tot i que l'ús de C++ enlloc de C incrementa la mida de l'executable final i les seves necessitats de memòria, el seu ús pot estar justificat en casos on l'encapsulació que proporciona C++ ajudi a la claredat del codi o a la portabilitat del mateix a diferents plataformes.
En qualsevol cas, cal una expertesa en el llenguatge per fer-ne un bon ús per tenir en compte les particularitats d'escriure codi C++ per sistemes encastats.
\chapter{Relació Esquemàtic i FW}
\label{ch:schematic}
Quan es dissenya un sistema encastat, una de les parts més importants i on contribueixen perfils professionals de diferent mena és la del disseny de l'esquemàtic. Aquest document especifica tots els detalls hardware de la connexió dels dispositius del sistema, els diferents dominis d'alimentació, els diferents rellotges del sistema, etc. Tots aquests aspectes influiran i seran influïts per, entre d'altres, el disseny de \gls{FW} i les característiques particulars del microcontrolador triat. És per tot això que en aquesta primera fase de disseny, cal implicació de part de l'equip de \gls{FW}.
\section{Selecció de pin-out}
Els microcontroladors actuals tenen la capacitat de poder cablejar la sortida o entrada d'un dels perifèrics a diferents pins del mateix. Per exemple, els dos pins del bus I2C (SCL i SDA, veure \fullref{sub:I2C}), en cert dispositiu de Silicon Labs (EFM32TG840) es poden cablejar cap a: PA0/PA1, PD6/PD7, PC6/PC7, PF0/PF1, PE12/PE13 \cite[50]{EFM32TG840}. A nivell de \gls{FW} serà indiferent fer servir un o altre conjunt de pins (tant sols caldrà canviar la configuració) però a nivell d'esquemàtic i a l'hora de fer la \gls{PCB} pot ser un canvi important.
Un altre aspecte a tenir en compte serà el dels pins d'entrada que poden o no generar \gls{IRQ}, de quina mena, etc. Per exemple, ja s'ha comentat que a la família STM32 de ST, els pins amb el mateix nombre generen la mateixa interrupció, així el pin PE6 genera la mateixa interrupció (EXTI6) que el pin PA6 \cite[382]{STM32F4RM}. De forma similar, a EFM32 els pins generen una interrupció o una altra segons tinguin numeració parell o senar i només un pin de cada conjunt amb el mateix nombre pot generar interrupció (només un dels pins de cada conjunt pot generar \gls{IRQ}: PA1, PB1, PC1, PD1, etc.) \cite[471]{EFM32TGRM}. Aquestes particularitats de cada família poden ser un inconvenient pel disseny \gls{FW} del sistema i caldrà tenir-ho en compte alhora de dissenyar l'esquemàtic i el \gls{FW} associat.
\section{Selecció de rellotges}
Un altre aspecte important és el de triar la freqüència de funcionament del rellotge del sistema i d'altres rellotges auxiliars. Com ja s'ha comentat a \fullref{ch:low-power}, la freqüència de funcionament del sistema és un dels factors més importants en el consum del microcontrolador. Com és evident, també afecta de forma directa al rendiment del sistema i a la seva capacitat de càlcul, procés de dades i resposta a esdeveniments.
També, però, és important la freqüència triada per la generació d'altres freqüències que necessitin alguns perifèrics. Per exemple, la USART necessita certes freqüències de rellotge per poder treballar amb els {\em bit-rates} més habituals. Els fabricants proporcionen mètodes per calcular les millors opcions de freqüències segons el {\em bit-rate} desitjat o taules amb paràmetres precalculats \cite[153]{EFM32TGRM} \cite[980]{STM32F4RM}.
Cal tenir en compte que, sovint, els microcontroladors tenen més d'un arbre de rellotges (veure \fullref{sec:clocks}) i que cal triar bé quins oscil·ladors i a quina freqüència treballaran.
\section{Canvis durant el {\em layout}}
Per últim, succeeix sovint que certes connexions de l'esquemàtic es canvien en l'etapa de \gls{layout} per necessitats del disseny. Pot ser que per poder {\em routejar} millor una línia es demani de canviar de pin. Això provocarà canvis en el \gls{FW} que s'hauran de tenir en compte. Si hem fet bé el disseny del nostre codi, només caldrà fer algun canvi senzill al nostre \gls{BSP} (veure \fullref{sec:BSP}).
\section{De la placa de prototipat a PCB pròpia}
Un altre dels canvis importants és el de passar de treballar amb una placa de prototipat o de desenvolupament a poder-ho fer en una PCB pròpia. La placa de prototipat porta muntat cert nombre de dispositius externs que segurament no estaran presents a la nostra PCB.
\subsection{Mecanisme de programació}
Un dels canvis més notoris és de l'absència del programador integrat a la PCB pròpia. Les plaques de desenvolupament modernes acostumen a integrar el programador, de manera que la placa de prototipat s'alimenta i es programa a través d'un connector USB estàndard. Això amaga que a la pròpia placa de desenvolupament hi ha tota el circuit per programar el microcontrolador principal. De fet, a la placa de prototipat que estem fent servir, hi ha un microcontrolador que rep les comandes del {\em debugger} per USB i les transforma a les comandes adequades per programar el microcontrolador principal a través del port {\em SWD} \cite[30]{USERMANUALDEVKIT}.
Aquest circuit no s'acostuma a posar les PCBs de productes finals, si no que es deixa disponible d'alguna manera (connector, pins, {\em pads}) a la PCB l'accés directe al port {\em SWD} del microcontrolador. Això provoca que calgui un programador extern a la PCB per tal de poder programar el microcontrolador. Això es pot fer comprant un dispositiu {\em debugger}, tot i que també es pot fer servir una de les plaques de prototipat perquè faci de programador de qualsevol microcontrolador extern connectat a través del port de DEBUG.
A part d'aquesta mena de programació pel port SWD, es pot tenir en compte que alguns microcontroladors porten un {\em bootloader} en HW que permet actualitzar el Firmware via un port sèrie (veure \fullref{sec:Bootloader}) \cite{AN0003}. Això pot simplificar la programació del microcontrolador, però cal tenir en compte que aquesta mena de comunicació no permet {\em debug}.
% \subsection{BSP}
% Com ja s'ha comentat prèviament (\fullref{sec:BSP}), l'ús d'un BSP permetrà canviar els ports i pins dels diferents perifèrics sense haver de canviar codi arreu del projecte.
\subsection{Migració vertical}
Treballant amb la placa de prototipat es poden avaluar les prestacions del sistema així com les necessitats de memòria, tant \gls{FLASH} com \gls{RAM}. Usualment, i per temes de costos, s'acostuma a triar el microcontrolador de la mateixa família més senzill i barat que compleixi els requeriments trobats.
Aquest canvi també pot causar canvis al nostre \gls{FW}, ja que pot ser que diferents models tinguin un mapat de pins diferents, o algun perifèric no es pugui {\em routejar} al pin que s'havia previst, etc. Aquesta informació acostuma a estar al {\em Reference Manual} de cada família, a \cite[8]{EFM32TGRM} es veu la taula resum de cada model (anomenats {\em parts} en anglès).
Per migració vertical ens refereix a la possibilitat de canviar de família dins un mateix fabricant mantenint el mateix encapsulat físic de manera que es poden incrementar les capacitats del microcontrolador sense haver de fer canvis a la \gls{PCB}.
Per exemple, es pot consultar els {\em datasheets} de la família {\em Tiny Gecko} (Cortex-M3) \cite[72]{EFM32TGDS}, {\em Zero Gecko} (cortex-M0+) \cite[66]{EFM32ZGDS} i {\em Happy Gecko} (Cortex-M0+)\cite[76]{EFM32HGDS} i es veurà que el mateix encapsulat, per exemple un QFN24, és compatible pin a pin amb qualsevol de les tres famílies (veure Figura~\ref{fig:pinout}). Així, en el nostre disseny podrem posar un Cortex més potent o menys segons l'aplicació o les necessitats sense haver de canviar el dibuix de la PCB. El mateix passa amb altres encapsulats i models tant en aquesta fabricant com a d'altes.
Com que actualment les biblioteques que ofereixen són compatibles entre diferents famílies (o si més no, molt similars) la transició entre diferents famílies acostuma a ser força senzill. La biblioteca {\bf emib} no presenta canvis entre diferents famílies de Cortex, per tant el nostre codi no haurà de recollir cap canvi en aquest sentit.
\begin{figure}[b]
\centering
\includegraphics[width=0.65\textwidth, keepaspectratio]{imatges/pinout.png}
\caption{Pinout pels microcontroladors}{Pinout pels microcontroladors EFM32ZG, EFM32HG i EFM32TG amb encapsulat QFN24 (extret de \cite[72]{EFM32TGDS}).}
\label{fig:pinout}
\end{figure}
\chapter{Inicialització del sistema i del llenguatge C}
\label{ch:initialization}
És la funció main() realment la primera funció que s’executa quan comença l’execució?
Qui implementa les funcions malloc()/free()?
Ara toca aprendre sobre els interiors del runtime de C i com s’inicialitza tot el sistema
Abans no comenci l’execució del nostre programa s’executen tot de funcions
per preparar tant el microcontrolador com l’entorn d’execució de C.
Comencem per l’inici: quan el microcontrolador surt de l’estat de reset,
el que fa és anar a executar el ResetHandler\index{ResetHandler} que està a l’adreça per defecte
del Program Counter (registre pc).
Aquesta funció la trobem definida al fitxer startup\_gcc\_efm32tg.s al directori
CMSIS del projecte i aquest handler tant sols crida la funció SystemInit()\index{SystemInit()}, tal
com es veu a la Figura~\ref{fig:resethandlercortex}.
\begin{figure}
\centering
\includegraphics[width=0.50\textwidth, keepaspectratio]{imatges/resethandlercortex.png}
\caption{Reset Handler per Cortex-M}
\label{fig:resethandlercortex}
\end{figure}
Podem dir-li al debugger que s’aturi en aquesta funció canviant-li la
configuració i dient-li que s’aturi a la funció que vulguem, en aquest cas hi
podem escriure Reset\_Handler\index{Reset\_Handler}. Per defecte veurem que està configurat per
que s’aturi a la funció main() (Figura~\ref{fig:debugstopat}).
\begin{figure}
\centering
\includegraphics[width=0.85\textwidth, keepaspectratio]{imatges/debugstopat.png}
\caption{Configuració del {\it Debugger}}
\label{fig:debugstopat}
\end{figure}
Aquesta funció està definida pel fabricant i la trobem al fitxer system\_efm32tg.c
al mateix directori. En el cas dels Cortex-M el que fa és modificar el registre
VTOR de la CPU per a que apunti a la taula de vectors d’interrupció definits
al fitxer startup\_gcc\_efm32tg.s.
A continuació segueix executant-se el Reset Handler, i el primer que fa és
copiar la secció {\bf .data} a la RAM (Figura~\ref{fig:copy_data_efm32}). Això què vol dir? Doncs que les variables
que s’han inicialitzat amb algun valor inicial al nostre codi s’han
emmagatzemat al fitxer binari a continuació del codi (secció {\bf .text}).
Abans de començar a funcionar el codi, cal copiar aquestes variables a la
memòria RAM, que és la secció {\bf .data}. Així, quan aquesta part de la
inicialització acaba, tenim les variables a la memòria RAM amb els seus
valors inicials.
\begin{figure}[h]
\centering
\includegraphics[width=0.85\textwidth, keepaspectratio]{imatges/copy_data_efm32.png}
\caption{Còpia de la secció .bss a la memòria RAM}
\label{fig:copy_data_efm32}
\end{figure}
Un acabada aquesta còpia, es crida a la funció \_start()\index{\_start()} de la biblioteca que
estiguem fent servir. En el cas de EFM32 la biblioteca és la Nano C library.
Aquesta biblioteca implementa les llibreries estàndard de C (stdlib, string,
memory, etc.) i li cal una inicialització que es troba al fitxer crt0.S (situada
a newlib/libc/sys/arm/crt0.S).
\begin{figure}
\centering
\includegraphics[width=0.85\textwidth, keepaspectratio]{imatges/crt0_setstack-1.png}
\caption{Inicialització del registre d'stack (Stack Pointer)}
\label{fig:crt0_setstack-1}
\end{figure}
El que es fa aquí és inicialitzar el punter de l’stack a l’adreça que s’indiqui al
fitxer del linker i posar a zero tota la memòria de la secció {\bf .bbs}
(Figures~\ref{fig:crt0_setstack-1} i \ref{fig:crt0_bssinit}).
\begin{figure}
\centering
\includegraphics[width=0.85\textwidth, keepaspectratio]{imatges/crt0_bssinit.png}
\caption{Inicialització de la secció .bss}
\label{fig:crt0_bssinit}
\end{figure}
A continuació es crida la funció \_\_libc\_init\_array()\index{\_\_libc\_init\_array()}
(situada a newlib/libc/misc/init.c) que va cridant funcions d’inicialització
de la pròpia biblioteca (i constructors estàtics si treballem en C++).
Finalment, la funció \_start()\index{\_start()} crida a la funció main() del nostre programa i ja
comença a executar-se el nostre codi (Figura~\ref{fig:crt0_callmain}).
\begin{figure}
\centering
\includegraphics[width=0.85\textwidth, keepaspectratio]{imatges/crt0_callmain.png}
\caption{Crida a la funció \_init() i funció main()}
\label{fig:crt0_callmain}
\end{figure}
Com es pot veure, no és trivial engegar un microcontrolador i tenir l’entorn del
llenguatge C preparat, però per sort tenim aquestes biblioteques i els fitxers
que ens donen els fabricants per fer-ho sense que ens n’haguem de preocupar.
\chapter{Treballant amb punt flotant}
Sempre s'ha dit que cal evitar l'ús de variables en punt flotant ({\it float} o
{\it double}) en sistemes encastats. Això ve dels temps en que els
microcontroladors disponibles no tenien cap unitat hardware de punt flotant i
aquestes operacions s'havien de fer per software i això penalitzava moltíssim el
rendiment.
Això encara és aplicable per la majoria de casos, tot i que els nous
microcontroladors basats en Cortex-M4 o Cortex-M7 poden portar unitats de punt
flotant. Anem a veure amb detall una mica com treballar amb les eines i el codi
perquè tot funcioni correctament.
Treballarem amb dos exemples molt senzills, que son les funcions {\bf mulf()} i
{\bf muld()} (Llistats~\ref{mulf} i \ref{muld}), que multipliquen dos valors de tipus
{\it float} i {\it double} respectivament.
Cal recordar que el tipus {\it float} correspon a un tipus de punt flotant de 32 bits
conegut com {\it single precision} seguint l'standard IEEE 754. {\it Double} correspon a
un tipus de 64 bits conegut com {\it double precision} del mateix standard \cite{wiki:IEEE754}.
\index{mulf()}
\begin{lstlisting}[style=customc,caption=funció mulf(),label=mulf]
/* mulf.c */
float mulf(float a, float b) {
return a*b;
}
\end{lstlisting}
\index{muld()}
\begin{lstlisting}[style=customc,caption=funció muld(),label=muld]
/* muld.c */
double mulf(double a, double b) {
return a*b;
}
\end{lstlisting}
Les biblioteques estàndard de C incorporen funcions per operar amb aquests
tipus, i son les que es fan servir per defecte pel compilador si no li donem
ordres especials.
Així doncs, si compilem el fitxer mulf.c amb la següent comanda
\begin{lstlisting}
> arm-none-eabi-gcc mulf.c -o- -S -mthumb -mcpu=cortex-m4
\end{lstlisting}
ens mostrarà per pantalla el codi en assemblador que genera el compilador. Cal
fixar-se que els {\it flags} que li passem al compilador son només que el
microcontrolador és un Cortex-M4.
\begin{lstlisting}[style=customasm,caption=codi assemblador de la funció mulf.c,label=ASMmulf]
push {r7, lr}
sub sp, sp, #8
add r7, sp, #0
str r0, [r7, #4] @ float
str r1, [r7] @ float
ldr r1, [r7] @ float
ldr r0, [r7, #4] @ float
bl __aeabi_fmul
mov r3, r0
mov r0, r3
adds r7, r7, #8
mov sp, r7
@ sp needed
pop {r7, pc}
\end{lstlisting}
\index{\_\_eabi\_fmul()}
Aquí el que es veu és que es preparen uns registres i es crida una funció
anomenada \_\_eabi\_fmul que és la funció de la biblioteca encarregada de fer les
multiplicacions per software.
Si ara especifiquem que el cortex-M4 te el mòdul d'operacions en punt flotant
amb la segúent comanda: