Oscillo HAMEG HM303-4 : ajout d'un ATmega 8 !

Un sélecteur digital pour la base de temps.

1 Enoncé du problème :

Il y a quelque temps le sélecteur de vitesse de balayage de mon oscilloscope HM303-4, actionné par le bouton TIME/DIV, s'est détraqué. Je l'ai réparé, ça a tenu un mois, je l'ai réparé de nouveau, ça a tenu trois jours !

Il faut dire que la pièce d'origine est un affreux bricolage, un bout de circuit imprimé soudé perpendiculairement à une grande carte, avec un peigne de contacts dont le centrage, qui doit être extrêmement précis, prend du jeu à la longue.

Ce sélecteur a 20 positions angulaires, il est relié par 8 contacts. J'ai pensé le remplacer par des micros relais (reeds - ILS)... pas top ! voire par de commutateurs analogiques... ou de simples FETs.

C'est alors que j'ai remarqué que le troisième contact en partant de la gauche est toujours en contact avec le peigne. J'ai pensé que c'était peut-être la masse, et après vérification, oui, c'est bien relié à GND.
Mais alors, pour peu que les tensions en jeu soient toujours positives, de simple transistors NPN pourraient convenir pour assurer les contacts, et c'est le cas !

Restait à relever (à l'ohmmètre ) la "table de vérité" de l'ensemble et de la reproduire avec un microcontôleur ATmega, qui se chargerait au passage de gérer une acquisition de position par un classique encodeur rotatif pas à pas code gray et un affichage à leds.

Voilà, je vous ai tout dit, prenez une feuille, vous avez une heure.

2 Le schéma

3 -

L'ATmega est configuré (par la programmation de ses "fuses") pour fonctionner à une fréquence très basse (oscillateur RC externe) de 15 kHz, ceci afin de générer le moins possible de perturbations électromagnétique au sein de l'appareil.

L'encodeur rotatif pas-à-pas est du modèle ci-contre.

4 Démontage du sélecteur d'origine

5 Réalisation de la carte d'affichage à LEDs CMS

Tout l'art consiste à placer précisément les LED sur un cercle, équidistantes, espacées d'un angle de 360/24 = 15 degrés. (Seules 20 positions sont occupées).

6 -

7 Découpe de l'emplacement qui recevra la carte d'affichage et l'encodeur

8 Mise en place de la carte

9 -

10 Placage de la partie précédemment découpée (et percée de 20 trous à l'emporte-pièce)

11 Ajout d'un bouton

12 Réalisation de la carte logique

13 Incorporation de la carte logique dans l'oscilloscope

L'alimentation 5V est présente sue la carte mère... (Il y a quelques boîtiers CMOS)

14 -

15 Le firmware pour l'ATmega8

CODE SOURCE en c
  1. /***********************************************************************
  2. par Silicium628
  3. derniere mise à jour 16 sept 2014
  4. ========================================================================
  5. Selecteur de la base de temps pour oscilloscope HAMEG HM303
  6. ATmega8
  7. -Affichage LCD 4 x 16 caractères
  8.  
  9. ************************************************************************/
  10.  
  11.  
  12.  
  13. // #define F_CPU 16000000
  14. #define F_CPU 3276800
  15.  
  16. /**
  17. Utilisation de l'oscillateur RC (R=1k - C=22nF externes) -> fréquence volontairement très basse (40kHz environ mesuré au fréquencemètre)
  18. pour éviter les perturbations HF parasites au sein de l'oscilloscope où sera situé le montage.
  19. PROGRAMMER LES FUSES pour External RC oscillator gamme 0.9MHz - 3MHz
  20. ôter le C de 22nF pour accélérer l'horloge avant toute opération de programmation.
  21. **/
  22.  
  23.  
  24. #include <avr/io.h>
  25. #include <avr/interrupt.h>
  26. #include <util/delay.h>
  27. #include <math.h>
  28.  
  29.  
  30. #define pin_a0 0b01000000 // sur port D
  31. #define pin_a1 0b10000000 // sur port D
  32. #define pin_a2 0b00000001 // sur port c
  33. #define pin_a3 0b00000010 // sur port c
  34. #define pin_a4 0b00000100 // sur port c
  35. #define pin_k0 0b00000010 // sur port D
  36. #define pin_k1 0b00001000 // sur port D
  37. #define pin_k2 0b00010000 // sur port D
  38. #define pin_k3 0b00100000 // sur port D
  39.  
  40.  
  41. #define pin_A 0b00010000 // sur port B
  42. #define pin_B 0b00000100 // sur port B
  43. #define pin_D 0b00000010 // sur port B
  44. #define pin_E 0b00000001 // sur port B
  45. #define pin_F 0b00100000 // sur port C
  46. #define pin_G 0b00010000 // sur port C
  47. #define pin_H 0b00001000 // sur port C
  48.  
  49.  
  50.  
  51. char * version = "1.2";
  52.  
  53.  
  54. uint8_t pos_bouton; // position du curseur (pour affichage)
  55.  
  56. uint8_t etat;
  57. uint16_t compteur1;
  58.  
  59.  
  60. // Le tableau suivant définit l'état des 7 transistors de sortie (le bit 5 est toujours à 1, c'est GND) en fonction des 20 positions du sélecteur rotatif
  61. uint8_t tabl_out[20] = {
  62. 0b01100001,
  63. 0b01100000,
  64. 0b11100010,
  65. 0b11100001,
  66. 0b11100000,
  67. 0b01100110,
  68. 0b01100101,
  69. 0b01100100,
  70. 0b10110010,
  71. 0b10110001,
  72. 0b10110000,
  73. 0b00110110,
  74. 0b00110101,
  75. 0b00110100,
  76. 0b10100010,
  77. 0b10100001,
  78. 0b10100000,
  79. 0b00100110,
  80. 0b00101101,
  81. 0b00101100 };
  82.  
  83.  
  84. // portB xxxAxBDE
  85. // portC xxFGHxxx
  86. // tabl ABCDEFGH
  87.  
  88. void out_commande(uint8_t n)
  89. {
  90. //n est le numéro de la ligne à envoyer(1 ligne pour chacune de 20 positions du bouton rotatif)
  91. // n=0..19
  92.  
  93. PORTB &= 0b11101000; // RAZ des bits 0,1,2,4
  94. PORTB = PORTB | ((tabl_out[n] & 0b00011000) >> 3) | ((tabl_out[n] & 0b01000000) >> 4 ) | ((tabl_out[n] & 0b10000000) >> 3 );
  95.  
  96. PORTC &= 0b11000111; // RAZ des bits 3,4,5
  97. PORTC = PORTC | ((tabl_out[n] & 0b00000111) << 3 );
  98. }
  99.  
  100.  
  101.  
  102. void init_ports (void) // ports perso
  103. // 0 = entree, 1=sortie ; les 1 sur les pins en entrees activent les R de Pull Up (tirage à VCC)
  104. {
  105. DDRB |= 0b11111111;
  106. PORTB = 0b00001000;
  107.  
  108. DDRC = 0b11111111;
  109. PORTC = 0b00000000;
  110.  
  111. DDRD = 0b11111010; // portD[0] entrée codeur_ROT ; portD[2] entrée INT0
  112. PORTD = 0b00000101;
  113. }
  114.  
  115.  
  116.  
  117.  
  118. void InitINTs (void)
  119. {
  120. GICR |= 0b01000000; // gere les INTs - bit6 ->INT0 request - voir page 67 du pdf
  121. MCUCR |= 0b00000010; // The falling edge of INT0 generates an interrupt request. p:67 du pdf
  122. }
  123.  
  124.  
  125. void init_variables(void)
  126. {
  127. pos_bouton = 9;
  128. }
  129.  
  130.  
  131.  
  132. /*
  133. void test_codeur_rot()
  134. {
  135. // pour test des états (diffèrent suivant modèles) du codeur rotatif pas à pas code gray
  136. // le mien est toujours à 11 en position stable
  137. // dans le sens CW il fait 11->01->00->10->11
  138. // et par conséquent dans le sens CCW il fait 11->10->00->01->11
  139. lcd_clrscr();
  140. while (1)
  141. {
  142. code_rot = PIND & 0b00000011;
  143. lcd_gotoxy(0,0);
  144. lcd_aff_bin (code_rot, 2);
  145. _delay_ms(10);
  146. }
  147. }
  148. */
  149.  
  150.  
  151. void eteintToutesLesAnodes()
  152. {
  153. PORTD = PORTD | pin_a0 | pin_a1;
  154. PORTC = PORTC | pin_a2 | pin_a3 | pin_a4;
  155. }
  156.  
  157.  
  158. void eteintToutesLesCathodes()
  159. {
  160. PORTD = PORTD & ~pin_k0 & ~pin_k1 & ~pin_k2 & ~pin_k3 ;
  161. }
  162.  
  163.  
  164.  
  165.  
  166. void allume1led()
  167. {
  168.  
  169. uint8_t a, k;
  170. a = pos_bouton %5;
  171. k=pos_bouton / 5;
  172.  
  173. eteintToutesLesCathodes();
  174. // alim les cathodes concernées
  175. if (k==0) { PORTD = PORTD | pin_k0; }
  176. if (k==1) { PORTD = PORTD | pin_k1; }
  177. if (k==2) { PORTD = PORTD | pin_k2; }
  178. if (k==3) { PORTD = PORTD | pin_k3; }
  179.  
  180. eteintToutesLesAnodes();
  181. // alim toutes les anodes concernées
  182. if (a==0) { PORTD = PORTD & ~pin_a0; }
  183. if (a==1) { PORTD = PORTD & ~pin_a1; }
  184. if (a==2) { PORTC = PORTC & ~pin_a2; }
  185. if (a==3) { PORTC = PORTC & ~pin_a3; }
  186. if (a==4) { PORTC = PORTC & ~pin_a4; }
  187.  
  188. }
  189.  
  190.  
  191.  
  192. /***********************************************************************
  193. // traitement du codeur_rot()
  194.  
  195. // codeur incrémental code Gray avec en plus fonction bouton poussoir
  196. // le codeur rotatif utilisé est toujours à 11 en position stable
  197. // dans le sens CW il fait 11->01->00->10->11
  198. // et par conséquent dans le sens CCW il fait 11->10->00->01->11
  199.  
  200. // 11
  201. // 01
  202. // 00
  203. // 10
  204. // 11
  205.  
  206. // il suffit de connecter un bit sur le pin INT0 (PD2) et de déclencher l'INT sur front descendant (passage de 0 à 1)
  207. // cette INT lira alors l'état de l'autre bit (connecté à PD0 par exemple). Cet état diffère suivant le sens de rotation
  208.  
  209. ************************************************************************/
  210.  
  211.  
  212. ISR(BADISR_vect)
  213. {
  214. // évite de planter si une int est enable et pas de procedure associée écrite (ce qui fait reseter l'ATmega)
  215. }
  216.  
  217.  
  218.  
  219. ISR (INT0_vect)
  220. {
  221.  
  222. //interruption sur front descendant sur l'entree Int0
  223. // declenchee par la rotation du codeur_ROT
  224.  
  225. etat = PIND & 0b00000001;
  226. if (etat == 0)
  227. {
  228. if (pos_bouton < 19) {pos_bouton++ ;}
  229. }
  230. else
  231. {
  232. if (pos_bouton > 0) {pos_bouton--;}
  233. }
  234.  
  235. out_commande(pos_bouton);
  236. allume1led();
  237. // _delay_ms(1);
  238. }
  239.  
  240.  
  241. int main (void)
  242. {
  243. init_variables();
  244. init_ports();
  245. InitINTs();
  246.  
  247. _delay_ms(10);
  248.  
  249.  
  250. sei(); // enable interruptions
  251.  
  252. out_commande(pos_bouton);
  253. allume1led();
  254. _delay_ms(1);
  255.  
  256.  
  257. while (1) {;}
  258.  
  259.  
  260. }
  261.  
  262.  

16 Video : fonctionnement du sélecteur

17 -

Ben oui, il marche encore, pourquoi ? M'enfin !

18 -

Liens...

8912