Mikrobasic, Interrupts en multiplexen

Ik ben inmiddels weer een eind opgeschoten in de wereld van de microprocessoren en heb diverse experimenten uitgevoerd. Met name dankzij input van vele forumleden. (Arco spant wat dat betref de kroon).

Ik merk dat wanneer ik nu blader door de topics en posts, het veel meer zegt dan een tijd geleden.
Ik zie ook veel post staan uit bv de jaren vóór 2010. Is alweer 10 jaar geleden, maar voor mij is de materie 'nieuw'.

Goed, als ik dan zo vrij mag zijn met wat vraagjes:
Ik gebruik MikroBasic als programmeertaal. Het maken van een klokje gaat goed. Multiplexen van de displays ook.
Zo laat ik 4 digits 250 keer knipperen (met een aan en uit tijd van 1 ms). Dan krijg je dus automatisch een seconde 'delay':

pic basic code:

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  

Nu wil ik het gebruik van interrupts ook eens gaan proberen.
Maar hoe combineer je dan interrupts en het multiplexen?
Misschien is daar een voorbeeld van in MikroBasic?

Even snel omgezet van het assembly voorbeeld voor de 1827, niet getest...

pic basic code:


Program Clock

Const Segtab As Byte[10] = (0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f)
      Digtab As Byte[4]  = (0x1,0x2,0x4,0x8)

Dim DigCount   As Byte
    Digit      As Byte[4]
    Seccount   As Byte
    MinCount   As Byte
    KeyCount   As Byte
    Flags      As Byte
    fMinute    As sBit At Flags.0
    Dp         As sBit At LATB.7

Const Maxdigits      = 4

'------------------------------------------------------------------------------
sub procedure Interrupt() iv 0x0004 ics ICS_AUTO
  If TMR2IF_bit Then
    Inc(Seccount)
    If Seccount = 250 Then
      Seccount = 0
      Inc(MinCount)
      If MinCount = 60 Then MinCount=0 fMinute= 1 End If
    End If
    PORTA = DigTab[DigCount]
    PORTB = SegTab[DigCount]
    If (DigCount = 2) And (Seccount > 125) Then dp = 1 End If
    Inc(DigCount)
    If DigCount = MAXDIGITS THEN DigCount = 0 End If
    TMR2IF_bit = 0
  End If
end sub

'------------------------------------------------------------------------------
Sub Procedure Init()
  T2CON      = %00100111
  TMR2IE_bit = 1
  PR2        = 124
  TRISA      = %00010000
  TRISB      = %00000000
  ANSELA     = %00000000
  ANSELB     = %00000000
  GIE_bit    = 1
  PEIE_bit   = 1
  DigCount   = 0
  Digit[0]   = 0    
  Digit[1]   = 0    
  Digit[2]   = 0    
  Digit[3]   = 0
End Sub

'==============================================================================
Main:

  Init()
  While True
    If (Digit[3] = 2) And (Digit[2] = 4) Then
      Digit[0]=0    Digit[1]=0    Digit[2]=0    Digit[3]=0
    End If
    While fMinute = 0     Wend
    Inc(Digit[0])
    If Digit[0] = 10 Then Digit[0] = 0 Break End If
    Inc (Digit[1])
    If Digit[1] = 6 Then Digit[1] = 0 Break End If
    Inc (Digit[2])
    If Digit[2] = 10 Then Digit[2] = 0 Inc(Digit[3]) Break End If
    fMinute = 0
  Wend
  End.
Arco - "Simplicity is a prerequisite for reliability" - www.arcovox.com

Ietsje aangepast (is maar 6 words langer als de assembly versie):

pic basic code:


Program Clock

Const Segtab As Byte[10] = (0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f)
      Digtab As Byte[4]  = (0x1,0x2,0x4,0x8)

Dim DigCount   As Byte
    Digit      As Byte[4]
    Seccount   As Byte
    MinCount   As Byte
    KeyCount   As Byte
    Flags      As Byte
    fMinute    As sBit At Flags.0
    Dp         As sBit At LATB.7

Const Maxdigits      = 4

'------------------------------------------------------------------------------
sub procedure Interrupt() iv 0x0004 ics ICS_AUTO
  If TMR2IF_bit Then
    Inc(Seccount)
    If Seccount = 250 Then
      Seccount = 0
      Inc(MinCount)
      If MinCount = 60 Then MinCount=0 fMinute= 1 End If
    End If
    LATA = DigTab[DigCount]
    LATB = SegTab[DigCount]
    If (DigCount = 2) And (Seccount > 125) Then dp = 1 End If
    Inc(DigCount)
    If DigCount = MAXDIGITS THEN DigCount = 0 End If
    TMR2IF_bit = 0
  End If
end sub

'==============================================================================
Main:

'------------------------------------------------------------------------------
  T2CON      = %00100111
  TMR2IE_bit = 1
  PR2        = 124
  TRISA      = %00010000
  TRISB      = %00000000
  ANSELA     = %00000000
  ANSELB     = %00000000
  GIE_bit    = 1
  PEIE_bit   = 1
  DigCount   = 0
  Digit[0]   = 0
  Digit[1]   = 0
  Digit[2]   = 0
  Digit[3]   = 0

  While True
    If (Digit[3] = 2) And (Digit[2] = 4) Then
      Digit[0]=0    Digit[1]=0    Digit[2]=0    Digit[3]=0
    End If
    While fMinute = 0     Wend
    Inc(Digit[0])
    If Digit[0] = 10 Then 
      Digit[0] = 0
      Inc (Digit[1])
      If Digit[1] = 6 Then 
        Digit[1] = 0
        Inc (Digit[2])
        If Digit[2] = 10 Then
          Digit[2] = 0
          Inc(Digit[3])
        End If
      End If
    End If
    fMinute = 0
  Wend

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

Een eerste test (ik moest ahv de karaktertabel even uitpluizen aan welke pinnen de segmenten zitten).

In het programma heb ik de klok ingesteld op 8Mhz en hierbij het OSCCON register ingevuld (anders loopt er niks).

pic basic code:

 OSCCON     =  %01110000  

Het resultaat is dat de 4 digits staan te knipperen op een frequentie van ik schat 50 Hz, waarbij de cijfers continu op '0123' blijven staan.
Als je goed kijkt zie je dat ze wel achter elkaar worden getoond, dus er is sprake van multiplexing.
Maar de klok loopt niet.

Extra info: ik heb een PIC16F1826 ipv een 1827. Die zijn vrijwel gelijk.

Je originele code zal altijd achter gaan lopen, want de interval is nu 1ms (geen idee hoe nauwkeurig die is overigens), plus de tijd die de processor nodig heeft om de rest van die code uit te voeren.

Persoonlijk zou ik in de interrupt service routine alleen het display updaten; het heeft weinig zin om 1000x per seconde te kijken of er al een seconde verstreken is, en zolang het display geen cumulatieve fout krijgt zie jij echt niet of het een milliseconde te laat versprong.

Tegelijk is het niet veel rekenwerk, dus het zal wel passen, maar het kunt het overwegen.

Overigens kwam ik laatst tot de ontdekking dat 250Hz, tot mijn verbazing, niet hoog genoeg was om een display helemaal stil te laten staan; ik kan vanuit mijn ooghoeken nog steeds zien dan het knippert.

Een manager is iemand die denkt dat negen vrouwen in één maand een kind kunnen maken

Bij een klokfrequentie van 32Mhz (OSCCON = %11110000) is het flikkeren in ieder geval voorbij.

Digits blijven op 0123 staan; de klok 'loopt' dus niet. De decimale punt knippert wel (zo te zien 1 x per seconde). Maar is wel bij digit 1, dus 012.3

Ik denk dat ik het al heb gevonden.

pic basic code:

 LATB = SegTab[DigCount] 

moet zijn:

pic basic code:

  LATB = SegTab[digit]DigCount]]

.

Anders wordt de waarde van de digit niet juist getoond.

Klopt toch, he?

Klopt, was een foutje... ;)

Je hoeft niet 'uit te pluizen', segmenten zitten hetzelfde als bij de assemblyversie. Clock is 10MHz, voor andere waarde moet je PR2 van Timer2 aanpassen.
Klok liep te traag doordat de Timer2 prescaler op 64 stond i.p.v. 16...

Ik heb meteen de gelijkzetknop erbij geplakt...

pic basic code:


Program Clock

Const Segtab As Byte[10] = (0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f)
      Digtab As Byte[4]  = (0x1,0x2,0x4,0x8)

Dim DigCount   As Byte
    Digit      As Byte[4]
    Seccount   As Byte
    MinCount   As Byte
    KeyCount   As Byte
    Flags      As Byte
      fMinute  As sBit At Flags.0
      fKeyRpt  As sBit At Flags.1
    Dp         As sBit At LATB.7
    Button     As sBit At PORTA.4

Const Maxdigits      = 4         'Displayed digits
      Keytime        = 120       '0.6sec
      Keydebounce    = 10        '40mS
      Keyrpttime     = 10        '40mS

'----------------------------------------------------------------------------------------
sub procedure Interrupt() iv 0x0004 ics ICS_AUTO
'----------------------------------------------------------------------------------------
  If TMR2IF_bit Then                                                'See if timer2 irq
    Inc(Seccount)                                                   'If so, update coun-
    If Seccount = 250 Then                                          'ters.
      Seccount = 0                                                  'Set flag if minute
      Inc(MinCount)                                                 'passed.
      If MinCount = 60 Then MinCount=0 fMinute= 1 End If            '
    End If                                                          '
    '------------------------------------------------------------------------------------
    If Button = 0 Then                                              'Button pressed?
      Inc(KeyCount)                                                 'Yes, count + 1
      Select Case KeyCount                                          '
        Case KeyTime                                                'See if it's a nor-
          fKeyRpt  = 1                                              'mal or a repeated
          KeyCount = 0                                              'keypress.
        Case KeyDebounce                                            '
          fMinute   = 1                                             '
          SecCount  = 0                                             '
          MinCount  = 0                                             '
          If fKeyRpt Then KeyCount = 0 End If                       '
        Case KeyRptTime                                             '
          If fKeyRpt Then                                           '
            fMinute   = 1                                           '
            SecCount  = 0                                           '
            MinCount  = 0                                           '
            KeyCount  = 0                                           '
          End If                                                    '
      End Select                                                    '
    Else                                                            'No key, clear count
      fKeyRpt   = 0                                                 'and flag
      KeyCount  = 0                                                 '
    End If                                                          '
    '------------------------------------------------------------------------------------
    LATA = DigTab[DigCount]
    LATB = SegTab[Digit[DigCount]]
    If (DigCount = 2) And (Seccount > 125) Then dp = 1 End If
    Inc(DigCount)
    If DigCount = MAXDIGITS THEN DigCount = 0 End If
    TMR2IF_bit = 0
  End If
end sub

'========================================================================================
Main:

'----------------------------------------------------------------------------------------
  T2CON      = %00100110                                            'Post:5 - Pre:16
  PR2        = 124                                                  'Timer load value
  TRISA      = %00010000                                            'Only RA4 input
  TRISB      = %00000000                                            '
  ANSELA     = %00000000                                            'All ports digital
  ANSELB     = %00000000                                            '
  GIE_bit    = 1                                                    'Enable timer2 irq
  PEIE_bit   = 1                                                    '
  TMR2IE_bit = 1                                                    '
  DigCount   = 0                                                    'Clear digits and
  Digit[0]   = 0                                                    'digitcounter
  Digit[1]   = 0                                                    '
  Digit[2]   = 0                                                    '
  Digit[3]   = 0                                                    '
  '--------------------------------------------------------------------------------------
  While True                                                        '
    If (Digit[3] = 2) And (Digit[2] = 4) Then                       'If hour '24', reset
      Digit[0]=0    Digit[1]=0    Digit[2]=0    Digit[3]=0          'to 00:00
    End If                                                          '
    While fMinute = 0     Wend                                      'Wait for time update
    Inc(Digit[0])                                                   '
    If Digit[0] = 10 Then                                           'Update all 4 Digit-
      Digit[0] = 0                                                  'values
      Inc (Digit[1])                                                '
      If Digit[1] = 6 Then                                          '
        Digit[1] = 0                                                '
        Inc (Digit[2])                                              '
        If Digit[2] = 10 Then                                       '
          Digit[2] = 0                                              '
          Inc(Digit[3])                                             '
        End If                                                      '
      End If                                                        '
    End If                                                          '
    fMinute = 0                                                     'Reset flag
  Wend                                                              '
                                                                    '
  End.                                                              '
Arco - "Simplicity is a prerequisite for reliability" - www.arcovox.com

Ja, het werkt nu allemaal netjes.

Toch zou het fijn zijn voor wat uitleg hierbij, zodat ik het helemaal begrijp.

Er zijn dus 2 timers hier, de eerste geeft een interrupt en activeert als handler de procedure_Interrupt.
Waar kan ik nu zien wanneer deze interrupt wordt geactiveerd?
Ik heb wat lopen studeren op hoe het nu zit met de frequentie en 'scalers'.
Begrijp ik het goed dat de klokfrequentie eerst gedeeld wordt door 4, vervolgens een prescaler van 5 en daarna een postscaler van 16?
Als ik dat uitreken, kom ik op 10.000.000 / 4 / 5 / 16 = 31.250Hz. In tijd betekent dat 1/31250 = 32us. Is dat de interrupt time?

Dan wordt er in de interrupt routine gekeken of timer 2 een interrupt geeft dmv het afvragen van TMRIF_bit. Maar welk bit van dat TMRIF register is dat dan?
En waarom wordt hier een bit uitgelezen om te kijken of er sprake is van een interrupt? is er voor timer2 dan ook niet een interrupt-handler?

Het PR2 register wordt op 124 gezet. Moet ik dat zien als %01111100 ?
In de DS op pag 189 wordt er summier iets verteld over het PR2 register, maar ik kan de waardes niet herleiden.

Bij deze... ;)

Er zijn dus 2 timers hier, de eerste geeft een interrupt en activeert als handler de procedure_Interrupt.

Nee, er is 1 timer (timer2 in 8 bit mode). Timer2 roept iedere keer de interrupt aan waar dan weer diverse counters worden bijgehouden.
(seccount en mincount om tot 1 min. te tellen, en keycount om te zien hoe lang de toets is ingedrukt o.a. voor repeat functie van de toets)
De interrupt wordt gegenereerd wanneer de ingestelde waarde voor Timer2 overeenkomt met de actuele waarde. (die wordt dan weer '0')

Begrijp ik het goed dat de klokfrequentie eerst gedeeld wordt door 4, vervolgens een prescaler van 5 en daarna een postscaler van 16?

Ja, bij de 16F pics is de instructieclock fOsc/4. (bij de 24F pics is dat fOsc/2)

Het PR2 register wordt op 124 gezet. Moet ik dat zien als %01111100 ?

Ja,
De waarde 124 blijft 124, of je het binair, decimaal of hex schrijft...
124 is de waarde waar naartoe Timer2 moet tellen voordat er een interrupt komt. (je moet er '1' bij optellen om de juiste waarde te krijgen)

Als ik dat uitreken, kom ik op 10.000.000 / 4 / 5 / 16 = 31.250Hz. In tijd betekent dat 1/31250 = 32us.

Nee,
Je vergeet het PR2 register. Het is:

code:

 (10000000/4) / 5 / 16 / (124+1) = 250Hz 

 

Dan wordt er in de interrupt routine gekeken of timer 2 een interrupt geeft dmv het afvragen van TMRIF_bit. Maar welk bit van dat TMRIF register is dat dan?

TMR2IF_bit is (zoals de naam al zegt) een bit, geen register. Het zit in het PIR1 register.

In de DS op pag 189 wordt er summier iets verteld over het PR2 register, maar ik kan de waardes niet herleiden.

Is dus:

code:

(fOsc/4) / postscaler / prescaler / (PR2 value +1)
Arco - "Simplicity is a prerequisite for reliability" - www.arcovox.com

Tjonge,
Het is echt voor mij een cursus geworden. Het doet me sterk denken aan aan ver verleden, toen ik nog als jongen zwart/wit televisies repareerde en gebruik maakte van een - toen - heel populaire boekenreeks.
Met als hoofdrolspelers "Vraagal en Weetal".
Wie kent dat nog? ;)

Om me nog even in de rol van Vraagal te verplaatsen:

pic basic code:

 If TMR2IF_bit Then                                                'See if timer2 irq

- Ik begrijp dus goed dat TMR2IF_bit bit1 van het PIR Register is?
Maar de vraag: waarom moet je hier deze afvraging doen om te kijken of er sprake is van een interrupt conditie? Je zít toch immers al in de handler die werd aangeroepen ómdat er een interrupt was?

Na alle rekenarij begrijp is dus dat er iedere 4 msec (250Hz) een aanroep van de Interrupt subroutine plaatsvindt. Voor iedere seconde dus 250 keer. Hierdoor bereik je dus een goede balans om multiplexen zonder knipperen te regelen?

- Wie reset het DP? Hij wordt nl wel op 1 gezet, maar ik zie geen 'clear'. Komt dat omdat DP als een sbit is gedefinieerd en automatisch wordt gereset?

- Bij een MikroBasic project kan / moet je de klokfrequentie opgeven, alsmede hoe je het hebt ingericht (int, ext oscillator, etc).
Kan ik hieruit afleiden dat de compiler de setting van het OSCCON register dan voor zijn rekening neemt? Of moet je die toch zelf nog opgeven?

- Uit veel posts lees ik verschillende meningen over het gebruik van interrupts, Daar waar de een zegt "alleen gebruiken als het écht niet anders kan", beveelt een ander het direct weer aan als 'de' methode.

Los van wie het dichtst bij de waarheid ligt: een statement als Delay_MS of de timer maken beide gebruik van de systeemklok. Betekent dat dat een interrupt niet meer nauwkeurigheid oplevert dan een 'delay-oplossing?'

En nu zwijgt 'Vraagal' maar even..
Met excuses voor de hoeveelheid vragen... :/

Vragen staat vrij... ;)

Ik begrijp dus goed dat TMR2IF_bit bit1 van het PIR Register is?
Maar de vraag: waarom moet je hier deze afvraging doen om te kijken of er sprake is van een interrupt conditie? Je zít toch immers al in de handler die werd aangeroepen ómdat er een interrupt was?

De pic heeft tientallen interruptbronnen (timers, comparator, spi, i2c, a/d,...) die allemaal in dezelfde routine terecht komen.
Je moet dus wel vaststellen of dit de juiste interrupt is (zelfs als je denkt dat er maar 1 interrupt aan staat blijft dat wel zo netjes)

Na alle rekenarij begrijp is dus dat er iedere 4 msec (250Hz) een aanroep van de Interrupt subroutine plaatsvindt. Voor iedere seconde dus 250 keer. Hierdoor bereik je dus een goede balans om multiplexen zonder knipperen te regelen?

Ja, je krijgt 250/4 is 62.5Hz multiplex voor ieder display, meeste mensen zien dat niet meer als knipperen.
Frequenties lager beter vermijden. Ook 50Hz of een veelvoud daarvan. Geeft bij aanwezigheid van TL of ledlamp soms onaangename interferentiepatronen.

- Wie reset het DP? Hij wordt nl wel op 1 gezet, maar ik zie geen 'clear'. Komt dat omdat DP als een sbit is gedefinieerd en automatisch wordt gereset?

Er hoeft niks te worden gereset. Standaard staan in de segmenttable de dp's uit. De regel:

pic basic code:

If (DigCount = 2) And (Seccount > 125) Then dp = 1 End If

zorgt ervoor dat als digit 2 aan de beurt is, en de laatste helft van de seconde bezig is, de dp ook aangestoken wordt in display 2.

Bij een MikroBasic project kan / moet je de klokfrequentie opgeven, alsmede hoe je het hebt ingericht (int, ext oscillator, etc).
Kan ik hieruit afleiden dat de compiler de setting van het OSCCON register dan voor zijn rekening neemt? Of moet je die toch zelf nog opgeven?

Nee, dat moet je zelf doen. Mikrobasic heeft die info alleen nodig om de delayloops uit te kunnen rekenen...

- Uit veel posts lees ik verschillende meningen over het gebruik van interrupts, Daar waar de een zegt "alleen gebruiken als het écht niet anders kan", beveelt een ander het direct weer aan als 'de' methode.

Los van wie het dichtst bij de waarheid ligt: een statement als Delay_MS of de timer maken beide gebruik van de systeemklok. Betekent dat dat een interrupt niet meer nauwkeurigheid oplevert dan een 'delay-oplossing?'

De interrupt is nauwkeuriger, omdat die op vaste tijden komt.
Een delay_ms(1000) kan bij veel interrupt-afhandelingen best wel eens 1500ms duren.
(iedere keer als een interrupt moet worden afgehandeld wordt de delay() onderbroken, en al die onderbrekingen moet je bij de delay optellen)

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

De pic heeft tientallen interruptbronnen (timers, comparator, spi, i2c, a/d,...) die allemaal in dezelfde routine terecht komen.
Je moet dus wel vaststellen of dit de juiste interrupt is (zelfs als je denkt dat er maar 1 interrupt aan staat blijft dat wel zo netjes)

Dit verduidelijkt veel, bedankt! Je moet dus altijd de bron ('oorzaak') van de interrupt vaststellen.

De klok loop nu echt heel mooi. Ik heb hem vanmiddag gelijk gezet met de PC tijd en hij loopt nog helemaal in het gelid!

Ik vermoed dat het gebruik van een extern kristal (heb ik nu) altijd meer nauwkeurigheid oplevert dan gebruik te maken van de standaard interne klok, zoals de 1826 en 1827 heeft.
Toch?

Nee, dat moet je zelf doen. Mikrobasic heeft die info alleen nodig om de delayloops uit te kunnen rekenen...

Het OSCCON register van de PIC16F1826/1827:

5.6 Oscillator Control Registers
REGISTER 5-1: OSCCON: OSCILLATOR CONTROL REGISTER
R/W-0/0 R/W-0/0 R/W-1/1 R/W-1/1 R/W-1/1 U-0 R/W-0/0 R/W-0/0
SPLLEN IRCF<3:0> — SCS<1:0>
bit 7 bit 0
Legend:
R = Readable bit W = Writable bit U = Unimplemented bit, read as ‘0’
u = Bit is unchanged x = Bit is unknown -n/n = Value at POR and BOR/Value at all other Resets
‘1’ = Bit is set ‘0’ = Bit is cleared
bit 7 SPLLEN: Software PLL Enable bit
If PLLEN in Configuration Word 1 = 1:
SPLLEN bit is ignored. 4x PLL is always enabled (subject to oscillator requirements)
If PLLEN in Configuration Word 1 = 0:
1 = 4x PLL Is enabled
0 = 4x PLL is disabled
bit 6-3 IRCF<3:0>: Internal Oscillator Frequency Select bits
000x =31 kHz LF
0010 =31.25 kHz MF
0011 =31.25 kHz HF(1)
0100 =62.5 kHz MF
0101 =125 kHz MF
0110 =250 kHz MF
0111 =500 kHz MF (default upon Reset)
1000 =125 kHz HF(1)
1001 =250 kHz HF(1)
1010 =500 kHz HF(1)
1011 =1 MHz HF
1100 =2 MHz HF
1101 =4 MHz HF
1110 =8 MHz or 32 MHz HF(see Section 5.2.2.1 “HFINTOSC”)
1111 =16 MHz HF
bit 2 Unimplemented: Read as ‘0’
bit 1-0 SCS<1:0>: System Clock Select bits
1x = Internal oscillator block
01 = Timer1 oscillator
00 = Clock determined by FOSC<2:0> in Configuration Word 1.
Note 1: Duplicate frequency derived from HFINTOSC.

In het klokprogramma had ik geen OSCCON opgenomen, maar wel een 10Mhz kristal aangesloten en de instelling op Extern HS.
De klok loopt perfect.

Bovenstaande frequenties gelden alleen voor gebruik van een interne oscillator neem ik aan (10Mhz staat er immers ook niet eens bij).
Er is dus blijkbaar een 'default' waarde die maakt dat het toch werkt?
Of dat de interne oscillator wordt genegeerd).

Het OSCCON register doet inderdaad niets bij een externe clocksource (daar valt immers niks aan in te stellen)

Bij een extern kristal hoef je alleen LP(32kHz), XT(onder 8MHz), of HS(>=8MHz) in te stellen in config.
Aan frequentie valt niks in te stellen, want die staat al vast bij een kristal.

Bij een pic24 heb ik bij wat experimenten vastgesteld, dat de kristalfrequentie het meest precies was met een 12pF condensatortje aan OSC1(ingang),
en een 22pF condensatortje aan OSC2(uitgang)

De interne rc oscillator is niet bruikbaar voor een klok, die is veel te onnauwkeurig...

Bij het gelijkzetten van de klok kun je 'fast-forward' door de knop ingedrukt te houden, maar dat zul je al wel ontdekt hebben...

[Bericht gewijzigd door Arco op 26 november 2019 00:26:35 (10%)]

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

maar dat zul je al wel ontdekt hebben...

Jazeker! Ik sta er bovendien nog steeds versteld van om te zien hoe keurig het ding op tijd loopt. Na een hele nacht nog exáct gelijk met de PC klok.

Ik ga het programmaatje nu maar eens uitbreiden met de seconden.

Zijn maar een paar hele kleine wijzigingen... ;)
(pic16f1936 o.i.d.)

pic basic code:


Program Clock

Const Maxdigits      = 6         'Displayed digits
      Keytime        = 120       '0.6sec
      Keydebounce    = 10        '40mS
      Keyrpttime     = 2         '8mS

Const Segtab As Byte[10] = (0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f)
      Digtab As Byte[maxdigits]  = (0x1,0x2,0x4,0x8,0x10,0x20)

Dim DigCount   As Byte
    Digit      As Byte[maxdigits]
    Seccount   As Word
    KeyCount   As Byte
    Flags      As Byte
      fMinute  As sBit At Flags.0
      fKeyRpt  As sBit At Flags.1
    Dp         As sBit At LATB.7
    Button     As sBit At PORTC.0

'----------------------------------------------------------------------------------------
sub procedure Interrupt() iv 0x0004 ics ICS_AUTO
'----------------------------------------------------------------------------------------
  If TMR2IF_bit Then                                                'See if timer2 irq
    Inc(Seccount)                                                   'If so, update coun-
    If Seccount = 500 Then                                          'ters.
      Seccount = 0                                                  'Set flag if minute
      fMinute = 1
    End If                                                          '
    '------------------------------------------------------------------------------------
    If Button = 0 Then                                              'Button pressed?
      Inc(KeyCount)                                                 'Yes, count + 1
      Select Case KeyCount                                          '
        Case KeyTime                                                'See if it's a nor-
          fKeyRpt  = 1                                              'mal or a repeated
          KeyCount = 0                                              'keypress.
        Case KeyDebounce                                            '
          fMinute = 1                                               '
          SecCount  = 0                                             '
          If fKeyRpt Then KeyCount = 0 End If                       '
        Case KeyRptTime                                             '
          If fKeyRpt Then                                           '
            fMinute  = 1                                            '
            SecCount  = 0                                             '
            KeyCount = 0                                            '
          End If                                                    '
      End Select                                                    '
    Else                                                            'No key, clear count
      fKeyRpt   = 0                                                 'and flag
      KeyCount  = 0                                                 '
    End If                                                          '
    '------------------------------------------------------------------------------------
    LATA = DigTab[DigCount]
    LATB = SegTab[Digit[DigCount]]
    If ((DigCount = 2) Or (DigCount = 4)) And (Seccount > 250) Then dp = 1 End If
    Inc(DigCount)
    If DigCount = MAXDIGITS THEN DigCount = 0 End If
    TMR2IF_bit = 0
  End If
end sub

'========================================================================================
Main:                                                               '
'----------------------------------------------------------------------------------------
  Dim lCnt As Byte

  T2CON      = %00100101                                            'Post:5 - Pre:16
  PR2        = 250                                                  'Timer load value
  TRISA      = %00000000                                            'Only RA4 input
  TRISB      = %00000000                                            '
  TRISC      = %00000001
  ANSELA     = %00000000                                            'All ports digital
  ANSELB     = %00000000
  GIE_bit    = 1                                                    'Enable timer2 irq
  PEIE_bit   = 1                                                    '
  TMR2IE_bit = 1                                                    '
  DigCount   = 0                                                    'Clear digits 
  For lCnt = 0 To Maxdigits-1                                       '
    Digit[lCnt]  = 0                                                '
  Next lCnt                                                         '
  Seccount   = 0                                                    '
'--------------------------------------------------------------------------------------
  While True                                                        '
    If (Digit[5] = 2) And (Digit[4] = 4) Then                       'If hour '24', reset
      For lCnt = 0 To Maxdigits-1                                   'to 00:00:00
        Digit[lCnt]  = 0                                            '
      Next lCnt                                                     '
    End If                                                          '
    While fMinute = 0     Wend                                      'Wait for time update
    Inc(Digit[0])                                                   '
    If Digit[0] = 10 Then                                           '
      Digit[0] = 0                                                  '
      Inc(Digit[1])                                                 '
      If Digit[1] = 6 Then                                          '
        Digit[1] = 0                                                '
        Inc(Digit[2])                                               '
        If Digit[2] = 10 Then                                       'Update all 4 Digit-
          Digit[2] = 0                                              'values
          Inc (Digit[3])                                            '
          If Digit[3] = 6 Then                                      '
            Digit[3] = 0                                            '
            Inc (Digit[4])                                          '
            If Digit[4] = 10 Then                                   '
              Digit[4] = 0                                          '
              Inc(Digit[5])                                         '
            End If                                                  '
          End If                                                    '
        End If                                                      '
      End If                                                        '
    End If                                                          '
    fMinute = 0                                                     'Reset flag
  Wend                                                              '
                                                                    '
  End.                                                              '

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

(pic16f1936 o.i.d.)

Zal idd wel moeten; de PIC16f1826/1827 heeft geen C-poort. Voor de Button is daar geen plaats meer voor, A6 en A7 worden gebruikt voor het kristal.
De B-poort is volledig in gebruik voor de segmenten.

Ik heb het programma even getest met de 1826 zonder de button, maar het loopt nu súpersnel, de seconden flitsen over de display, het lijkt wel een stopwatch.. :)

Werkt wel, hoor...
Je moet de button natuurlijk wel uitzetten dan. (nu denkt 'ie dat de button continu ingedrukt wordt.)
Even commentaar maken van het 'If button = 0 Then...... End If' blok in de interrupt. (een ' ervoor zetten)

De 1827 komt inderdaad een pin tekort voor 6 digits... (RA5 is input-only)

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

Tenzij ik de interne oscillator gebruik. Dan komt poort A6 en A7 vrij.
Maar dan is hij natuurlijk minder nauwkeurig.

Een klok die 15 minuten per dag voor of achter loopt vind ik niet erg nuttig... ;) (de interne oscillator is +/- 1% op zijn best...)
Met zo'n externe DIL oscillator zou 't wel net kunnen:

https://media.rs-online.com/t_large/F7960514-01.jpg

[Bericht gewijzigd door Arco op 27 november 2019 00:04:50 (32%)]

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

Deze ken ik niet. Die sluit je ook aan op A6 en A7? Dat kost dan toch nog steeds 2 poorten?
Of werkt dat anders

Een ander PIC-je is natuurlijk waarschijnlijker handiger. Zo heb ik hier ook nog een aantal PIC16F887. Wel iets groter qua formaat, maar met meerdere poorten.

Externe oscillator gebruikt maar 1 pin: OscIn

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

Een andere technische oplossing zou wellicht kunnen zijn om gebruik te maken van een aantal 74LS247 segment decoders. Daar heb ik er nog een berg van liggen.
Dan heb je maar 4 poorten nodig voor de ingang, terwijl deze een 'enable' heeft om de displays uit te zetten. Kost wel 6 Ic's, maar scheelt ook wel weer 6 transistoren met 6 input-weerstanden.
Tja.

Maar andere PIC blijft aantrekkelijk.
Daar komt bij dat de klok een oefen project is. Want het is leuk om het te maken. Maar er zijn ook complete klok-ic's die alles doen.

Ook kan je natuurlijk een RTC zoals DS3231 gebruiken, met zijn eigen temperatuur gecontroleerde oscillator aan boord. Een klein moduultje voor ca € 0,68. (Hoe kunnen ze het maken, en er zit nog winst op ook).

En die inhoud displayen.

En niet te vergeten de mogelijkheid met LED matrixen (vind ik ook leuker dan 7 segmenten), met de MAX7219 aan boord.

Ik kan natuurlijk zelf ook matrixen maken van LED's met diverse formaten, zodat het een 'kunstwerkje' wordt dat een plekje in de huiskamer verdient.

7-segmenten hebben veel weg van wekker-radio's..