Tonen generen met PIC16F628A

Een puzzelstukje is nog even om een 'timer' in te stellen, dwz hoe lang een procedure moet worden uitgevoerd.

Daarbij vraagt de Delay instructie een integer. Maar als je de frequentie naar tijd gaat omrekenen, kom je met afrondingsverschillen.

Bijvoorbeeld freq = 523 Hz. Tijd = 1000 / 523 = 1,9 en zal hij 1 van maken.

Ja,,
Ingebakken delay functies zijn alleen voor het groffe werk, voor precieze timing zul je zelf een (meestal interrupt gestuurde) timer moeten maken...
Als je veel interrupts gebruikt, zijn de ingebouwde delays vreselijk onnauwkeurig, delay_mS(100) kan dan wel 150mS of meer duren.
(dit omdat de delay steeds onderbroken wordt door een interrupt)

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

Voor de timerfunctie had ik inmiddels al een truc bedacht: je weet de frequentie, en daardoor de tijdsinterval. Door het tellen van alle pulsen kan je vaststellen wanneer de limiet (duur) is bereikt.
Dan kan met for -- next

Inmiddels heb ik het programaatje wat uitgebreid en heb nu een PIC16F1827.

Deze MC werkt inderdaad wat lekkerder dan de 1826, vanwege zijn grotere capaciteit.

Ik zag trouwens nog een foutje in de Read_DS3231() (de AND's staan verkeerd)

pic basic code:


  Time[0] = Bcd2Dec(i2c1_Rd(1))
  Time[1] = Bcd2Dec(i2c1_Rd(1))
  Time[2] = Bcd2Dec(i2c1_Rd(1)) And 0x3F
  Time[3] = Bcd2Dec(i2c1_Rd(1))
  Time[4] = Bcd2Dec(i2c1_Rd(1))
  Time[5] = Bcd2Dec(i2c1_Rd(1)) And 0x7F
  Time[6] = Bcd2Dec(i2c1_Rd(0))

moet zijn:

pic basic code:


  Time[0] = Bcd2Dec(i2c1_Rd(1))
  Time[1] = Bcd2Dec(i2c1_Rd(1))
  Time[2] = Bcd2Dec(i2c1_Rd(1) And 0x3F)
  Time[3] = Bcd2Dec(i2c1_Rd(1))
  Time[4] = Bcd2Dec(i2c1_Rd(1))
  Time[5] = Bcd2Dec(i2c1_Rd(1) And 0x7F)
  Time[6] = Bcd2Dec(i2c1_Rd(0))
Arco - "Simplicity is a prerequisite for reliability" - www.arcovox.com

Zolang het century / 12/24 bit niet geset zijn geen problemen...

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

Ik had trouwens wat spulletjes besteld bij Farnell.
Supersnelle service moet ik zeggen!

Op 26 oktober 2019 18:12:21 schreef Arco:
Is bij de 1826/27/47 al gedeclareerd in de include file (P16F1826.MBAS)

[bijlage]

Is deze include wellicht uit te schakelen? Het is nl af en toe wat lastig met noten.

Je kunt hem editen (staat in de DEFS directory van de compiler.)
Weghalen zou ik niet doen; alle registernamen en config settings (o.a. PORTx, LATx, TRISx, ANSELx...) zitten daar ook in, die ben je dan ook kwijt.

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

Het werken met MikroBasic begint steeds beter te lopen.
Ook de samenwerking met PICKit3 is fijn. Compileren en PicKit pakt hem vanzelf op en programeert de uC.

Ik heb diverse settings van de Editor uitgeprobeerd.

Ik ben alleen nog veel aan het schuiven binnen de code vind ik. Zo mis ik wat in VB.NET zo mooi werkt, dat is dat hij de code mooi formateert. Bijvoorbeeld inspringen na een if en het netjes uitlijnen zodat je precies weet bij welke if de end hoort (handig als je veel geneste statements hebt).

Maar wellicht kan dat in MB ook.

Op 25 oktober 2019 19:41:55 schreef Arco:
[...]
Ik gebruik de PIC24FJ256GA106/108/110 vaak...
https://nl.farnell.com/search?st=pic24fj256ga1

Dit zijn wel grote dingen. Maar hoe gebruik je die in een testomgeving?
Dat kan dan vast niet op een breadboard neem ik aan. En zijn daar dan voetjes voor te krijgen?

Ik laat meteen een print maken als ik een ontwerp heb (kost tegenwoordig toch geen drol meer)
Maar er zijn ook boarden als de EasyPicFusion V7 waar je een losse module met de gewenste processor op kunt steken.

https://media.digikey.com/Photos/MikroElektronika/MFG_MIKROE-1205.JPG

Ik heb nog zo'n board liggen van de 18F serie (18F46xx/67xx/87xx serie)

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

Goh, wéér wat geleerd...
Dat krijg je als je zolang uit een hobby bent geweest.

Het gaat nu ineens heel snel, eerst met wat TTL spelen, PIC's programmeren, andere type uC, andere programmeer-omgeving...

Wel heel grappig (en tijdrovend).
En ook best spannend.

Ter leering ende vermaeck..

Hier is mijn programmaatje.
Een deurbel, die verschillende melodietjes speelt afhankelijk van de datum.

Met de PiC16F1827 als Micro Controller

pic basic code:

 '==============================================================================
Program Deurbel
'------------------------------------------------------------------------------
 'Notes       Frequency (Hz)
 Const E4  =  329
 Const G4  =  392
 Const A4  =  440
 Const A4h =  466
 Const B4a =  499
 Const C5  =  523
 Const C5h =  554
 Const D5  =  587
 Const D5h =  622
 Const E5  =  659
 Const F5  =  698
 Const G5  =  784
 Const A5  =  880
 Const A5a =  932
 Const B5a =  987
 Const C6  = 1047
 Const D6  = 1175

'Midsomer Murders
 Const NOTE as WORD [13] = (D5h, D5, D5h,D5, C5,A4h, D5,  C5,D5h, G5, C6,B5a,  G5)
 Const DUR  as Word [13] = (250,250,250,250,375,125,250,1000,250,750,500,250,1500)

'Lang zal ze leven
 Const NOTE1 as WORD [13] = ( C5,  C5,  C5,  C5,  G4,  E5,  E5,  E5,  E5,  C5)
 Const DUR1  as Word [13] = (500, 200, 200, 500, 700, 500, 200, 200, 500, 750)
 Const DLY1  as Byte [13] = ( 15,  15,  15,  15,   0,   0,  15,  15,  15,   0)

'Jingle Bells
 Const NOTE2 as WORD [11] = (E5,   E5,  E5,   E5,   E5,  E5,  E5,  G5,  C5,  D5,  E5)
 Const DUR2  as Word [11] = (250, 250, 500,  250,  250, 500, 250, 250, 250, 250, 750)
 Const DLY2  as Byte [11] = ( 15,  15, 100,   15,  15,  150,   0,   0,   0,  0,    0)
 
 'Hoor wie klopt daar kinderen
 Const NOTE3 as WORD [22] = (G4,   G4,  E5,   C5,   C5,  C5,  G4,  G4,  E5,  C5,  C5,  C5,  G4,   G4,   D5,  C5H,   D5, C5H,  D5,  E5,  C5)
 Const DUR3  as Word [22] = (220, 220, 220,  220,  220, 220, 220, 220, 220, 220, 220, 220, 250,  250,  250,  250,  250, 250, 250, 250, 700)
 Const DLY3  as Byte [22] = ( 15,  0,    0,   15,  15,  15,   15,   0,   0,  15,  15,   0,  15,    0,    0,    0,    0,   0,   0,   0,   0)
 
 'Stille Nacht
 Const NOTE4 as WORD [23] = ( G5,  A5,  G5,   E5,   G5,  A5,  G5,  E5,  D6,  D6,  B5a,  C6,  C6,  G5,  A5,  A5,  C6, B5a,  A5,  G5,  A5,  G5,  E5)
 Const DUR4  as Word [23] = (420, 420, 420,  620,  420, 420, 420, 620, 420, 320,  520, 420, 320, 520, 320, 220, 320, 220, 320, 320, 320, 320, 620)
 Const DLY4  as Byte [23] = (  0,   0,   0,  200,    0,   0,   0, 200,  15,   0,  200,  15,   0, 200, 15,   0,   0,   0,   0,   0,   0,   0,   0)
 
 'O Denneboom
 Const NOTE5 as WORD [16] = ( G4,  C5,  C5,   C5,   D5,  E5,  E5,  E5,  E5,  D5,  E5,  F5,  B4a, D5,  C5,  C5)
 Const DUR5  as Word [16] = (420, 220, 220,  520,  420, 220, 220, 520, 420, 420, 420, 320, 320, 420, 220, 420)
 Const DLY5  as Byte [16] = (  0,  15,  15,  200,    0,  15,  15,  15,   0,   0,   0,   0,   0,   0,  15,   0)

 'We wish you a mary Christmas
 Const NOTE6 as WORD [30]= (  C5,  F5,  F5,  G5,  F5 , E5,  D5,  D5,  D5,  G5,  G5,  A5,  G5,  F5,  E5,  C5,  C5,  A5,  A5, A5a,  A5,  G5,  F5,  D5,  C5,  C5,  D5,  G5,  E5,  F5)
 Const DUR6  as Word [30] = (420, 220, 220, 220, 220, 220, 220, 420, 420, 220, 220, 220, 220, 220, 220, 420, 420, 220, 220, 220, 220, 220, 220, 420, 420, 420, 420, 420, 420, 720)
 Const DLY6  as Byte [30] = (  0,  15,   0,   0,   0,   0,  15,  15,   0,  15,   0,   0,   0,   0,   0,  15,  15,  15,   0,   0,   0,   0,   0,   0,  15,   0,   0,   0,   0,   0)

 Const PLUS as byte = 100                           'Extra delay for speed melodie)
 Dim Time  As Byte[7]
 Dim Toon As Byte
 Dim Xmas as byte

 Dim Switch1 as sbit at PORTA.0

''==============================================================================
Sub procedure Write_DS3231()                       'Write Date in RTC registers
''------------------------------------------------------------------------------
  i2c1_Start()
  i2c1_Wr(0xD0)
  i2c1_Wr(0x00)
  i2c1_Repeated_Start()
  i2c1_Wr(0xD0)

  i2c1_Wr(0x01)   'Seconds
  i2c1_Wr(0x31)   'Minutes
  i2c1_Wr(0x12)   'Hour
  i2c1_Wr(0x3)    'Daynr
  i2c1_Wr(0x01)   'Day
  i2c1_Wr(0x11)   'Month
  i2c1_Wr(0x19)   'Year
  i2c1_Stop()
End Sub

'==============================================================================
Sub procedure Read_DS3231()                         'Read Date in RTC registers
'------------------------------------------------------------------------------
  i2c1_Start()
  i2c1_Wr(0xD0)
  i2c1_Wr(0x00)
  i2c1_Repeated_Start()
  i2c1_Wr(0xD1)

  Time[0] = Bcd2Dec(i2c1_Rd(1))
  Time[1] = Bcd2Dec(i2c1_Rd(1))
  Time[2] = Bcd2Dec(i2c1_Rd(1) And 0x3F)
  Time[3] = Bcd2Dec(i2c1_Rd(1))
  Time[4] = Bcd2Dec(i2c1_Rd(1))
  Time[5] = Bcd2Dec(i2c1_Rd(1) And 0x7F)
  Time[6] = Bcd2Dec(i2c1_Rd(0))
  i2c1_Stop()
End Sub

Sub Procedure Midsomer
   For Toon = 0 to 12
     Sound_Play(Note[Toon], (Dur[Toon] + Plus))
   Next Toon
 End Sub

 Sub Procedure Verjaardag
   For Toon = 0 to 9
     Sound_Play(Note1[Toon], (Dur1[Toon] + Plus))
     VDelay_MS(DLY1[Toon])
  Next Toon
  End sub

 Sub Procedure JingleBells
   For Toon = 0 to 10
     Sound_Play(Note2[Toon], (Dur2[Toon] + Plus))
     VDelay_MS(DLY2[Toon])
  Next Toon
 End sub

 Sub Procedure HoorWieKlopt
   For Toon = 0 to 21
     Sound_Play(Note3[Toon], (Dur3[Toon] + Plus))
     VDelay_MS(DLY3[Toon])
  Next Toon
 End sub

Sub Procedure StilleNacht
   For Toon = 0 to 22
     Sound_Play(Note4[Toon], (Dur4[Toon] + Plus))
     VDelay_MS(DLY4[Toon])
  Next Toon
 End sub

 Sub Procedure Denneboom
   For Toon = 0 to 15
     Sound_Play(Note5[Toon], (Dur5[Toon] + Plus))
     VDelay_MS(DLY5[Toon])
  Next Toon
 End sub

Sub Procedure Merry_Xmas
   For Toon = 0 to 29
     Sound_Play(Note6[Toon], (Dur6[Toon] + Plus))
     VDelay_MS(DLY6[Toon])
  Next Toon
 End sub

'==============================================================================
main:

'==============================================================================
  ANSELB = 0                                    'All output/digital
  ANSELA = 0                                    '
  TRISA  = 0                                    '
  TRISB  = 0
  PORTA =  %11111111                            'Port A to high
  OSCCON = %11110000                            '32MHz internal
  i2c1_init(100000)                             'Init i2c port 100kHz
  Sound_Init(PORTB, 3)                          'Init Sound Port (B.3)
  Xmas = 0
  'Write_DS3231()                               'Time can be reset by uncommend
  While True                                    'Endless loop waiting for S1
   if Switch1 = 0 then
      delay_ms(20)                              'Delay for depressing disturb
      if Switch1 = 0 then
         Read_DS3231()                          'Read RTCC
         Delay_ms(100)
         if ((Time[4] > 15) and (Time[5] = 11) Or ((Time[4] < 6) And (Time[5] = 12))) then
            HoorWieKlopt()
         else
            if ((Time[4] = 13) AND (Time[5] = 06) or ((Time[4] = 15) AND (Time[5] = 12))) then
              Verjaardag()
            else
              if (Time[4] = 24)AND (Time[5] = 12) then
                 StilleNacht()
              else
                 if (Time[4] > 5) AND (Time[4] < 27) AND (Time[5] = 12) then
                     Xmas = Xmas + 1
                     If Xmas > 3 then            'Around Xmas time there will be played 3 different melodies
                        Xmas = 1
                     end if
                     select case Xmas
                     case 1
                       JingleBells()
                     case 2
                       Denneboom()
                     case 3
                       Merry_Xmas()
                     end select
                 else
                   Midsomer()
                 end if
              end if
            end if
         end if
      end if
    end if
    Delay_ms(100)
  Wend

end. 

Mooi...

Voor het puntje op de i: PORTA en PORTB moeten eigenlijk LATA en LATB zijn... (werkt betrouwbaarder)

Zo mis ik wat in VB.NET zo mooi werkt, dat is dat hij de code mooi formateert.

Nooit naar gekeken, ik vind dat vreselijk... ;) (doe het liever zelf)

[Bericht gewijzigd door Arco op 1 november 2019 13:31:42 (43%)]

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

Op 1 november 2019 13:09:27 schreef Arco:
Mooi...

Voor het puntje op de i: PORTA en PORTB moeten eigenlijk LATA en LATB zijn... (werkt betrouwbaarder)

Ik had ergens gelezen dat je voor Input altijd PORT moet gebruiken en voor output altijd LAT.
Dat is dus niet zo?
Voor de sound (PORT B.3) moet het idd sowieso LAT zijn.

Voor de constanten had ik zelf liever een tabel gehad met één naam, waarbij je met indexen de juiste waarden selecteert.

Want nu heb ik voor ieder melodietje een eigen tabel én een eigen procedure.
Waarbij de lengte van de 'tabel' (aantal constanten) ook nog eens nogmaals moet worden opgegeven in de procedure.
Het is weliswaar recht-toe-recht-aan en heel zelfverklarend, maar het lijkt me zelf wat overdone en niet efficiënt.

Het zou hierbij mooi zijn als je een instructie had in de geest van LEN(NOTE..).

PORTA is toch ook output? (ik zie TRISA = 0...)
LATx is inderdaad output. Voorkomt RMW problemen...

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

Je hebt gelijk; er staat TRISA = 0.
Ik gebruik PORTA voor het afvragen van de schakelaar, dus input.

Maar het werkt wel.
Dan zal de status vermoedelijk automatisch op input worden gezet?

Wellicht hierdoor:

pic basic code:

 PORTA =  %11111111 

[Bericht gewijzigd door Bavelt op 1 november 2019 15:19:55 (16%)]

Je leest gewoon de pin status uit. Als die als output staat, zul je die (met de schakelaar) met geweld naar beneden trekken.
Dat vinden de output drivers niet fijn (die proberen de pin hoog te houden), maar ze overleven het meestal wel...

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

Ik heb het in ieder geval aangepast.

Ik heb de schakeling even getest zonder de RTC- DS3231 module erin. Dan blijft de processor wachten tot hij iets krijgt. Eeuwig dus.

Is er wellicht iets te bedenken dat je een afvraging kunt doen of de I2C Lees of schrijfopdracht wel is gelukt? Of een vorm van Time-out (waarbij je dan bv een LEDje kan laten branden bij een mislukte I/O).

Want stel je voor dat zo'n moduultje stuk is, dan hangt de boel dus ook.
In de Arduino is er een programmaatje dan alle adressen afspeurt of er een I2c Device is gevonden. (I2C-scanner).
Die zal ook wel iets in de geest doen of een I/O is gelukt, ACKnowledge, o.i.d.

Da's gebruikelijk met de meeste libraries dat i2c dan hangt... ;)
Daarom heb ik zelf een vervangende versie gemaakt...

pic basic code:


module lib_i2c

Const I2CTimeOut = 200
Dim I2cStatus As Byte

'Statusbits:    00000001 - Start
'               00000010 - Restart
'               00000100 - Stop
'               00001000 - Write
'               00010000 - Read
'               00100000 - Idle
'               01000000 - Receive buffer
'               10000000 - Busy

Sub Procedure I2CInit()
Sub Procedure I2CStart()
Sub Procedure I2CRestart()
Sub Procedure I2CStop()
Sub Procedure I2CWrite(Dim pDat As Byte)
Sub Procedure I2CIdle()
Sub Function  I2CRead(Dim pAck As Byte) As Byte

implements

'==================================================================================================
Sub Procedure I2CIdle()                                             'Wait for bus idle
'--------------------------------------------------------------------------------------------------
  Dim lCnt As Word                                                  '
  For lCnt = 0 To I2CTimeout                                        '
    If (I2C1CON And 0x1F) = 0  then break end if                    '
  Next lCnt                                                         '
  I2cStatus.5 = (I2C1CON And 0x1F)                                   '
End Sub                                                             '

'==================================================================================================
Sub Procedure I2Cinit()                                             'Init I2C1 peripheral (400kHz)
'--------------------------------------------------------------------------------------------------
  I2C1BRG   = 0x0025                                                '(Fcy/scl)-(Fcy/10000000) -1
  I2CEN_Bit = 1                                                     'Enable I2C1
  Delay_ms(10)                                                      '
  I2C1STAT = 0                                                      '
End Sub                                                             '

'==================================================================================================
Sub Procedure I2CStart()                                            'Start bus
'--------------------------------------------------------------------------------------------------
  Dim lCnt As Word                                                  '
  I2cStatus = 0x80
  I2CIdle()                                                         'Wait for bus idle
  SEN_Bit = 1                                                       'Set start bit
  For lCnt = 0 To I2CTimeout                                        'Wait for bit to clear
    If (SEN_Bit = 0) Then Break End If                              '
  Next lCnt                                                         '
  I2cStatus.0 = SEN_bit                                             'Errorstatus
End Sub                                                             '

'==================================================================================================
Sub Procedure I2CRestart()                                          'Restart bus
'--------------------------------------------------------------------------------------------------
  Dim lCnt As Word                                                  '
  I2CIdle()                                                         'Wait for bus idle
  RSEN_Bit = 1                                                      'Set repeated start bit
  For lCnt = 0 To I2CTimeout                                        'Wait for bit to clear
    If (RSEN_Bit = 0) Then Break End If                             '
  Next lCnt                                                         '
  I2cStatus.1 = RSEN_bit                                            'Errorstatus
End Sub                                                             '

'==================================================================================================
Sub Procedure I2CStop()                                             'Stop bus
'--------------------------------------------------------------------------------------------------
  Dim lCnt As Word                                                  '
  I2CIdle()                                                         'Wait for bus idle
  PEN_Bit = 1                                                       'Set stop bit
  For lCnt = 0 To I2CTimeout                                        'Wait for bit to clear
    If (PEN_Bit = 0) Then Break End If                              '
  Next lCnt                                                         '
  I2cStatus.2 = PEN_Bit                                             'Errorstatus
  I2cStatus.7 = 0                                                   '
End Sub                                                             '

'==================================================================================================
Sub Procedure I2CWrite(Dim pDat As Byte)                            'Write byte pDat to bus
'--------------------------------------------------------------------------------------------------
  Dim lCnt As Word                                                  '
  I2CIdle()                                                         'Wait for bus idle
  MI2C1IF_Bit = 0                                                   'Clear interruptflag
  I2C1TRN = pDat                                                    'Data to send into buffer
  For lCnt = 0 To I2CTimeout                                        'Wait for interruptflag to
    If MI2C1IF_bit Then Break End If                                'get set
  Next lCnt                                                         '
  I2cStatus.3 = MI2C1IF_Bit Xor 1                                   'Errorstatus
End Sub

'==================================================================================================
Sub Function I2CRead(Dim pAck As Byte) As Byte                      'Read byte of bus(1=Nack/0=Ack)
'--------------------------------------------------------------------------------------------------
  Dim lCnt As Word                                                  '
  I2CIdle()                                                         'Wait for bus idle
  MI2C1IF_Bit = 0                                                   'Clear interruptflag
  RCEN_Bit = 1                                                      '
  For lCnt = 0 To I2cTimeout                                        'Wait for buffer
    If RBF_Bit Then Break End If                                    '
  Next lCnt                                                         '
  I2cStatus.6 = RBF_Bit Xor 1                                       'Errorstatus
  Result = I2C1RCV                                                  'Get received byte
  ACKDT_Bit = pAck                                                  'Send Ack/Nack
  ACKEN_Bit = 1                                                     '
  For lCnt = 0 To I2CTimeout                                        '
    If MI2C1IF_Bit Then Break End If                                '
  Next lCnt                                                         '
  I2cStatus.4 = MI2C1IF_Bit Xor 1                                   'Errorstatus
End Sub                                                             '
end.

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

Dat is een leuke om eens uit te proberen eerdaags...