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; } } }