Ce que vous allez faire
Je vous avais dit que le travail que vous avez fait sur le clavier matriciel resservirait, le moment est venu. :) Jusqu'à maintenant, nous avons fait varier le rapport cyclique de notre signal PWM, nous allons maintenant faire varier sa fréquence.
Vous allez réaliser un mini-orgue avec les consignes suivantes :
Les touches numériques du clavier correspondent à 12 notes successives.
Une note est jouée tant que sa touche est appuyée. Si aucune touche numérique n'est appuyée, on arrête le compteur.
Une seule touche peut être appuyée à la fois (on prend la première trouvée dans l'ordre du balayage), donc pas de polyphonie.
Vous trouverez les fréquences des notes sur cette page Wikipedia.
-
Vous relierez la sortie PWM que vous aurez choisie au circuit suivant pour pouvoir entendre vos notes :
Pour changer sa valeur, vous aurez besoin d'accéder directement au registre du prescaler en écrivant TIMx->PSC. Si vous regardez son code source, c'est ce que fait la fonction TIM_TimeBaseInit(), par exemple.
La modification de la valeur du prescaler n'est prise en compte que si le compteur est en fonction, c'est-à-dire que TIM_Cmd(TIMx, ENABLE) a été appelée.
Au cas où vous auriez galéré pour gérer le clavier matriciel, je vous propose une solution minimale ci-dessous pour que vous puissiez vous concentrer sur l'utilisation du timer :
#include <ch32v00x.h>
#include <debug.h>
static const char keys[][3] = {
{ '1', '2', '3', },
{ '4', '5', '6', },
{ '7', '8', '9', },
{ '*', '0', '#', },
};
int main() {
USART_Printf_Init(115200);
// On initialise le GPIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitTypeDef gpioInit = { 0 };
// PC0..3 correspondent aux rangées (entrées), de haut en bas.
gpioInit.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
gpioInit.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(GPIOC, &gpioInit);
// PC5..7 correspondent aux colonnes (sorties), de gauche à droite.
gpioInit.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
gpioInit.GPIO_Mode = GPIO_Mode_Out_PP;
gpioInit.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &gpioInit);
int currentColumn = 0;
while (1) {
// Le principe est d'envoyer un niveau logique haut alternativement
// sur chaque sortie. Si on retrouve ce niveau sur une entrée, c'est
// que la touche correspondante est appuyée.
switch (currentColumn) {
case 0:
GPIO_Write(GPIOC, 0x20);
break;
case 1:
GPIO_Write(GPIOC, 0x40);
break;
case 2:
GPIO_Write(GPIOC, 0x80);
break;
}
// La lecture du port renvoie l'état de toutes les lignes,
// donc sorties comprises.
int key = GPIO_ReadInputData(GPIOC);
// On regarde si on trouve un niveau logique haut sur une des entrées.
if (key & 0x0f) {
// On recherche la position du "1" dans les entrées pour trouver
// la rangée.
int row = 0;
for (int v = key; !(v & 1); v >>= 1, row++);
// On recherche la position du "1" dans les sorties pour trouver
// la colonne, en ignorant PC4 que nous n'utilisons pas.
int column = 0;
for (int v = key; !(v & 0x20); v >>= 1, column++);
// On affiche rangée, colonne et caractère correspondant.
printf("rangee = %d, colonne = %d, touche = %c\r\n", row, column, keys[row][column]);
}
// On passe à la colonne suivante.
currentColumn++;
if (currentColumn > 2) {
currentColumn = 0;
}
}
}