PIC 16F887 en MikroBasic


Ik zit net te grasduinen in de datasheet van de 1827. En zojuist ook de bijbehorende .inc bekeken.
Ik begin het toch wel leuk te vinden moet ik zeggen.

Er zat nog een klein foutje in het programma: ;)

pic asm code:

 
call    delay_250mS
delay_250ms	movlw	d'250' 

De S van delay_250mS staat als hoofdletter.
Blijkbaar is alles case-sensitive (of je moet dat uit kunnen schakelen).

Case sensitivity altijd uitzetten:
Project -> Build Options -> Project -> MPAsm assembler -> Disable case sensitivity...

SET en EQU zijn om constantes een naampje te geven. Zijn beiden hetzelfde met 1 verschil:
EQU is eenmalig (kan niet gewijzigd worden), terwijl SET wel gewijzigd kan worden.

[Bericht gewijzigd door Arco op 9 november 2019 01:59:27 (42%)]

Arco - "Simplicity is a prerequisite for reliability" - www.arcovox.com

pic asm code:

 sysclock        equ	d'10000000'	;Crystal frequency 10MHz
fclock		set	sysclock >> 2	;Internal cycle clock
delay1ms_value	set	fclock / d'54000' + fclock / d'500000'    	;Delayloop value 1 ms
 

Dit ontgaat me even, wat er hier nu precies gebeurt.
sysclock, fclock, delay1ms_value zijn allemaal naampjes voor constanten en geen instructies voor zover ik het begrijp.

Maar wie zet nu de frequentie goed? Welke instructie doet dat dan?
en vanwaar het delen door 54000 en 500000?

Ik heb een fysiek kristal van 4 Mhz aangesloten en de waarde van d'10000000 veranderd in d'4000000.
De Led knippert nu met een interval van 40 seconden.

De deling is nodig, om (ongeveer) de juiste delays te krijgen.
(hangt van de routines af. Als je andere routine maakt zul je die deling weer moeten tweaken om de juiste waarde te krijgen...)
Sysclock is de oscillatorfrequentie, fclock de instructieclock en delay1ms de waarde voor de delayroutines. Zijn allemaal constantes.

Het programma zou 500mS aan/uit moeten knipperen. 40sec kan niet, is veel te veel.
Ik zie wel dat ik de config op externe oscillator had laten staan. Voor een kristal moet je die veranderen.

- _FOSC_LP voor een 32kHz kristal
- _FOSC_XT voor 4MHz en lager
- _FOSC_HS voor hoger als 4MHz

Er valt in frequentie niks 'goed te zetten'. De delayloops is zuiver een kwestie van instructiecycles tellen...
(op 4MHz een kwestie van tot 1.000.000 tellen voor 1sec... ;) )

Arco - "Simplicity is a prerequisite for reliability" - www.arcovox.com

Het aanbrengen van _FOSC_XT bracht de LED inderdaad naar een beduidend lagere knipperfrequentie terug.

Dat doe hij nu ca 0,5 sec.

De waardes van delay_250ms t/m delay_1ms hebben vrijwel geen invloed op de knipperfrequentie. Ik heb wat waarden geprobeerd tussen d'0 en d'250 maar dat geeft geen (zichtbaar) effect.

Verder valt me op dat er geen RETURN instructie is na de call delay_250 ms
Dat hoeft niet?

Welke delay je kiest heeft wel degelijk invloed op de knipperfrequentie.
Bij delay_500mS moet de 250mS loop twee maal worden doorlopen, daarom geen return achter de eerste keer...

pic asm code:


delay_500ms     call    delay_250mS    ;Eerste 250mS loop
delay_250ms	movlw	d'250'         ;Tweede 250mS loop   
		goto    delay_ms
                ...
                ...
                ...
                decfsz  mscount,f              		
                goto    delay_ms_lp1           		
                retlw   0

Dit had ook gekund (maar kost meer code en levert niks extra's op):

pic asm code:


delay_500ms     call    delay_250mS    ;Eerste 250mS loop
                call    delay_250mS    ;Tweede 250mS loop   
                return

delay_250ms	movlw	d'250'         
		goto    delay_ms
                ...
                ...
                ...
                decfsz  mscount,f              		
                goto    delay_ms_lp1           		
                retlw   0

[Bericht gewijzigd door Arco op 10 november 2019 12:34:48 (32%)]

Arco - "Simplicity is a prerequisite for reliability" - www.arcovox.com

Maar wanneer (bij welk statement) eindigt dan een call zonder return?

Beide hebben een return: de retlw aan het eind van de routine. Let op dat alleen de eerste een call is, de tweede niet.

pic asm code:


delay_500ms     call    delay_250mS    ;Eerste 250mS loop
delay_250ms	movlw	d'250'         ;Tweede 250mS loop   
		goto    delay_ms
                ...
                ...
                ...
                decfsz  mscount,f              		
                goto    delay_ms_lp1           		
                retlw   0  <<<<<<<<----
Arco - "Simplicity is a prerequisite for reliability" - www.arcovox.com

Het kosttte wat moeite om alles te doorgronden, maar ik ben nu een heel eind.

De vele goto's maken een programma wat lastig om te doorgronden. Maar daar kom je niet onderuit denk ik.

In een ander artikel las ik dat de instructietijd = 4 / oscillatorfrequentie.

Maar waar dient dit statement

pic asm code:

 fclock		set	sysclock >> 2	;Internal cycle clock

dan voor?

In een ander artikel las ik dat de instructietijd = 4 / oscillatorfrequentie.

Instructieclock is oscillatorfrequentie / 4...
( sysclock >> 2 is hetzelfde als sysclock / 4 )

Arco - "Simplicity is a prerequisite for reliability" - www.arcovox.com

Ik heb het programmaatje wat versimpeld zodat er een knipperfrequentie overblijft van ca 1 sec.
Het lukt nu allemaal. Het blijft wel even wennen bij het maken van loops en condities, omdat door bijvoorbeeld de Decfsz er één statement wordt overgeslagen.

Er is geen end if of wat daar op lijkt, waarmee je een aantal statements zou kunnen vangen in het blok (zoals in basic).
In plaats daarvan moet het dan worden opgevangen met goto's.

Ik heb het nu zo:

pic asm code:

 ;**********************************************************************
                #include <p16f1827.inc>     ;processor specific variable definitions
   				__CONFIG _CONFIG1, _FOSC_XT & _WDTE_OFF & _BOREN_OFF & _PWRTE_ON & _CP_OFF & _CPD_OFF & _MCLRE_OFF & _IESO_ON
				__CONFIG _CONFIG2, _LVP_OFF & _PLLEN_OFF & _BORV_19
                ErrorLevel - 302
;**********************************************************************
   	org		00h                ;beginadres kiezen
		banksel	TRISA            	 ;kies bank 1
		clrf		TRISA              ;alles pinnen poort A uitgang maken
				
	CBLOCK 0X20
		Count1 ; delen door 250
		Count2 ; delen door 200
		Count3 ; delen door 4		
	ENDC 				
          
                
knipper	        
		banksel	LATA
		movf		LATA,w		 		;laad 01h in W
		xorlw  	1
		movwf		LATA		   		;zet de waarde van W
	        call 	        delay_1sec
		goto		knipper	

Delay_1sec								;delen door 4*200*250 = 200.000
		movlw d'4'
		movwf count3
loop1
		movlw d'200'
		movwf count2
loop2
		movlw d'250'
		movwf count1
loop3
		nop
		nop
		decfsz count1,f
		goto loop3
		decfsz count2,f
		goto loop2
		decfsz count3,f
		goto loop1
		return		
 END 

MPSIM is heel handig om timeloops te bestuderen, kun je stap voor stap of animated er doorheen lopen...
Er zweven wel hulpfiles met macro's rond die FOR/WHILE/DO/SELECT CASE functionaliteit bieden, zoals ENGR2210.INC (een keer getest en werkt, maar ik gebruik 't niet)

Arco - "Simplicity is a prerequisite for reliability" - www.arcovox.com

Bij nieuwe projecten wordt de vraag gesteld of het 'absolute' of 'relocatable' moet worden gebuild.

Wat is aanbevelenswaardig?

Het hangt er vanaf of het een klein project met 1 source is of uit meerdere sources bestaat...

Arco - "Simplicity is a prerequisite for reliability" - www.arcovox.com

Op 6 november 2019 16:12:56 schreef Arco:

Hier heb ik al eens een leuk voorbeeld gegeven in assembly: een 4 digit led klok met gelijkzetmogelijkheid in maar 171 words geheugen... ;)
https://www.circuitsonline.net/forum/view/message/2107242#2107242

Ik heb geen 648A. De uitdaging is om dit aan de praat te krijgen met een PIC16F628A.
Beiden lijken veel op elkaar als ik in de datasheets kijk, zij het dat de 648A meer Flash geheugen heeft zo te zien.

De displays lichten echter niet op.
(connecties / aansluitingen zijn goed, een testprogrammaatje toont dit aan).
Ik gebruik een extern kristal van 4 Mhz.

Zou gewoon moeten werken. Je moet wel de clock goedzetten met _FOSC_XT. (staat op externe oscillator 10MHz)
Voor 4 MHz moet je dan

code:


                banksel pr2                     ;Timer 2 value 
                movlw   d'124'
                movwf   pr2

veranderen in

code:


                banksel pr2                     ;Timer 2 value 
                movlw   d'49'
                movwf   pr2

Je kunt ook de 1827 code gebruiken. (die heb je toch ook?)

Arco - "Simplicity is a prerequisite for reliability" - www.arcovox.com

Ik heb er voor alle zekerheid een 10Mhz kristal in gedaan en de oude waarden laten staan.
Maar help niet.
Het programma lijkt niet te 'lopen'; zo staan alle Poorten B op 0, net zoals de vier 'driver' Poorten A3 t/m A0.
De MCLR is hoog, met een pull-up naar de +5V.

Zoals gezegd, hij staat op externe oscillator, dus geen kristal. Voor 10MHz moet je die op _FOSC_HS zetten...
Werkt hier prima met een 628a/648a/1827
Hexfile voor de 628a attached.

Arco - "Simplicity is a prerequisite for reliability" - www.arcovox.com

Nu weer even terug naar MikroBasic om 7-segment displays te multiplexen.
Ik tel tot 59 met 1 seconde als telfrequentie.

Op zich lukt dat wel, maar ik worstel alleen nog even met de vraag waar je nu de delay van 1000 ms neerlegt.
Onderstaand programma telt tot 59, maar je ziet de displays maar in een flits van 5ms vanwege de delay_ms(5).
Verhoog ik die delay, dan krijg je knipperen als effect.

Eigenlijk zou je de display loop continue willen laten lopen voor een strak beeld, maar hoe krijg je er dan in dat je per seconde de waarden ververst?

pic basic code:

program LedBasic

' Declarations section 

Const Cijfer as Byte [10] = (63, 6,  91, 79, 102, 109, 125, 7, 127, 111)
Const Disp as Byte [4] = (1, 2, 4, 8)

Dim Inhoud as Byte[4]

Dim D1 as byte
Dim D2 as byte
Dim X1 as byte

sub procedure Tonen()
for x1 = 0 to 1
   Portb = Inhoud[x1]
   Porta = Disp[x1]
   delay_ms(5)
   Porta = %00000000
next x1
end sub

main:

    trisa = %00000000
    porta = %00000000
    
    trisb = %00000000
    portb = %00000000

while true
   For D2 = 0 to 5
       Inhoud[1] = Cijfer[D2]
       For D1 = 0 to 9
          delay_ms(1000)
          Inhoud[0] = Cijfer[D1]
          Tonen()
       Next D1
   Next D2
wend
end.  

Eigenlijk heb je voor nette timing interrupts nodig zoals in mijn klok voorbeeld...
Belangrijkste is dat je alle displays om de beurt even lang laat zien. En dat met een snelheid hoger als een mens kan waarnemen.
(in mijn klok voorbeeld is de multiplexrate 250Hz, dwz. elk display is 1/250 seconde aan)

Arco - "Simplicity is a prerequisite for reliability" - www.arcovox.com

Jawel,
Maar als je ergens een delay inbouwt van 1 sec, dan wordt de subroutine Tonen pas om de seconde aangeroepen. En zie je daardoor maar heel even de displays oplichten.

Je moet ook 250x per seconde van display wisselen, niet 1 seconde lang hetzelfde display... ;) Dus:

- Disp1 aan, 4mS wachten, disp1 uit.
- Disp2 aan, 4mS wachten, disp2 uit.
- Disp3 aan, 4mS wachten, disp3 uit.
- Disp4 aan, 4mS wachten, disp4 uit.
- Disp1 aan, 4mS wachten, disp1 uit.
...
...

Arco - "Simplicity is a prerequisite for reliability" - www.arcovox.com

Dat was dus inderdaad de truc.

Het MikroBasic programma werkt nu.
Misschien wel niet helemaal zuiver qua tijd: 4 display's met een vertraging van 1ms 250 keer laten knipperen levert in principe 1000ms op.
Daarbij is de tijd die nodig is voor het aansturen van de displays niet meegeteld (wellicht valt dat mee).

In ieder geval hier mijn programma. (Wie had dat kunnen denken nog geen 2 maanden geleden...)

pic basic code:

  program LedBasic

' Declarations section 

Const Cijfer as Byte [10] = (63, 6,  91, 79, 102, 109, 125, 7, 127, 111)
Const Disp as Byte [4] = (1, 2, 4, 8)

Dim Inhoud as Byte[4]

Dim D1 as byte
Dim D2 as byte
Dim D3 as byte
Dim D4 as byte
Dim X1 as byte
Dim T1 as byte

sub procedure Tonen()
For T1 = 1 to 250
   For X1 = 0 to 3
      Portb = Inhoud[x1]
      Porta = Disp[x1]
      delay_ms(1)
      Porta = %00000000
   Next X1
Next t1
end sub

main:

    trisa = %00000000
    porta = %00000000
    trisb = %00000000
    portb = %00000000

while true
   For D4 = 0 to 5
      Inhoud[3] = Cijfer[D4]
      For D3 = 0 to 9
         Inhoud[2] = (Cijfer[D3] OR %10000000)
            For D2 = 0 to 5
               Inhoud[1] = Cijfer[D2]
               For D1 = 0 to 9
                  Inhoud[0] = Cijfer[D1]
                  Tonen()
               Next D1
           Next D2
    Next D3
   Next D4
wend

end.