Sei sulla pagina 1di 17

Capitolo 1

Pulse Width Modulation

Il Pulse Width Modulation è una tecnica di modulazione ottenuta va-


riando la larghezza di un impulso. É usata sostanzialmente per trasmettere
informazioni o per pilotare carichi limitandone la potenza.
Un segnale PWM1 è sostanzialmente un’onda quadra (fig. 1.1) con un’ampiezza
ben definita, un periodo e un duty cycle.

Figura 1.1: Segnale PWM.

1
Pulse Width Modulation

1
2 CAPITOLO 1. PULSE WIDTH MODULATION

La forma d’onda oscilla tra due stati che possiamo definire come stato
attivo e stato idle in base alla logica di funzionamento che decidiamo di
usare (fig. 1.2)

Figura 1.2: Logica Active High e Logica Active Low.

Il Periodo2 è la risultante della somma dei tempi di active e di idle.

T = tactive + tidle ; (1.1)

Il Duty Cycle, espresso in percentuale, è il rapporto tra il tempo di active


e il periodo.
tactive
D= ; (1.2)
T
Un valore di D = 0% equivale a una tensione nulla mentre un valore di
D = 100% rappresenta un segnale di tensione continuo.
Con la tecnica PWM le comunicazioni hanno una più alta immunità al
rumore essendo l’informazione contenuta nella larghezza dell’impulso e non
2
Il periodo di un segnale è comunemente definito come il tempo necessario all’onda per
compiere un ciclo completo.
1.1. STM32F4 TIMER 3

nell’ampiezza, inoltre, essa può essere sfruttata per regolare la potenza ap-
plicata a carichi come un led o un motore DC.
Per fare ulteriore chiarezza, introduciamo il concetto di valore medio Vav
di un’onda quadra, definito come:
1 ZT 1 Z tactive 1 ZT
Vav = · V (t)dt = · V (t)dt + · V (t)dt; (1.3)
T 0 T 0 T tidle

Considerando una logica active high e un’ampiezza compresa tra 0 V e Vdd V3 ,


posso annullare il secondo termine dell’equazione.
1 Z tactive 1 Z tactive
Vav = · V (t)dt = · V · dt =
T 0 T 0
1
· Vdd · tactive =
T (1.4)
1
· Vdd · D · T =
T
Vdd · D;

Il valore medio della tensione è proporzionale al duty cycle, ne consegue che


lo è anche la potenza.
P = V · I = Vdd · D · I; (1.5)
La scelta della frequenza del PWM varia in base al tipo di applicazione:
nel pilotaggio di LED, per evitare l’effetto flickering, lo sfarfallio, si scelgono
frequenze di lavoro a partire dai 200 Hz in su.

1.1 STM32F4 Timer


Il microcontrollore STM32F401 è equipaggiato con una serie di periferi-
che dette TIMER il cui compito è quello di incrementare un contatore, a
16-bit o 32-bit4 , sulla base di una frequenza di lavoro, insomma, esse contano
i cicli di di un segnale di clock.
Attraverso i TIMER possiamo misurare segnali, valutare dei tempi ed intra-
prendere azioni al raggiungimento di un certo valore del contatore e generare
segnali PWM.
Configurandone opportunamente i registri, i TIMER possono generare fino
a quattro segnali PWM alla stessa frequenza ma con duty cycle differenti.
3
Vdd è la massima ampiezza in tensione.
4
Il numero di bit del contatore definisce quella che si chiama risoluzione del TIMER.
4 CAPITOLO 1. PULSE WIDTH MODULATION

In più, è possibile segnalare attraverso degli interrupt sia l’evento periodico


sia gli eventi legati al duty di ciascun canale (fig. 1.3).

Figura 1.3: Rappresentazione di segnali PWM generati da due canali dello stesso
TIMER.

Concentriamo la nostra attenzione sui registri TIMx_PSC e TIMx_ARR.


Il primo rappresenta un prescaler5 alla sorgente di clock che alimenta la pe-
riferica; il secondo rappresenta il valore del contatore a cui resettare il timer.
Agendo opportunamente su questi registri siamo in grado di generare segnali
dal periodo desiderato.
fAP Bx 1
fpwm = · ; (1.6)
P SC ARR
Dove

• fpwm rappresenta la frequenza desiderata del segnale PWM;

• fAP Bx è la sorgente di clock della periferica TIMER utilizzata;


5
Divisore.
1.2. PWM CHIBIOS DRIVER 5

• P SC è il valore contenuto nel registro TIMx_PSC;

• ARR è il valore contenuto nel registro TIMx_ARR.

1.2 PWM ChibiOS Driver


ChibiOS mette a disposizione un’API dedicata per facilitare l’utilizzo dei
TIMER quando sono utilizzati per la generazione di segnali a modulazione
di larghezza d’impulso.
La macchina a stati del driver è rappresentata in figura 1.4

Figura 1.4: Macchina a stati del driver PWM.

Abilitiamo l’uso della periferica nel file halconf.h mettendo l’opportuna define
a TRUE.
Codice 1.1: halconf.h
1 /**
2 * @brief Enables the PWM subsystem.
3 */
4 #if !defined(HAL_USE_PWM) || defined(__DOXYGEN__)
5 #define HAL_USE_PWM TRUE
6 #endif

Nel file mcuconf.h selezioniamo l’istanza della periferica da utilizzare facendo


attenzione ad eventuali altri driver che utilizzano lo stesso driver.
Codice 1.2: halconf.h
1 /*
2 * ICU driver system settings.
3 */
4 #define STM32_ICU_USE_TIM1 FALSE
6 CAPITOLO 1. PULSE WIDTH MODULATION

5 #define STM32_ICU_USE_TIM2 FALSE


6 #define STM32_ICU_USE_TIM3 FALSE
7 #define STM32_ICU_USE_TIM4 FALSE
8 #define STM32_ICU_USE_TIM5 FALSE
9 #define STM32_ICU_USE_TIM9 FALSE
10 ...
11 /*
12 * GPT driver system settings.
13 */
14 #define STM32_GPT_USE_TIM1 FALSE
15 #define STM32_GPT_USE_TIM2 FALSE
16 #define STM32_GPT_USE_TIM3 FALSE
17 #define STM32_GPT_USE_TIM4 FALSE
18 #define STM32_GPT_USE_TIM5 FALSE
19 #define STM32_GPT_USE_TIM9 FALSE
20 #define STM32_GPT_USE_TIM11 FALSE
21 ...
22 /*
23 * PWM driver system settings.
24 */
25 #define STM32_PWM_USE_ADVANCED FALSE
26 #define STM32_PWM_USE_TIM1 TRUE
27 #define STM32_PWM_USE_TIM2 FALSE
28 #define STM32_PWM_USE_TIM3 FALSE
29 #define STM32_PWM_USE_TIM4 FALSE
30 #define STM32_PWM_USE_TIM5 FALSE
31 #define STM32_PWM_USE_TIM9 FALSE
32 ...
33 /*
34 * ST driver system settings.
35 */
36 #define STM32_ST_IRQ_PRIORITY 8
37 #define STM32_ST_USE_TIMER 2

Ci sono almeno quattro driver (GPT, ICU, PWM, ST) che utilizzano i
TIMER, selezioniamo l’istanza da usare tra quelle che sono ancora disponi-
bili. In caso contrario il compilatore ci avvisa con un errore.
Una volta abilitato il driver, chiamando la funzione halInit() a livello appli-
cativo, il driver PWM passa nello stato di STOP. Attraverso la funzione
pwmStart(), il driver passa nello stato READY dal quale è possibile utiliz-
zare a pieno la periferica.
Funzione Parametri
-pwmp. Puntatore al driver PWM.
void pwmStart(PWMDriver *pwmp, const PWMConfig *config)
-config. Puntatore alla struttura di configurazione.
-pwmp. Puntatore al driver PWM.
void pwmEnableChannel(PWMDriver *pwmp, pwmchannel_t channel, pwmcnt_t width) -channel. Canale da abilitare.
-width. Larghezza dell’impulso.
void pwmStop(PWMDriver *pwmp) -pwmp. Puntatore al driver PWM.

Tabella 1.1: Funzioni principali driver PWM.

La struttura di configurazione, analogamente per quanto avviene in altri dri-


ver, è formata da una parte di alto livello e una parte di basso livello dipen-
1.2. PWM CHIBIOS DRIVER 7

dente dall’hardware utilizzato. In questo modo si dà la possibilità all’utente


di poter configurare in maniera diretta alcuni registri della periferica.
Codice 1.3: struttura di configurazione PWM ad alto livello
1 /**
2 * @brief Type of a PWM driver configuration structure.
3 */
4 typedef struct {
5 /**
6 * @brief Timer clock in Hz.
7 * @note The low level can use assertions in order to catch invalid
8 * frequency specifications.
9 */
10 uint32_t frequency;
11 /**
12 * @brief PWM period in ticks.
13 * @note The low level can use assertions in order to catch invalid
14 * period specifications.
15 */
16 pwmcnt_t period;
17 /**
18 * @brief Periodic callback pointer.
19 * @note This callback is invoked on PWM counter reset. If set to
20 * @p NULL then the callback is disabled.
21 */
22 pwmcallback_t callback;
23 /**
24 * @brief Channels configurations.
25 */
26 PWMChannelConfig channels[PWM_CHANNELS];
27 /* End of the mandatory fields.*/
28 } PWMConfig;

Il campo frequency rappresenta la frequenza desiderata del TIMER da cui


il driver ricava il valore da inserire nel registro TIMx_PSC.
Il period va a configurare il registro TIMx_ARR.
La frequenza del PWM, come spiegato il precedenza, dipende dal rapporto
di questi due campi
f requency
fpwm = ; (1.7)
period
Nel campo callback va la funzione da richiamare ad ogni periodo del PWM.
Il campo channels rappresenta la struttura di configurazione dei singoli canali
del TIMER.
Codice 1.4: Struttura di configurazione per i singoli canali.
1 /**
2 * @brief Type of a PWM driver channel configuration structure.
3 */
4 typedef struct {
5 /**
6 * @brief Channel active logic level.
8 CAPITOLO 1. PULSE WIDTH MODULATION

7 */
8 pwmmode_t mode;
9 /**
10 * @brief Channel callback pointer.
11 * @note This callback is invoked on the channel compare event. If set to
12 * @p NULL then the callback is disabled.
13 */
14 pwmcallback_t callback;
15 /* End of the mandatory fields.*/
16 } PWMChannelConfig;

Ogni canale può essere abilitato selezionando la modalità di funzionamen-


to in logica ACTIVE_HIGH e ACTIVE_LOW, in più, possiamo associare
un’ulteriore callback sul cambio di stato6 del segnale per ciascun canale.
Le callback sono abilitate attraverso le API pwmEnablePeriodicNotification()
e pwmEnableChannelNotification().

1.3 Esercizio 22
Creiamo un nuovo progetto e nominiamolo 22-RT-STM32F4-BLACK-
PILL-PWM. Il nostro intento è quello di pilotare il LED RED, preceden-
temente connesso al PIOA 107 , con un segnale PWM con un periodo di 1 ms
variandone il duty cycle in maniera graduale per aumentare l’intensità lumi-
nosa.
La prima cosa da fare è controllare se il pin PIOA 10 supporta la modalità
TIMER.
Dal documento stm32f401re controlliamo le funzionalità dei pin (fig. 1.5).
Il pin PA10 in modalità alternate 1, funziona da canale 3 del TIMER 1, può
essere dunque utilizzato per pilotare il led rosso.
Abilitiamo il driver PWM, come abbiamo visto precedentemente, nei file
halconf.h e mcuconf.h selezionando l’istanza del TIMER 1.
Nella struttura di configurazione utilizziamo il canale 3 in modalità ACTI-
VE_HIGH e lasciamo a NULL i campi delle callback.
Codice 1.5: struttura di configurazione PWM esercizio 22.
1 static PWMConfig pwmcfg = {
2 10000, /* 10kHz PWM clock frequency. */
3 10, /* Initial PWM period 1ms. */
4 NULL,
5 {

6
Passaggio da active a idle.
7
Capitolo GPIO
1.3. ESERCIZIO 22 9

Figura 1.5: Mappa dei pin per gentile cortesia di STMicroelectronics.

6 {PWM_OUTPUT_DISABLED, NULL},
7 {PWM_OUTPUT_DISABLED, NULL},
8 {PWM_OUTPUT_ACTIVE_HIGH, NULL},
9 {PWM_OUTPUT_DISABLED, NULL}
10 },
11 0, /* hw dependent. */
12 0
13 };

La frequenza del PWM generato dal canale 3 è esattamente di 1 kHz.


Facciamo lavorare PA10 in modalità alternate 1 e startiamo il driver.
Codice 1.6: main.c esercizio 22.
1 /*
2 * Configure PA10 (RED) as TIM1 channel 3
3 */
4 palSetPadMode(GPIOA, 10, PAL_MODE_ALTERNATE(1));
5
6 /* Start PWM1 */
7 pwmStart(&PWMD1, &pwmcfg);

In un thread apposito abilitiamo il canale 3 e modifichiamone il duty cycle.


Codice 1.7: esercizio 22.
1 /*
2 * PWM LED RED thread, times are in milliseconds.
3 */
4 static THD_WORKING_AREA(waThread2, 256);
5 static THD_FUNCTION(Thread2, arg) {
6
10 CAPITOLO 1. PULSE WIDTH MODULATION

7 (void)arg;
8 chRegSetThreadName("brighter");
9
10 uint32_t duty = 0;
11
12 while (true) {
13 /*
14 * Changes the PWM channel 0 to 100% duty cycle.
15 */
16 pwmEnableChannel(&PWMD1, 2, PWM_PERCENTAGE_TO_WIDTH(&PWMD1, duty));
17 duty += 1000;
18
19 /* if 100% duty then restart. */
20 if (duty > 10000) {
21 duty = 0;
22 }
23 chThdSleepMilliseconds(250);
24 }
25 }

Impostiamo un valore di duty pari a 0, abilitiamo il canale attraverso la fun-


zione pwmEnableChannel()8 .
La macro PWM_PERCENTAGE_TO_WIDTH(), passata come ter-
zo parametro, converte in larghezza d’impulso un valore in percentuale di
duty cycle. Ogni 250 ms incrementiamo il valore del duty per poi resettarlo
al raggiungimento del 100%.
Osservando il LED RED durante l’esecuzione del programma, notiamo come
la sua intensità luminosa aumenti gradualmente.

Esercizio. Nell’esercizio precedente il led è ripilotato con un duty allo


0% ad ogni ciclo di funzionamento. Modificare l’esercizio in modo tale
da incrementare e decrementare gradualmente l’intensità.

1.4 Esercizio 23
Modifichiamo l’esercizio precedente utilizzando le callback per pilotare un
secondo LED, quello giallo, collegato al PA9. Lo scopo dell’esercizio è creare
un segnale PWM complementare alzando e abbassando il pin attraverso le
funzioni palClearPad() e palSetPad() all’interno delle callback del periodo e
del canale.
Codice 1.8: main.c esercizio 23
1 /* Called every T_pwm */
2 static void pwmpcb(PWMDriver *pwmp) {

8
Attenzione! I canali sono numerati da 0 a n-1. Il canale 3 fisico del TIMER viene
identificato dal numero 2.
1.4. ESERCIZIO 23 11

3
4 (void)pwmp;
5 palClearPad(GPIOA, 9);
6 }
7
8 /* Called every Active duty time */
9 static void pwmc1cb(PWMDriver *pwmp) {
10
11 (void)pwmp;
12 palSetPad(GPIOA, 9);
13 }
14
15 static PWMConfig pwmcfg = {
16 10000, /* 10kHz PWM clock frequency. */
17 10, /* Initial PWM period 1ms. */
18 pwmpcb,
19 {
20 {PWM_OUTPUT_DISABLED, NULL},
21 {PWM_OUTPUT_DISABLED, NULL},
22 {PWM_OUTPUT_ACTIVE_HIGH, pwmc1cb},
23 {PWM_OUTPUT_DISABLED, NULL}
24 },
25 0,
26 0
27 };

Configuriamo il pin PA9 come un semplice GPIO in modalità output push


pull e abilitiamo le callback attraverso le API dedicate.
Codice 1.9: main.c esercizio 23.
1 /*
2 * Configure PA10 (red) as TIM1 channel 3
3 */
4 palSetPadMode(GPIOA, 10, PAL_MODE_ALTERNATE(1));
5
6 /*
7 * Configure PA9 (yellow) as output push pull
8 */
9 palSetPadMode(GPIOA, 9, PAL_MODE_OUTPUT_PUSHPULL);
10 palClearPad(GPIOA, 9);
11
12 /* Start PWMD1 */
13 pwmStart(&PWMD1, &pwmcfg);
14
15 /* Enable IRQ on PWM counter reset */
16 pwmEnablePeriodicNotification(&PWMD1);
17
18 /* Enable PWM on channel compare event (changes with duty)
19 * @note: We are going to create complementary PWM
20 */
21 pwmEnableChannel(&PWMD1, 2, PWM_PERCENTAGE_TO_WIDTH(&PWMD1, 0));
22 pwmEnableChannelNotification(&PWMD1, 2);

Osserviamo come i due led si comportino in modo perfettamente comple-


mentare: ad intensità crescente su uno corrisponde un’intensità decrescente
dell’altro.
12 CAPITOLO 1. PULSE WIDTH MODULATION

Nota Frequenza
C5 523.25 Hz
D5 587.33 Hz
E5 659.26 Hz
F5 698.46 Hz
G5 783.99 Hz
A5 880.00 Hz
B5 987.77 Hz
C6 1046.50 Hz

Tabella 1.2: Corrispondenza nota-frequenza.

1.5 Esercizio 24
Attraverso il PWM siamo in grado anche di generare note pilotando uno
speaker, in questo caso una variazione di duty cycle corrisponde a una varia-
zione di volume della nota prodotta.
Lo speaker in dotazione nel KIT ha un’impedenza di 8 Ω e una potenza di
0.25 W, colleghiamone un’estremità a massa e l’altra al pin PA89 . Pilotiamo
lo speaker in modo da generare i suoni appartenenti a una scala maggiore di
DO.
La tabella 1.2 riporta i valori di frequenza per le note da C5 e C6. Fissiamo la
frequenza del TIMER a 1 MHz, e calcoliamo i valori di period per ciascuna
nota con la formula:
ftimer
period = . (1.8)
fnote
dove fnote rappresenta la frequenza della nota desiderata.
Per generare note diverse quindi è necessario cambiare il period durante l’e-
secuzione del programma, possiamo farlo utilizzando la funzione pwmChan-
gePeriod().
Codice 1.10: main.c esercizio 24.
1 /* Note period */
2 #define C5 1911
3 #define D5 1703
4 #define E5 1517
5 #define F5 1432
6 #define G5 1275
7 #define A5 1136
8 #define B5 1012

9
TIMER 1 channel 1.
1.5. ESERCIZIO 24 13

9 #define C6 956
10
11 #define SIZE 8
12
13 /* C Major scale */
14 static pwmcnt_t scale[SIZE] = {C5, D5, E5, F5, G5, A5, B5, C6};
15
16 static PWMConfig pwmcfg = {
17 1000000, /* 1MHz PWM clock frequency. */
18 100, /* Initial PWM period 0.1ms. */
19 NULL,
20 {
21 {PWM_OUTPUT_ACTIVE_HIGH, NULL},
22 {PWM_OUTPUT_DISABLED, NULL},
23 {PWM_OUTPUT_DISABLED, NULL},
24 {PWM_OUTPUT_DISABLED, NULL}
25 },
26 0,
27 0
28 };
29 ...
30 /*
31 * PWM LED RED thread, times are in milliseconds.
32 */
33 static THD_WORKING_AREA(waThread2, 256);
34 static THD_FUNCTION(Thread2, arg) {
35
36 (void)arg;
37 chRegSetThreadName("player");
38
39 uint32_t i;
40
41 while (true) {
42
43 /*
44 * Changes the period.
45 */
46 for (i = 0; i < SIZE; i++) {
47 pwmChangePeriod(&PWMD1, scale[i]);
48 pwmEnableChannel(&PWMD1, 0, PWM_PERCENTAGE_TO_WIDTH(&PWMD1, 6000));
49 chThdSleepMilliseconds(1000);
50 pwmDisableChannel(&PWMD1, 0);
51 }
52 chThdSleepMilliseconds(1000);
53 }
54 }
55
56 /*
57 * Application entry point.
58 */
59 int main(void) {
60
61 /*
62 * System initializations.
63 * - HAL initialization, this also initializes the configured device drivers
64 * and performs the board-specific initializations.
65 * - Kernel initialization, the main() function becomes a thread and the
66 * RTOS is active.
67 */
14 CAPITOLO 1. PULSE WIDTH MODULATION

68 halInit();
69 chSysInit();
70
71 /*
72 * Configure PA8 as TIM1 channel 1
73 */
74 palSetPadMode(GPIOA, 8, PAL_MODE_ALTERNATE(1));
75
76 /* Start PWMD1 */
77 pwmStart(&PWMD1, &pwmcfg);
78
79 ...
80 }

1.6 Esercizio 25
Adesso che sappiamo come generare note pilotando uno speaker, scrivia-
mo una nuova demo che suoni un brano e non una semplice scala maggiore.
In un file song.h definiamo il period delle note, la loro durata e la velocity
intesa come intensità di volume. Associamo ad ogni nota del brano la durata
e la corrispondente velocity utilizzando un array multidimensionale10 .
Codice 1.11: main.c esercizio 25.
1 /*
2 * PWM LED RED thread, times are in milliseconds.
3 */
4 static THD_WORKING_AREA(waThread2, 256);
5 static THD_FUNCTION(Thread2, arg) {
6
7 (void)arg;
8 chRegSetThreadName("player");
9
10 uint32_t i;
11
12 while (true) {
13
14 /*
15 * Changes the period.
16 */
17 for (i = 0; i < (sizeof march / sizeof march[0]); i++) {
18 pwmChangePeriod(&PWMD1, march[i][0]);
19 pwmEnableChannel(&PWMD1, 0, PWM_PERCENTAGE_TO_WIDTH(&PWMD1, march[i][2]));
20 chThdSleepMilliseconds(march[i][1] - 50);
21 pwmDisableChannel(&PWMD1, 0);
22 chThdSleepMilliseconds(50);
23 }
24 chThdSleepMilliseconds(1000);
25 }
26 }

10
Abbiamo modellato una nota come un array di tre elementi.
1.7. SHELL 15

Come si evince dal codice, i valori di period, duty e durata sono letti dall’arry
multidimensionale march 11 dichiarato nel file song.h.

Esercizio. Quale sarebbe stato il modo migliore di modellare l’elemento


nota? Modificate il programma in tal senso.

1.7 SHELL
Aggiungiamo le funzionalità del PWM all’applicativo SHELL. Copia-
mo la precedente SHELL e rinominiamola 26-RT-STM32F4-BLACK-PILL-
SHELL. Implementiamo due nuovi comandi:

• start_play. Accetta come argomento una song da suonare.

• stop_play. Interrompe il player precedentemente avviato.

Il thread del player non è allocato in maniera statica ma creato dinamicamen-


te dall’heap attraverso la funzione chThdCreateFromHeap() in cmd_start_play()
e terminato con chThdTerminate() in cmd_stop_play().
Codice 1.12: start e stop player command.
1 /*
2 * Command start play
3 */
4 static void cmd_start_play(BaseSequentialStream *chp, int argc, char *argv[]) {
5
6 /* Check argument’s number */
7 if (argc != 1) {
8 shellUsage(chp, "start_play [SONG]\r\n\t\t"
9 "- MARCH\r\n\t\t");
10 return;
11 }
12
13 if (strcmp(argv[0], "MARCH")) {
14 chprintf(chp, "Parameters error\r\n");
15 shellUsage(chp, "start_play [SONG]\r\n\t\t"
16 "- MARCH\r\n\t\t");
17 return;
18 }
19
20 tplayp = chThdCreateFromHeap(NULL, PLAYER_WA_SIZE,
21 "player", NORMALPRIO + 1,
22 playerThread, NULL);
23
24 /*
25 * Check if operation success
26 */
27 if (tplayp != NULL) {

11
Il brano trascritto è la marcia imperiale tratta dalla saga di Star Wars.
16 CAPITOLO 1. PULSE WIDTH MODULATION

28 chprintf(chp, "Player started\r\n");


29 }
30 else {
31 chprintf(chp, "Player started error\r\n");
32 }
33
34 return;
35 }
36
37 /*
38 * Command stop play
39 */
40 static void cmd_stop_play(BaseSequentialStream *chp, int argc, char *argv[]) {
41
42 (void) argv;
43
44 /* Check argument’s number */
45 if (argc != 0) {
46 shellUsage(chp, "stop_play has no parameters\r\n\t\t");
47 return;
48 }
49
50 if (tplayp == NULL) {
51 chprintf(chp, "Player is not running\r\n");
52 return;
53 }
54
55 msg_t status;
56
57 /* Ask for terminate thread */
58 chThdTerminate(tplayp);
59
60 /* Wait for termination */
61 status = chThdWait(tplayp);
62
63 /* Important */
64 tplayp = NULL;
65
66 /*
67 * Check if operation success
68 */
69 if (status != MSG_OK) {
70 chprintf(chp, "Error in termination\r\n");
71 }
72 else {
73 chprintf(chp, "Player stopped\r\n");
74 }
75
76 return;
77 }

La parte relativa al PWM e al thread che si occupa di pilotarne i segnali


non si discosta da quella vista nell’esercizio precedente.
Codice 1.13: gestione PWM nella shell.
1 /* Thread Player area size */
2 #define PLAYER_WA_SIZE THD_WORKING_AREA_SIZE(256)
1.7. SHELL 17

3
4 static thread_t *tplayp = NULL;
5
6 static PWMConfig pwmcfg = {
7 1000000, /* 1MHz PWM clock frequency. */
8 100, /* Initial PWM period 0.1ms. */
9 NULL,
10 {
11 {PWM_OUTPUT_ACTIVE_HIGH, NULL},
12 {PWM_OUTPUT_DISABLED, NULL},
13 {PWM_OUTPUT_DISABLED, NULL},
14 {PWM_OUTPUT_DISABLED, NULL}
15 },
16 0,
17 0
18 };
19
20 /*
21 * Player song thread
22 */
23 static THD_FUNCTION(playerThread, arg) {
24
25 (void)arg;
26 chRegSetThreadName("player");
27
28 uint32_t i;
29
30 /* Start PWMD1 */
31 pwmStart(&PWMD1, &pwmcfg);
32
33 /*
34 * Plays until terminated
35 */
36 while (!chThdShouldTerminateX()) {
37 /*
38 * Changes the period.
39 */
40 for (i = 0; i < (sizeof march / sizeof march[0]); i++) {
41 if (chThdShouldTerminateX()) {
42 break;
43 }
44 pwmChangePeriod(&PWMD1, march[i][0]);
45 pwmEnableChannel(&PWMD1, 0, PWM_PERCENTAGE_TO_WIDTH(&PWMD1, march[i][2]));
46 chThdSleepMilliseconds(march[i][1] - 50);
47 pwmDisableChannel(&PWMD1, 0);
48 chThdSleepMilliseconds(50);
49 }
50 chThdSleepMilliseconds(1000);
51 }
52
53 /* Stop PWMD1 */
54 pwmStop(&PWMD1);
55 chThdExit(MSG_OK);
56 }

Potrebbero piacerti anche