Zur Benutzung der Schaltung:
Mit dem Jumper für Mode am SAA3049 stellt man den Typ des Fernbedienungssignals ein: Mode = GND bedeutet RC5-Code, Mode = +5V bedeutet RECS80-Code. Mit den Jumpern A4 .. A0 stellt man die Geräteadresse beim SAA3049 ein (nicht verwechseln mit den Jumpern für die I2C-Subadresse beim PCF8574): Beim RC5-Code bedeutet z.B: das Gerät mit Adresse 00000 = A4 = A3 = A2 = A1 = A0 = GND die Fernsehfernbedienung. Wird nun ein gültiges Kommando empfangen, so wird am CRI-Pin ein Lo-Puls erzeugt, mit dem LED2 aufblitzt und die einen Interrupt am IRQ-Pin der C-Control auslösen kann. So muss die C-Control nicht ständig prüfen, ob neue Daten angekommen sind (so wie z.B. beim Testen auf neue Daten an der seriellen Schnittstelle), sondern wird automatisch darüber informiert. Die C-Control könnte in so einem Moment das gescannte Infrarot-Kommando direkt auslesen, indem man A .. F direkt an PortPins klemmt. Wer aber Ports und Kabel sparen will, hängt A .. F, sowie das Toggle-Bit T (im Datenbatt heisst es T0) an den Parallel-Eingang des PCF8574, und kann diesen Wert dann via I2C-Bus in die C-Control transportieren. Das spart Ports und Kabel (aber kein Geld). Die Subadresse des PCF8574 muss natürlich noch eingestellt werden. Das Demolisting geht von A2 = A1 = A0 = GND aus.
Zum ToggleBit:
Das ToggleBit T wechselt mit jedem Tastenniederdruck seinen Wert. So kann man zwischen 2 mal gedrückter Taste und einer dauernd gedrückten Taste unterscheiden. Das Demolisting soll das mit einem piepsen für jeden ?echten? neuen Tastenanschlag illustrieren.
Zum I2C-Bus-Assembler:
Der Assemblercode ist platzoptimiert, da die C-Control nur sehr wenig Platz für Maschinenprogramme bietet. Darum benützt es die betriebssystem-eigenen I2C-Routinen und damit natürlich auch den vorhandenen I2C-Bus. Eine Warnung dazu vorweg: Versucht man über diese Routinen mit einem nicht ansprechbaren Busteilnehmer zu kommunizieren (z.B. falsche Subadresse, kein Strom, abgeklemmt), so kehren die Routinen nicht mehr zurück und das Programm hängt sich auf. Zwar wird im Listing nur bei einem Interrupt der I2C-Bus angesprochen, und wenn die Schaltung nicht angeschlossen ist, kann auch kein Interrupt auftreten, also sollte nichts passieren können. Wer aber aufpassen muss, dass sein Programm nicht abstürzt, weil sonst der Keller überflutet wird o.ä. sollte also auf ?hot-plugging? verzchten.
Generelles zum I2C-Bus:
Es ist unter Basic nicht möglich, den I2C-Bus anzusprechen, da der Basic-Code in einem EEPROM 24C65 ausgelagert ist, das am I2C-Bus hängt. Man müsste also den ast, auf dem man sitzt absägen ...
Unter assembler sieht das anders aus: Der syscode wird in einem prozessoreigenen EEPROM gespeichert. Um nun ein I2C-Gerät anzusprechen, muss zunächst das EEPROM abgemeldet werden:
; Disconect 24C65...
jsr I2C_ReadLast
Dann kann man sein neues Gerät ansprechen und Daten austauschen:
;Connect PCF8574...
ldx #PCF8574_ReadAdr
jsr I2C_Start
jsr I2C_Read
coma
sta PCF8574_ResBuf
und danach auch wieder abmelden:
; Disconnect PCF8574...
jsr I2C_ReadLast
danach muss man den EEPROM wieder ankoppeln, und ihm die aktuelle Leseposition (AdrCntHi und AdrCntLo), die man sich als eine Art ?Instruction-Pointer? im Basic-Interpreter vorstellen kann, mitteilen:
; Reconnect 24C65...
ldx #_24C65_WriteAdr
jsr I2C_Start
ldx AdrCntHi
jsr I2C_Write
ldx AdrCntLo
jsr I2C_Write
ldx #_24C65_ReadAdr
jsr I2C_Start
rts
Die Listings:
Der assembler-Source sieht so aus:
; Header.asm
; 14.02.2000, common Header-File for all Assembler-Files
; RAM-Registers
PortA.equ $00; Port A Data
SDA.equ 0; PortA.I2C-Data
SCL.equ 1; PortA.I2C-Clock
R_LED.equ 2; PortA.RedLED
Y_LED.equ 3; PortA.YellowLED
G_LED.equ 4; PortA.GreenLED
START.equ 5; PortA.StartButton
RTS.equ 6; PortA.RTS
CTS.equ 7; PortA.CTS
PortB.equ $01; Port B Data
PortC.equ $02; Port C Data
PortD.equ $03; Port D Input Data
PAdir.equ $04; Port A Direction
PBdir.equ $05; Port B Direction
PCdir.equ $06; Port C Direction
EEPROM_ECLK.equ $07; EEPROM/ECLCK Control
ADC_Data.equ $08; AD-Converter Data
ADC_Ctrl.equ $09; AD-Converter Control
PLM_A.equ $0A; Pulse Length Modulator A
PLM_B.equ $0B; Pulse Length Modulator B
Misc.equ $0C; Miscellanous
WDOG.equ 0; MISC.Watchdog-Enable-Flag
SM.equ 1; MISC.SlowMode-Flag
SFB.equ 2; MISC.?-Flag
SFA.equ 3; MISC.?-Flag
INTE.equ 4; MISC.IRQ-Enable-Flag
INTN.equ 5; MISC.IRQ-Negative-Edge-Flag
INTP.equ 6; MISC.IRQ-Positive-Edge-Flag
POR.equ 7; MISC.Power-On-Reset-Flag
BRate.equ $0D; RS232 SCR-Baudrate
SCCR1.equ $0E; RS232 Control 1
SCCR2.equ $0F; RS232 Control 2
SCSR.equ $10; RS232 Status
SCDat.equ $11; RS232 Data
TCR.equ $12; Timer Control
TSR.equ $13; Timer Status
Cap1Hi.equ $14; Input Capture 1 Hi
Cap1Lo.equ $15; Input Capture 1 Lo
Cmp1Hi.equ $16; Output Compare 1 Hi
Cmp1Lo.equ $17; Output Compare 1 Lo
Cnt1Hi.equ $18; Counter 1 Hi
Cnt1Lo.equ $19; Counter 1 Lo
Cnt2Hi.equ $1A; Counter 2 Hi
Cnt2Lo.equ $1B; Counter 2 Lo
Cap2Hi.equ $1C; Input Capture 2 Hi
Cap2Lo.equ $1D; Input Capture 2 Lo
Cmp2Hi.equ $1E; Output Compare 2 Hi
Cmp2Lo.equ $1F; Output Compare 2 Lo
IrqPtr .equ $50; User-Interrupt-Routinen-Pointer
CapPtr.equ $51
CmpPtr .equ $52
OflPtr .equ $53
AdrCntHi.equ $66; ReadPointer LoByte in 24C65
AdrCntLo.equ $67; ReadPointer HiByte in 24C65
rxBuf .equ $5B; RxD-RingPuffer (8 Byte lang)
rxCount .equ $63; Pufferlänge
rxGetIndex .equ $64; AusgabeIndex
rxPutIndex .equ $65; EinfügeIndex
SysFlgs.equ $7B; Betriebssystem-Flags
SYNC.equ 0; SysFlgs.Synchronized-Flag
ACTIVE.equ 1; SysFlgs.Active-Flag
RUN.equ 2; SysFlgs.Run-Flag
DEBUG.equ 3; SysFlgs.Debug-Flag
DCFVALID.equ 4; SysFlgs.DcfValid-Flag
SLOWMODE.equ 5; SysFlgs.SlowMode-Flag
UINT.equ 6; SysFlgs.UserInterrupt-Flag
RTSCTS.equ 7; SysFlgs.RtsCts-Flag
_A.equ $91 ; RechenStack
_A_lo.equ $92
_B.equ $93
_B_lo.equ $94
_C.equ $95
_C_lo.equ $96
_D.equ $97
_D_lo.equ $98
_E.equ $99
_E_lo.equ $9A
_F.equ $9B
_F_lo.equ $9C
_G.equ $9D
_G_lo.equ $9E
VAR01.equ $A1 ; Userbytes
VAR02.equ $A2
VAR03.equ $A3
VAR04.equ $A4
VAR05.equ $A5
VAR06.equ $A6
VAR07.equ $A7
VAR08.equ $A8
VAR09.equ $A9
VAR10.equ $AA
VAR11.equ $AB
VAR12.equ $AC
VAR13.equ $AD
VAR14.equ $AE
VAR15.equ $AF
VAR16.equ $B0
VAR17.equ $B1
VAR18.equ $B2
VAR19.equ $B3
VAR20.equ $B4
VAR21.equ $B5
VAR22.equ $B6
VAR23.equ $B7
VAR24.equ $B8
RS232_WriteByte.equ $0C77; SubR. Senden des xRegs
RS232_EmptyRX.equ $0CE0; SubR. Löschen der RxQueue
Store_Bit.equ $1273; SubR. Bit nr xReg of UserBytes := _A
I2C_Init.equ $0811; SubR. I2C Inizialize
I2C_Start.equ $083C; SubR. I2C Start & Write, Addr in xReg
I2C_Read.equ $086F; SubR. I2C Read, Data in aReg
I2C_ReadLast.equ $08BB; SubR. I2C Read & Stop, no Result
I2C_Write.equ $0846; SubR. I2C Write, Data in xReg
I2C_Stop.equ $08E5; SubR. I2C z.B. nach I2C_Write
LCD_Port.equ PortC
LCD_Dir.equ PCdir
LCD_RW.equ 4
LCD_RS.equ 5
LCD_E.equ 6
_24C65_ReadAdr.equ $A1; I2C-LeseAdresse des 24C56
_24C65_WriteAdr.equ $A0; I2C-SchreibAdresse des 24C56
;End Header.asm
; _asm_FileVersion: $0008
; allgemeine LeseAddresse des PCF8574= 0.1.0.0.A2.A1.A0.1 (binär)
; allgemeine SchreibAddresse des PCF8574= 0.1.0.0.A2.A1.A0.0 (binär)
PCF8574_ReadAdr.equ $41; für A2, A1, A0 auf Lo = GND
PCF8574_WriteAdr.equ $40; für A2, A1, A0 auf Lo = GND
PCF8574_ResBuf.equ VAR23
.org $101
; Initialisation...
; ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((
Irq_SetPinSensitivity:
bclr INTP,Misc; disable IRQ on pos. Edge
bset INTN,Misc; enable IRQ on neg. Edge
rts
; ))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
; ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((
I2C_PCF8574_Read:
;Disconect 24C65...
jsr I2C_ReadLast
;Connect PCF8574...
ldx #PCF8574_ReadAdr
jsr I2C_Start
jsr I2C_Read
coma
sta PCF8574_ResBuf
;Disconnect PCF8574...
jsr I2C_ReadLast
;Reonnect 24C65...
ldx #_24C65_WriteAdr
jsr I2C_Start
ldx AdrCntHi
jsr I2C_Write
ldx AdrCntLo
jsr I2C_Write
ldx #_24C65_ReadAdr
jsr I2C_Start
rts
; ))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
.end
Assembliert und als syscode in Basic eingebunden sieht das ganze dann so aus:
' Entrypoints for asm_FileVersion $0008
define Irq_SetPinSensitivity&H0101
define I2C_PCF8574_Read&H0106
' _bas_FileVersion: $045E
define I2C_ResBufVAR23
define IrToggleFlagbit[183]
define IrLastTogglebit[190]
interrupt OnIrq
#Init
sys Irq_SetPinSensitivity
beep 300, 4, 0
#Loop
' hier irgendetwas tun
goto Loop
#OnIrq
sys I2C_PCF8574_Read
put I2C_ResBuf
if (IrLastToggle <> IrToggleFlag)then beep 200, 2, 0
IrLastToggle = IrToggleFlag
return interrupt
syscode
'Irq_SetPinSensitivity: Address &H0101
&H1D &H0C 'bclr INTP,Misc; disable IRQ on pos. Edge
&H1A &H0C 'bset INTN,Misc; enable IRQ on neg. Edge
&H81 'rts
'I2C_PCF8574_Read: Address &H0106
';Disconect 24C65...
&HCD &H08 &HBB 'jsr I2C_ReadLast
';Connect PCF8574...
&HAE &H41 'ldx #PCF8574_ReadAdr
&HCD &H08 &H3C 'jsr I2C_Start
&HCD &H08 &H6F 'jsr I2C_Read
&H43 'coma
&HB7 &HB7 'sta PCF8574_ResBuf
';Disconnect PCF8574...
&HCD &H08 &HBB 'jsr I2C_ReadLast
';Reonnect 24C65...
&HAE &HA0 'ldx #_24C65_WriteAdr
&HCD &H08 &H3C 'jsr I2C_Start
&HBE &H66 'ldx AdrCntHi
&HCD &H08 &H46 'jsr I2C_Write
&HBE &H67 'ldx AdrCntLo
&HCD &H08 &H46 'jsr I2C_Write
&HAE &HA1 'ldx #_24C65_ReadAdr
&HCD &H08 &H3C 'jsr I2C_Start
&H81 'rts
'.end
sysend
' 43 Byte(s) of SysCode in asm_FileVersion $0008
Ein paar Tippfehler möchte ich nicht völlig ausschliessen. Allerdings ist das ganze sowieso für Leute gedacht, die das hier nicht nur nachlöten und abkopieren, sonden verstehen und daher evtl. Fehler im Schaltplan oder Programm (mail an mich bitte!) selbstständig erkennen und korregieren können.
Trotz allem: Bei mir läuft das ganze brav zusammen mit einer DCF-Antenne, noch einigen anderen I2C-Slaves, Windgeschwindigkeitsmessung am Freq2-Pin, einem LC-Display und einem LED-Display, also einer schwer beschäftigten C-Control, die NICHT übertaktet ist.
Version 3.0
15.02.2000
Mario.Fischer@eikon.tum.de