;======================================================================= ; Fichier : gene_freq.asm ; Titre: GENERATEUR de Frequences ATMEGA8 - Afficheur LCD 2x16 ; Version: 1.02 ; Auteur: Fred ; Date: 27/03/2006 ;======================================================================= ;VCO - PLL - Quartz 3.2768MHz ; sortie rect 5V compatible CMOS et TTL ;convertisseur numerique/analogique pour stabiliser la PLL et diminuer le bruit de phase ;affichage 5 chiffres 02000 a 20000 ;7 GAMMES: ;2MHz - 200kHz (resolution 100Hz) ;200kHz - 20kHz (resolution 10Hz) ;20kHz - 2kHz (resolution 1Hz) ;2kHz - 200Hz (resolution 0.1Hz) ;200Hz - 20Hz (resolution 10mHz) ;20Hz - 2Hz (resolution 1mHz) ;2Hz - 0.2Hz (resolution 0.1mHz) ;======================================================================= ;Quelques exemples de frequences de sortie: ; 1.000Hz ; 50Hz ; 60Hz ; 1000Hz ; 15Hz a 15000Hz (BF audible) ; 15625Hz (TV - H) ; 455kHz (FI) ; 1.000MHz ;======================================================================= ;LISTE DES PARTIES: ;Déclaration des Variables ;MACROS ;Déclaration des Vecteurs d'interruptions ;Routines de traitement des interruptions ;Programme de RESET ;initialisation des ports ;PROGRAMME PRINCIPAL ;ROUTINES ;mul16_8 MULTIPLICATION 8 bits x 16 bits resultat sur 24 bits ;div24_8 DIVISION 24 bits par 8 bits, resultat sur 24 bits ; PROCEDURES D'AFFICHAGE PHYSIQUE SUR LE LCD ;======================================================================= .include "m8def.inc" ; nom du fichiers de références des registres (ATMEGA 8) ;------------------------------------------------------------------------ ;Déclaration des Variables ;------------------------------------------------------------------------ ;r0,1,2 = Fout du VCO (max=2^24 = 16.777216 MHz . En fait on se limitera a Fout max = 2MHz (2000 000 Hz) ) .def SAUVREG = r3 ; registre de sauvegarde de SREG .def fact_div =r4 ;facteur de division (valeur appliquee au commutateur 74LS151 a la sortie du generateur) ;r15 reserve pour l'INT_EXT0 .def a=r16 .def AA=r17 .def BB=r18 .def rang = r19 ;r20..25 reserve pour les variables locales ;r26,27 reserve comme registre d'adresse d'indexation (X) .def n_touche = r28 .def point_dec = r29 ;r30,31 reserve comme registre d'adresse d'indexation (Z) permet la lecture en memoire de programme .DSEG dtlcd: .BYTE 1 V_out: .BYTE 1 TAB_AFF8: .BYTE 8 ;tableau de 8 octets codants 8 chiffres a afficher nombre1: .BYTE 3 ;nombre code sur 3 octets (binaire) 0...16 777 216 nombre2: .BYTE 3 ;"" dividende: .BYTE 3 numerateur: .BYTE 1 denominateur: .BYTE 1 ;------------------------------------------------------------------------ ;MACROS ;------------------------------------------------------------------------ ;cette macro fait @0(variable en RAM, 24bits) := @1 valeur(24bits) pasee en parametre .MACRO Mset24 ldi a,BYTE1(@1) sts @0+0,a ldi a,BYTE2(@1) sts @0+1,a ldi a,BYTE3(@1) sts @0+2,a .ENDMACRO ;------------------------------------------------------------------------ ;cette macro fait @0(variable en RAM, 24bits) := r20,21,22 .MACRO Mstore24 sts @0+0,r20 sts @0+1,r21 sts @0+2,r22 .ENDMACRO ;------------------------------------------------------------------------ ;cette macro fait 3 registres successifs := @0 (variable en RAM, 24bits, pasee en parametre) .MACRO Mload24 lds r20,@0+0 lds r21,@0+1 lds r22,@0+2 .ENDMACRO ;------------------------------------------------------------------------ ;Déclaration des Vecteurs d'interruptions ;------------------------------------------------------------------------ .CSEG .ORG 0x0000 ;début zone mémoire programme rjmp RESET ;RESET rjmp EXT_INT0 ;Interruption externe INT0 rjmp EXT_INT1 ;Interruption externe INT1 rjmp TIM2_COMP ;Interrution comparaison réussie TIMER2 rjmp TIM2_OVF ;Interrution débordement compteur TIMER2 rjmp TIM1_CAPT ;Interrution entrée capture TIMER1 rjmp TIM1_COMPA ;Interrution comparateurA TIMER1 rjmp TIM1_COMPB ;Interrution comparateurB TIMER1 rjmp TIM1_OVF ;Interrution débordement compteur TIMER1 rjmp TIM0_OVF ;Interrution débordement compteur TIMER0 rjmp SPI_STC ;Interrution transmissions SPI terminée rjmp UART_RXC ;Interrution réception UART terminé RX rjmp UART_DRE ;Interrution UART vide rjmp UART_TXC ;Interrution émission UART terminé TX rjmp ADC_ok ;Interrution Conversion A/D terminée rjmp EE_RDY ;Interrution EEPROM prête rjmp ANA_COMP ;Interrution comparaison analogique effectuée ;------------------------------------------------------------------------ ;Routines de traitement des interruptions ;------------------------------------------------------------------------ ;F-in limite avec Qx=16MHz = 450kHz EXT_INT0: reti ;------------------------------------------------------------------------ EXT_INT1: reti ;------------------------------------------------------------------------ TIM2_COMP: reti ;------------------------------------------------------------------------ TIM2_OVF: reti ;------------------------------------------------------------------------ TIM1_CAPT: reti ;------------------------------------------------------------------------ TIM1_COMPA: reti ;------------------------------------------------------------------------ TIM1_COMPB: reti ;------------------------------------------------------------------------ TIM1_OVF: reti ;------------------------------------------------------------------------ TIM0_OVF: reti ;------------------------------------------------------------------------ SPI_STC: reti ;------------------------------------------------------------------------ UART_RXC: reti ;------------------------------------------------------------------------ UART_DRE: reti ;------------------------------------------------------------------------ EE_RDY: reti ;------------------------------------------------------------------------ ANA_COMP: reti ;------------------------------------------------------------------------ ADC_ok: reti ;------------------------------------------------------------------------ UART_TXC: reti ;------------------------------------------------------------------------ UART_RXC1: reti ;------------------------------------------------------------------------ ;------------------------------------------------------------------------ ;Programme de RESET ;------------------------------------------------------------------------ texte: .DW 'G'+256*'E','N'+256*'E',' '+256*'H','F'+256*' ' RESET: ldi a,low(RAMEND) out SPL,a ; Initialisation de la pile à ldi a,high(RAMEND) ; l'adresse haute de la SRAM out SPH,a ;------------------------------------------------------------------------ ;initialisation des ports ldi a,0b11111111 ;toutes en sorties out ddrb,a ldi a,0b00000000 ;toutes en entree out ddrd,a ldi a,0b11011111 ;R de tirage validee pour switchs out portd,a ldi a,0b11111111 ;toutes en sorties out ddrc,a clr a out portc,a ;sorties = 0 ;------------------------------------------------------------------------ ;wachtdog ; wdr ; ldi a,0b00001111 ; wachtdog enable; delai = 2s (voir p:82) ; out wdtcr,a wdr ldi a,0b00011111 out wdtcr,a ldi a,0b00010111 out wdtcr,a ; disable le wachtdog. (voir p:83) ;------------------------------------------------------------------------ ;TIMSK ;Timer Interrupt Mask; voir pdf p:70 ;bit7 (OCIE2) ;bit6 (TOIE2) ;bit5 (TICIE1) Timer/Counter1, Input Capture Interrupt Enable ;bit4 (OCIE1A) Timer/Counter1, Output Compare A Match Interrupt Enable ;bit3 (OCIE1B) Timer/Counter1, Output Compare B Match Interrupt Enable ;bit2 (TOIE1) Timer/Counter1, Overflow Interrupt Enable ;bit1 inutilise ;bit0 (TOIE0) Timer/Counter0 Overflow Interrupt Enable ; 76543210 ldi a,0b00000000 out TIMSK,a ;------------------------------------------------------------------------ ;TCCR1A ;definit le mode de fonctionnement du Timer1 ;voir Tavernier p:93 ;bit7 (COM1A1) ;bit6 (COM1A0) ;bit5 (COM1B0) ;bit4 (COM1B1) ;bit3,2 NC ;bit1 (PWM11) ;bit0 (PWM10) ; 76543210 ldi a,0b01000000 out TCCR1A,a ;------------------------------------------------------------------------ ;TCCR1B ;definit le mode de fonctionnement du Timer1 ;voir Tavernier p:94-95 ;et p:81-82 du datasheet.pdf ;bit7 (ICNC1): enable/disable reducteur de bruit ;bit6 (ICES1)=1: transfert du registre de comptage TCNT1 dans le registre de capture ICR1 sur front montant du pin ICP ;bit5 : inutilise ;bit4 : inutilise ;bit3 (WGM12 ou CTC1): raz registre de comptage (TCNT1) apres comparaison ;bits2,1,0: taux de predivision applique a l'horloge systeme. voir Tavernier p:95 ; 76543210 ldi a,0b00001110 ;horloge externe pour le Timer1, RAZ du compteur (TCNT1) lors de comparaison OK out TCCR1B,a ;------------------------------------------------------------------------ ;MCUCR bits[1,0] ;ISC11 ISC10 Description ;0 0 The low level of INT1 generates an interrupt request. ;0 1 Any logical change on INT1 generates an interrupt request. ;1 0 The falling edge of INT1 generates an interrupt request. ;1 1 The rising edge of INT1 generates an interrupt request. ldi a,0b00000000 out MCUCR,a ;voir p:64 du datasheet.pdf et Tavernier p:30 ;------------------------------------------------------------------------ ;GICR (GENERAL INTERRUPT CONTROL REGISTER) ;page 47 et 65 du datasheet ;bit 7 = INT1 enable ;bit 6 = INT0 enable ldi a,0b00000000 out GICR,a ;------------------------------------------------------------------------ initvar: clr a sts TAB_AFF8+0,a sts TAB_AFF8+1,a sts TAB_AFF8+2,a sts TAB_AFF8+3,a sts TAB_AFF8+4,a sts TAB_AFF8+5,a sts TAB_AFF8+6,a sts TAB_AFF8+7,a ldi a,5 ;c'est a dire 10 puissance 5 mov fact_div,a ldi r20,BYTE1(16670) ldi r21,BYTE2(16670) ldi r22,BYTE3(16670) mov r0,r20 ;r20,21,22 := Fout mov r1,r21 mov r2,r22 ldi rang,4 ;rang (position decimale) du chiffre a modifier ;------------------------------------------------------------------------ ;zone de tests de procedures ; cli ;arret des interruptions (pendant l'affichage qui utilise une tempo) ; ldi a,0 ; sts V_out,a ; ;test1: lds a,V_out ; inc a ; sts V_out,a ; rcall dt_out2 ; rcall tp1ms ; ; rjmp test1 ;------------------------------------------------------------------------ ;PROGRAMME PRINCIPAL ;------------------------------------------------------------------------ cli ;arret des interruptions (pendant l'affichage qui utilise une tempo) ldi a,0b00010000 mov r15,a ;valeur constante dans r15 durant tout le programme (pour economiser une instruction dans INT_EXT0) zero: rcall dspclr ;efface LCD rcall home rcall fset rcall setmod0 rcall dsp10c1 ldi a,0 rcall ddras rcall tp100ms mov r20,r0 ;r20,21,22 := Fout mov r21,r1 mov r22,r2 rcall storeF .equ nbr_chiffres = 5 ldi a,0 rcall ddras rcall affnb8 ldi a,nbr_chiffres ldi point_dec,0 set rcall affiphyN mov a,rang subi a,8-nbr_chiffres rcall ddras rcall tp100ms sei ;Sets the Global Interrupt flag (I) in SREG (status register) rjmp entree ;------------------------------------------------------------------------ ;BOUCLE PRINCIPALE bcl0: rcall scrute entree: cli ldi a,0 rcall ddras mov r20,r0 ;r20,21,22 := Fout mov r21,r1 mov r22,r2 rcall affnb8 ;affiche la frequence du VCO ldi a,nbr_chiffres ldi point_dec,0 set rcall affiphyN rcall affi_fact_div rcall out74151 rcall affiFout mov a,rang subi a,8-nbr_chiffres rcall ddras sei rcall attente ;attend relachement des touches rcall tp100ms ;ne pas supprimer rjmp bcl0 ;------------------------------------------------------------------------ ;ROUTINES ;------------------------------------------------------------------------ ;attend qu'une touche soit appuyee et la traite scrute: in n_touche,pind com n_touche andi n_touche,0b11011011 ;masque breq scrute ;si aucune touche appuyee ;---------------------------------- ;TOUCHE DEPLACE CURSEUR A GAUCHE cpi n_touche,1 brne scr2 dec rang cpi rang,4 brpl scr1a ldi rang,3 scr1a: rcall tp100ms rjmp fin_scr ;---------------------------------- ;TOUCHE DEPLACE CURSEUR A DROITE scr2: cpi n_touche,2 brne scr3 inc rang cpi rang,8 brlo scr2a ldi rang,7 scr2a: rcall tp100ms rjmp fin_scr ;---------------------------------- ;TOUCHE INCREMENTE Fout (uniquement le digit sous le curseur) scr3: cpi n_touche,8 brne scr4 rcall charge1p ;(valeurs positives pour incrementer Fout) mov r20,r0 ;r20,21,22 := Fout mov r21,r1 mov r22,r2 rcall add24a ;r21,22,23:=r21,22,23 + nombre1 Modifie : Fout:=Fout + nombre1(multiple de 10) .equ Fmax = 20000 ldi r23,BYTE1(-Fmax) ; -1 * butee max (20000 max) ldi r24,BYTE2(-Fmax) ldi r25,BYTE3(-Fmax) add r23,r20 adc r24,r21 adc r25,r22 brcc scr31 ldi r20,BYTE1(Fmax) ldi r21,BYTE2(Fmax) ldi r22,BYTE3(Fmax) scr31: rjmp storeF ;---------------------------------- ;TOUCHE DECREMENTE Fout (uniquement le digit sous le curseur) scr4: cpi n_touche,16 brne scr5 rcall charge1m ;(valeurs negatives pour decrementer Fout) mov r20,r0 ;r20,21,22 := Fout mov r21,r1 mov r22,r2 rcall add24a ;r21,22,23:=r21,22,23 + nombre1 ;TEST si pas <0 ;ne pas supprimer ce test qui ne fait pas double emploi avec le suivant ! brcs scr41 ;si pas debordement (par valeur negative) rcall charge1p ;si debordement (par valeur negative) rcall add24a ;RAZ derniere decrementation ;BUTEE MIN (2000) .equ Fmini = 2000 scr41: ldi r23,BYTE1(-Fmini) ;butee min ldi r24,BYTE2(-Fmini) ldi r25,BYTE3(-Fmini) add r23,r20 adc r24,r21 adc r25,r22 brcs scr42 ldi r20,BYTE1(Fmini) ldi r21,BYTE2(Fmini) ldi r22,BYTE3(Fmini) scr42: rjmp storeF ;---------------------------------- ;TOUCHE decremente facteur de division scr5: cpi n_touche,64 brne scr6 dec fact_div brpl scr5b inc fact_div scr5b: rcall tp100ms rjmp fin_scr ;---------------------------------- ;TOUCHE incremente facteur de division scr6: cpi n_touche,128 brne fin_scr inc fact_div mov a,fact_div cpi a,6 ;c'est a dire 1/10^6 = 1/1000 000 (voir schema) brlo scr6b ldi a,6 mov fact_div,a scr6b: rcall tp100ms rjmp fin_scr ;---------------------------------- ;on memorise la [frequence a generer en sortie du VCO]x10 dans r0,1,2 ;r0,1,2 = [2000...20000] ; 2000 pour 200kHz en sortie du VCO ;20000 pour 2MHz en sortie du VCO storeF: mov r0,r20 mov r1,r21 mov r2,r22 Mset24 nombre1, -1 ;pour que le rapport de division soit exact (le registre compte depuis zero) rcall add24a ;page 77 du datasheet: ;To do a 16-bit write, the High byte must be written before the Low byte. For a 16-bit ;read, the Low byte must be read before the High byte. cli out OCR1AH,r21 out OCR1AL,r20 sei rcall sortie_V fin_scr: ret ;------------------------------------------------------------------------ ;sortie d'une tension vers le 4015 du convertisseur N/A (externe) pour piloter le VCO ;la valeur a sortir sur vers le 4015 est egale a: (voir feuille tableur) ;V_out = 42 + 0.0025 * [r0,1,2] ;c.a.d = 42 + [r0,1,2]/400 ;=42 + [r0,1,2]/80/5 sortie_V: mov r20,r0 mov r21,r1 mov r22,r2 ldi r23,80 rcall div24_8 ldi r23,5 rcall div24_8 ;le resultat tient dans r20 ldi a,42 add r20,a ;le resultat (=51..87) tient toujours dans r20 sts V_out,r20 ;on le memorise en RAM dans V_out rcall dt_out2 ret ;------------------------------------------------------------------------ ;attend relachement des touches attente: in n_touche,pind com n_touche andi n_touche,0b00001111 cpi n_touche,0 brne attente ret ;------------------------------------------------------------------------ ;charge une valeur decimale multiple de 10 dans nombre1 ;remarque : un tableau en FLASH (lu par l'instruction LPM) ne m'a pas paru judicieux, vu la taille des mots (3 octets) ;je verrais plutot une version calculee de cette routine (par multiplication de 10 dans une boucle) charge1p: cpi rang,7 brne cg6p Mset24 nombre1, 1 ;appel ; nombre1(sur 3octets):=1 cg6p: cpi rang,6 brne cg5p Mset24 nombre1, 10 ;appel ; nombre1(sur 3octets):=10 cg5p: cpi rang,5 brne cg4p Mset24 nombre1, 100 ;appel ; nombre1(sur 3octets):=100 cg4p: cpi rang,4 brne cg3p Mset24 nombre1, 1000 ;appel ; nombre1(sur 3octets):=1000 cg3p: cpi rang,3 brne cg2p Mset24 nombre1, 10000 ;appel ; nombre1(sur 3octets):=10000 cg2p: cpi rang,2 brne cg1p Mset24 nombre1, 100000 ;appel ; nombre1(sur 3octets):=100000 cg1p: cpi rang,1 brne cg0p Mset24 nombre1, 1000000 ;appel ; nombre1(sur 3octets):=1000000 cg0p: ret ;------------------------------------------------------------------------ ;charge une valeur decimale multiple de 10 dans nombre1 charge1m: cpi rang,7 brne cg6m Mset24 nombre1, -1 ;appel ; nombre1(sur 3octets):=-1 cg6m: cpi rang,6 brne cg5m Mset24 nombre1, -10 ;appel ; nombre1(sur 3octets):=-10 cg5m: cpi rang,5 brne cg4m Mset24 nombre1, -100 ;appel ; nombre1(sur 3octets):=-100 cg4m: cpi rang,4 brne cg3m Mset24 nombre1, -1000 ;appel ; nombre1(sur 3octets):=-1000 cg3m: cpi rang,3 brne cg2m Mset24 nombre1, -10000 ;appel ; nombre1(sur 3octets):=-10000 cg2m: cpi rang,2 brne cg1m Mset24 nombre1, -100000 ;appel ; nombre1(sur 3octets):=-100000 cg1m: cpi rang,1 brne cg0m Mset24 nombre1, -1000000 ;appel ; nombre1(sur 3octets):=-1000000 cg0m: ret ;---------------------------------------------------------------------- ;affiche le facteur de division (utilise par les CD4518 en sortie) affi_fact_div: ldi a,6 ;emplacement d'affichage sur la 1ere ligne rcall ddras ldi AA,8 fdv1: ldi a,' ' rcall ecrire dec AA brne fdv1 mov a,fact_div rcall affi_a ldi a,1 ldi point_dec,0 set rcall affiphyN ldi a,6 ;emplacement d'affichage sur la 1ere ligne rcall ddras ldi a,'1' rcall ecrire mov AA,fact_div cpi AA,0 breq fdv3 fdv2: ldi a,'0' rcall ecrire dec AA brne fdv2 fdv3: ret ;---------------------------------------------------------------------- ;Affiche la frequence de sortie sur la deuxieme ligne affiFout: ldi a,64 rcall ddras mov r20,r0 mov r21,r1 mov r22,r2 rcall affnb8 ;en memoire mov a,fact_div cpi a,3 brlo aff2 rcall aff_Hz rjmp fin_af aff2: rcall aff_kHz rjmp fin_af fin_af: ret ;---------------------------------------------------------------------- aff_kHz: ldi a,5 ldi point_dec,2 add point_dec,fact_div clt rcall affiphyN ldi a,'k' rcall ecrire ldi a,'H' rcall ecrire ldi a,'z' rcall ecrire ret ;---------------------------------------------------------------------- aff_Hz: ldi point_dec,-1 add point_dec,fact_div set ;pour afficher 0 au debut mov a,fact_div cpi a,6 breq aff_Hz2 clt ;pour ne pas afficher 0 au debut aff_Hz2: ldi a,5 rcall affiphyN ldi a,'H' rcall ecrire ldi a,'z' rcall ecrire ldi a,' ' rcall ecrire ret ;---------------------------------------------------------------------- ;sortie physique du facteur de division vers le 74LS151 out74151: mov a,fact_div ;------------ ;les 8 lignes suivantes permuttent les valeurs a sortir conformement au cablage "tordu" du LS151 sur mon proto ;ne pas les conserver sur le montage definitif ;ca aura au moins eu l'avantage de m'apprendre a utiliser les tableaux en memoire de programme...! ldi ZH, high(Table_1<<1);Initialize Z pointer (le shift gauche because le LSB est reserve. voir doc) ldi ZL, low(Table_1<<1) andi ZL,0b11111110 ;RAZ LSB pour acceder aux octets de poids faible lsl a ;shift gauche aussi add ZL,a ;decallage dans le tableau (voir plus bas) clr a adc ZH,a lpm a, Z ;Load constant from program ;------------ cbi portb,0 sbrc a,0 sbi portb,0 cbi portc,4 sbrc a,1 sbi portc,4 cbi portc,5 sbrc a,2 sbi portc,5 ret ;---------------------------------------------------------------------- Table_1: .DW 0,2,3,5,6,4,1 ;======================================================================== ; PROCEDURES D'AFFICHAGE PHYSIQUE SUR LE LCD ;======================================================================== ;FUNCTION SET fset: cbi portc,2 ;E=0 cbi portc,3 ;R/S=0 ldi a,56 ;56=(32 + 16(=8bits) + 8(=2lignes)) ou 48=(32 + 16(=8bits) + 0(=1ligne)) sts dtlcd,a rcall dt_out1 ;transmission série vers 4015 -> DATA // LCD rcall impuls ret ;---------------------------------------------------------------------- ;DISPLAY CLEAR dspclr: cbi portc,2 ;E=0 cbi portc,3 ;R/S=0 ldi a,1 sts dtlcd,a rcall dt_out1 rcall impuls ret ;---------------------------------------------------------------------- ;RETURN HOME home: clr a out portc,a ;clrf portc ldi a,2 ;movlw .2 sts dtlcd,a rcall dt_out1 rcall impuls ret ;---------------------------------------------------------------------- ;ENTRY MODE SET 0 ;le curseur se déplace setmod0: cbi portc,2 ;E=0 cbi portc,3 ;R/S=0 ldi a,6 ;( 4 + 2(sens) + (accompagnement)) sts dtlcd,a rcall dt_out1 rcall impuls ret ;---------------------------------------------------------------------- ;DISPLAY ON curseur OFF dsp10c0: cbi portc,2 ;E=0 cbi portc,3 ;R/S=0 ldi a,12 ;( 8+4 ) sts dtlcd,a rcall dt_out1 rcall impuls ret ;---------------------------------------------------------------------- ;DISPLAY ON curseur ON dsp10c1: cbi portc,2 ;E=0 cbi portc,3 ;R/S=0 ldi a,14 ;( 8+4+2 ) sts dtlcd,a rcall dt_out1 rcall impuls ret ;---------------------------------------------------------------------- ;DDRAM ADRESS SET (A) ;a doit contenir l'adresse (position d'affichage. voir doc) .def b=r20 ddras: push r20 cbi portc,2 ;E=0 cbi portc,3 ;R/S=0 ldi b,128 ;addlw .128 add a,b sts dtlcd,a rcall dt_out1 rcall impuls pop r20 ret ;---------------------------------------------------------------------- ;ECRIRE ;a doit contenir la valeur ASCII du caractère à afficher ecrire: wdr cbi portc,2 ;E=0 sbi portc,3 ;R/S=1 sts dtlcd,a rcall dt_out1 sbi portc,2 ;impuls E à 1 avec RS=1 rcall tp1ms sbi portc,2 ;fin impulsion rcall tp1ms ret ;---------------------------------------------------------------------- ;impulsion E à 1 avec RS=0 impuls: wdr cbi portc,3 ;R/S=0 sbi portc,2 ;impulsion E à 1 rcall tp1ms cbi portc,2 ;fin impulsion rcall tp1ms ret ;---------------------------------------------------------------------- ;SORTIE DATA (8 bits) en série sur PC0 vers CD4015(1) PC1=Clock 4015(1) ;dtlcd doit contenir la donnee .def n=r20 dt_out1: push r20 ldi n,8 lds a,dtlcd dtbcl1: rol a ;bit de poids fort -> dans c ;rlf dtlcd,f cbi portc,0 ;bcf portc,0 ;bit = 0 (à priori) brcc dt_out11 ;btfsc STATUS,0 ;test carry sbi portc,0 ;bsf portc,0 ;non, bit = 0 dt_out11: sbi portc,1 ;clock 4015(1) rcall tp1ms cbi portc,1 ;fin clock rcall tp1ms dec n ;decfsz n,f cpi n,0 brne dtbcl1 pop r20 ret ;---------------------------------------------------------------------- ;SORTIE DATA (8 bits) en série sur PC0 vers CD4015(2) PB2=Clock 4015(2) ;V_out doit contenir la donnee a sortir .def n=r20 dt_out2: ldi n,8 lds a,V_out dtbcl2: rol a ;bit de poids fort -> dans c ;rlf dtlcd,f cbi portc,0 ;bcf portc,0 ;bit = 0 (à priori) brcc dt_out21 ;btfsc STATUS,0 ;test carry sbi portc,0 ;bsf portc,0 ;non, bit = 0 dt_out21: sbi portb,2 ;clock 4015(2) nop ;attention, duree tres breve cbi portb,2 ;fin clock nop ;attention, duree tres breve dec n ;decfsz n,f cpi n,0 brne dtbcl2 ret ;----------------------------------------------------------------------- ;decompose un octet [0..255] -> notation BCD dans variables aff1..4 ;unites -> aff1 ;dizaines -> aff2 ;centaines -> aff3 ;milliers (en fait toujours zero) -> aff4 ;a doit contenir l'octet à afficher ;ex: a=184 affi_a: push AA push BB mov AA,a rcall cvBDU ;BB=18 et AA=4 ; fractionne les unites et les dizaines sts TAB_AFF8,AA ;aff1=4 mov AA,BB ;AA=18 rcall cvBDU ;BB=1 et AA=8 ; si les dizaines sont > 9, fractionne en dizaines et centaines sts TAB_AFF8+1,AA ;aff2=8 sts TAB_AFF8+2,BB ;aff3=1 ldi a,0 sts TAB_AFF8+3,a ;affiche toujours zero sts TAB_AFF8+4,a sts TAB_AFF8+5,a sts TAB_AFF8+6,a sts TAB_AFF8+7,a pop BB pop AA ret ;------------------------------------------------------------------------ ;CONVERSION BINAIRE --> BCD ;appellee depuis affi_a qui push pop AA et BB ;nombre à convertir dans AA ;resultat dans BB (dizaines) et dans AA (unités) ;ex: AA=237 -> BB=23 et AA=7 ;ex2 a=84 -> BB=8 et AA=4 ; cvBDU: clr BB conv2: cpi AA,10 ;AA:=AA-10 brcs conv3 ;9 passages au max si nb <= 99 subi AA,10 inc BB rjmp conv2 conv3: ret ;------------------------------------------------------------------------ ;decode un nombre de 5 chiffres (2 octets) en vu de l afficher en notation decimale ;NB = 0...65535 ;CONVERSION BINAIRE 2 octets -> Affi5,4,3,2,1 ;r20,21 doivent contenir le nombre a convertir. r20=poids faible affnb: push AA clr r22 ;byte du dividende non utilisee ldi r23,10 ;diviseur ldi r26,LOW(TAB_AFF8) ;registre x = adresse (TAB_AFF8) ldi r27,HIGH(TAB_AFF8) ;registre x ldi AA,5 ;compteur de boucle affnb1: rcall Div24_8 ;(ne touche pas r26) st x+,r24 ;reste de la division par 10 dec AA brne affnb1 pop AA ret ;------------------------------------------------------------------------ ;decode un nombre de 8 chiffres (3 octets) en vu de l afficher en notation decimale ;NB = 0...16777215 ;CONVERSION BINAIRE 3 octets -> Affi8,7,6,5,4,3,2,1 ;r20,21,22 doivent contenir le nombre a convertir. r20=poids faible ;ex: 123456 =1 *256*256 + 226 *256 +64 *256 ;r20=64 ;r21=226 ;r22=1 affnb8: push AA ldi r23,10 ;diviseur ldi r26,LOW(TAB_AFF8) ;registre x = adresse de (TAB_AFF8) ldi r27,HIGH(TAB_AFF8) ;registre x ldi AA,8 ;compteur de boucle affnb81: rcall Div24_8 ;(ne touche pas r26) st x+,r24 ;reste de la division par 10 - cette instruction incremente x a chaque passage dec AA brne affnb81 pop AA ret ;------------------------------------------------------------------------ ;affichage physique de N chiffres (N=1..8) ;a doit contenir N ;point_dec doit contenir la position du point decimal ;le flag T gere l'affichage des zeros non significatifs: ;T=0 -> pas d'affichage des 0 ;T=1 -> afficher tout affiphyN: push AA push BB mov BB,a ldi r26,LOW(TAB_AFF8+8) ;registre x ldi r27,HIGH(TAB_AFF8+8);registre x ldi AA,8 ;compteur de boucle affphyN1: ld a,-x ;cette instruction decremente x a chaque passage ldi b,48 ;code ASCII de zéro add a,b cp BB,AA ;compare AA-BB brlo affphyN3 ;defense d'afficher brts affphyN2 ;saute la gestion des zeros non significatifs si T=1 ;ne pas afficher un zero non significatif brne affphyN2 ;est-ce le premier chifffre a afficher? cpi a,'0' ;oui. est-ce un zero ? brne affphyN2 ;non ldi a,' ' ;afficher un espace a la place du zero non significatif rcall ecrire rjmp affphyN3 affphyN2: rcall ecrire cp AA,point_dec brne affphyN3 ldi a,'.' rcall ecrire affphyN3: dec AA brne affphyN1 pop BB pop AA ret ;---------------------------------------------------------------------- ;ADDITION de registres sur 3 octets ;r20,21,22:= r20,21,22 + r0,r1,r2 add24r: add r20,r0 adc r21,r1 adc r22,r2 ret ;---------------------------------------------------------------------- ;ADDITION sur 3 octets (1) ;r20,21,22:= r20,21,22 + nombre 1 add24a: lds a,nombre1+0 add r20,a lds a,nombre1+1 adc r21,a lds a,nombre1+2 adc r22,a ret ;---------------------------------------------------------------------- ;ADDITION sur 3 octets (2) ;nombre1:= nombre 1 +nombre2 add24b: push AA push BB lds AA,nombre1+0 lds BB,nombre2+0 add AA,BB sts nombre1+0,AA lds AA,nombre1+1 lds BB,nombre2+1 adc AA,BB sts nombre1+1,AA lds AA,nombre1+2 lds BB,nombre2+2 adc AA,BB sts nombre1+1,AA pop BB pop AA ret ;---------------------------------------------------------------------- ;MULTIPLICATION 8 bits x 16 bits resultat sur 24 bits ;multiplie r20(poids faible),r21 , r22(poids fort) par 'a' ;produit dans r20,r21,r22 .def produit0=r20 ;produit poids faible .def produit1=r21 ;produit .def produit2=r22 ;produit poids fort .def n1=r23 mul16_8: ldi n1,17 ;compteur de boucle mul16_81: ror produit2 ror produit1 ror produit0 brcc mul16_82 ;lecture d'un bit de r20 ; test de ce bit add produit2,a ;si c' est un '1' on ajoute 'a' à A2 mul16_82: dec n1 brne mul16_81 ret ;---------------------------------------------------------------------- ;DIVISION 24 bits par 8 bits, resultat sur 24 bits ;note; le quotient est stocke dans le dividende; permet de decaller les deux a la fois ;r20,21,22 doivent contenir le dividende (r20=LSB) ;r23 doit contenir le diviseur ;resultat (Quotient) dans r20 (LSB) et r21 et r22 ;reste dans r24 ;le bit T=1 indique un debordement .def Dividend0=r20 ;pour la routine Div24_8 .def Dividend1=r21 ;pour la routine Div24_8 .def Dividend2=r22 ;pour la routine Div24_8 .def Diviseur =r23 ;pour la routine Div24_8. .def Aux0=r24 ;pour la routine Div24_8 .def Aux1=r25 .def n1=r16 ;attention: ecrase r16 (=a) div24_8: ldi n1,24 clr Aux0 ;clrf Aux+0 div24_80: lsl Dividend0 rol Dividend1 rol Dividend2 div24_81: rol Aux0 ;decalle le dividende dans 'Aux' (passage d'un bit par "c") rol Aux1 cp aux0,Diviseur brlo auxPetit auxGrand: sub Aux0,Diviseur sec rjmp saut1 auxPetit: clc saut1: rol Dividend0 ;decalle a gauche en incorporant 'c' comme bit 0 rol Dividend1 ;decalle a gauche avec passage d'un bit par "c" rol Dividend2 ;decalle a gauche et envoie un bit par 'c' dans 'Aux' (apres le saut) dec n1 ;important: ne touche pas a 'c' brne Div24_81 ret ;---------------------------------------------------------------------- ;TEMPO 1ms exactement avec un Qx=16.000MHz ;attention: sous reserve de ralentissement par les interruptions... .def i1=r20 .def i2=r21 tp1ms: push r20 push r21 ldi i1,16 bcl2: ldi i2,248 bcl3: dec i2 ;cpi i2,0 brne bcl3 nop ;pour ajuster exactement la duree nop nop dec i1 ;cpi i1,0 brne bcl2 nop pop r20 pop r21 ret ;---------------------------------------------------------------------- ;tempo 100ms .def n=r16 tp100ms: push r16 ldi n,100 bcl4: rcall tp1ms dec n ;cpi n,0 brne bcl4 pop R16 ret ;----------------------------------------------------------------------