6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Tue Nov 12, 2024 9:05 am

All times are UTC




Post new topic Reply to topic  [ 17 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Sun Dec 02, 2018 10:06 am 
Offline

Joined: Wed Sep 11, 2013 8:43 pm
Posts: 207
Location: The Netherlands
In my current project I’d like to use an extra interrupt output line of the SC26C92. I want to create an interrupt at say 50 or 60Hz or any arbitrary frequency. I think the best approach is to use pin OP3 with a 3k3 pull-up resistor?


In fact I’m already using:
1. Channel A with a UART to USB converter to my PC at 152000 baud.
2. Channel B I’m using as a TTL MIDI output wit 500kHz fed into pin IP5.

Can I use the timer/counter with the extra interrupt output line (OP3) independently and at the same time with those two functions above?

I have no idea how to set up the timer/counter and create an interrupt at output line (OP3).

This is my code so far:

Code:
;
; Registers
;
duart_base   =   $bf00            ;$bf00-$bf0f SC26C92 DUART
mr0a         =   duart_base + $0      ; R/W : Mode Register 0A
mr1a         =   duart_base + $0      ; R/W : Mode Register 1A
mr2a         =   duart_base + $0      ; R/W : Mode Register 2A
sra         =   duart_base + $1      ; READ: Status Register A
csra         =   duart_base + $1      ;WRITE: Clock Select Register A
cra         =   duart_base + $2      ;WRITE: Command Register A !!!DONT READ!!!
rxfifoa      =   duart_base + $3      ; READ: Rx Holding Register A
txfifoa      =   duart_base + $3      ;WRITE: Tx Holding Register A
ipcr         =   duart_base + $4      ; READ: Input Port Change Register
acr         =   duart_base + $4      ;WRITE: Aux. Control Register
isr         =   duart_base + $5      ; READ: Interrupt Status Register
imr         =   duart_base + $5      ;WRITE: Interrupt Mask Register
ctu         =   duart_base + $6      ; READ: Counter/Timer Upper Value
ctpu         =   duart_base + $6      ;WRITE: C/T Upper Preset Value
ctl         =   duart_base + $7      ; READ: Counter/Timer Lower Value
ctpl         =   duart_base + $7      ;WRITE: C/T Lower Preset Value
mr0b      =   duart_base + $8      ; R/W : Mode Register 0B
mr1b      =   duart_base + $8      ; R/W : Mode Register 1B
mr2b      =   duart_base + $8      ; R/W : Mode Register 2B
srb         =   duart_base + $9      ; READ: Status Register B
csrb         =   duart_base + $9      ;WRITE: Clock Select Register B
crb         =   duart_base + $a      ;WRITE: Command Register B !!!DONT READ!!!
rxfifob      =   duart_base + $b      ; READ: Rx Holding Register B
txfifob      =   duart_base + $b      ;WRITE: Tx Holding Register B
opcr         =   duart_base + $d      ;WRITE: Output Port Conf. Register
sop12      =   duart_base + $e      ;WRITE: Set Output Port Bits Command
rop12      =   duart_base + $f         ;WRITE: Reset Output Port Bits Command
acia_adat      =   txfifoa
;
;
; Routines
;
;
; serial port initialization
;
acia_a_init
acia_a_portset
         lda   #%10110000      ;set MR pointer to 0
         sta   cra
         lda   #%00000100      ;MR0 Extended mode II
         sta   mr0a
         lda   #%00010011      ;No parity, 8 bits per char.
         sta   mr1a
         lda   #%00000111      ;Stop bit length 1.000
         sta   mr2a
         lda   #%01100110      ;ExII receiver clock select 115200 transmitter clock select 115200
         sta   csra
         lda   #%00000101      ;enable Tx enable Rx
         sta   cra

acia_b_init
         lda   #%10110000      ;set MR pointer to 0
         sta   crb
         lda   #%00000000      ;MR0 Normal mode
         sta   mr0b
         lda   #%00010011      ;No parity, 8 bits per char.
         sta   mr1b
         lda   #%00001111      ;Stop bit length 2.000
         sta   mr2b
         lda   #%11101110      ;receiver clock select IP5-16X transmitter clock select IP5-16X
         sta   csrb

         lda   #%00000101      ;enable Tx enable Rx
         sta   crb

         rts


;
; input char from acia (waiting)
;
acia_a_input
         lda   sra         ;get status reg A
         and   #%00000001      ;is receiver full?
         beq   acia_a_input      ;no char to get
         lda   rxfifoa         ;get chr
         rts            ;done
;
; scan char from acia (non-waiting)
;
acia_a_scan
         clc
         lda   sra         ;get status reg A
         and   #%00000001      ;is receiver full?
         beq   acia_a_scan2      ;no, proceed without
         lda   rxfifoa         ;get chr
         sec
acia_a_scan2      rts            ;done
;
; output to output port
;
acia_a_output
         pha            ;save acc
acia_a_out1      lda   sra         ;get status reg A
         and   #%00000100      ;is transmit buffer empty?
         beq   acia_a_out1      ;no
         pla            ;get chr
         sta   txfifoa         ;put character to port
         rts            ;done
;
; midi output
;
midiout
         pha            ;save acc
-         lda   srb         ;get status reg A
         and   #%00000100      ;is transmit buffer empty?
         beq   -         ;no
         pla            ;get chr
         sta   txfifob         ;put character to port
         rts            ;done
;
; end
;


_________________
Marco


Top
 Profile  
Reply with quote  
PostPosted: Sun Dec 02, 2018 7:51 pm 
Offline
User avatar

Joined: Tue Mar 05, 2013 4:31 am
Posts: 1385
While I would consider BDD the guru for the NXP DUARTS, I am using the SCC2691 as a serial console with interrupt-driven transmit/receive and also for the counter/timer as a jiffy clock (10ms) and received break also handled in the ISR.

Looking at your init code, you are setting up both UARTs, but not setting up the counter/timer. You should add code to your init routine to set the counter high and low values, and the OPCR for OP3 (bits 3-2 as 0-1) as output from the counter/timer and enable (the timer) via the 3-bits in the ACR (bits 6-4 as 110). With the SCC2691, the ISR needs to send a counter stop command to the chip, which doesn't actually stop the counter, but resets the count again from the counter high/low preload values.

The counter values work from the baud clock frequency. In my configuration, the counter values are set as: $4800 (=-$48, L=$00), or 18432 decimal. This results in an interrupt every 10ms with the standard crystal frequency of 3.6864MHz. It works out to a count of 200, but it's double the 100 that's generated. Note that you also need to handle the timer interrupt in your service routine.

Hope this helps.

_________________
Regards, KM
https://github.com/floobydust


Top
 Profile  
Reply with quote  
PostPosted: Sun Dec 02, 2018 9:26 pm 
Offline

Joined: Wed Sep 11, 2013 8:43 pm
Posts: 207
Location: The Netherlands
I think this pushes me in the right direction. I setup the counter/timer for 50Hz (20ms) n = 36864. Then OP3 as counter/timer output, followed by enabling the counter/timer which produces a square wave at pin OP3.

Do I understand correctly that in timer mode (ACR (bits 6-4 as 110)) I don’t have to reset anything and I don’t need a pull-up resistor?

Code:
acia_ct_init
         lda   #>36864         ;set c/t for 50Hz or 20ms
         sta   ctpu
         lda   #<36864         ;deviser = 3.6864MHz / (2*50) = 36864
         sta   ctpl
         lda   opcr         ;set OP3 as c/t output
         and   #%11110111      ;clear bit 3
         ora   #%00000100      ;set bit 2
         sta   opcr
         lda   acr         ;enable timer (square wave) X1 mode
         and   #%11101111      ;clear bit 4
         ora   #%01100000      ;set bits 6 & 5
         sta   acr
         rts


I’ll test tomorrow.

_________________
Marco


Top
 Profile  
Reply with quote  
PostPosted: Sun Dec 02, 2018 9:39 pm 
Offline
User avatar

Joined: Tue Mar 05, 2013 4:31 am
Posts: 1385
According to the datasheet, OP3 acts like an open drain output when in this mode, so you should require a pull-up resistor.

Also, are you running the DUART in interrupt mode for any of the configuration? Again, I'm using the older 2691 single channel UART which doesn't have any extra IP or OP pins. I'm also using interrupt driven code for all of the UARTs functions.

_________________
Regards, KM
https://github.com/floobydust


Top
 Profile  
Reply with quote  
PostPosted: Sun Dec 02, 2018 10:10 pm 
Offline

Joined: Wed Sep 11, 2013 8:43 pm
Posts: 207
Location: The Netherlands
No, currently I’m not using any interrupts for the regular INTRN line. For now I want to use OP3 as a separate interrupt line. Later I want to be able to use both lines independently.

I didn’t read the 2691 datasheet, but according to the SC26C92 datasheet it states that OP3 produces only a square wave when in timer mode. Which I’ll test first, just as proof of concept.

I probably need to use the counter mode (ACR (bits 6-4 as 011)). Then I have to use a pull-up resistor *and* I’d have to restart the counter by a start counter command by reading register $E in my ISR.

_________________
Marco


Top
 Profile  
Reply with quote  
PostPosted: Mon Dec 03, 2018 12:04 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8482
Location: Midwestern USA
lordbubsy wrote:
In my current project I’d like to use an extra interrupt output line of the SC26C92. I want to create an interrupt at say 50 or 60Hz or any arbitrary frequency. I think the best approach is to use pin OP3 with a 3k3 pull-up resistor?


In fact I’m already using:
1. Channel A with a UART to USB converter to my PC at 152000 baud.
2. Channel B I’m using as a TTL MIDI output wit 500kHz fed into pin IP5.

Can I use the timer/counter with the extra interrupt output line (OP3) independently and at the same time with those two functions above?

I have no idea how to set up the timer/counter and create an interrupt at output line (OP3).

In timer mode and assuming you set up the C/T to underflow (reach terminal count, which is $0000) at 100 Hz (my recommendation), you will see a continuous 100 Hz square wave at OP3 if bits 3-2 of OPCR are set to %01. The C/T must have been started, of course. Once started, the C/T will endlessly count from the prescalar value ($4800 for 100 Hz) to terminal count. That is, upon reaching terminal count, the prescalar value will immediately be reloaded into the C/T and the process will repeat. If the C/T interrupt has been enabled (bit 3 of IMR = %1), the 'C92 will negate its ITRN line when the C/T reaches terminal count, as well as assert bit 3 in ISR. Issuing a "stop C/T" command to the 'C92 will clear the pending IRQ but will have no effect on the C/T itself. OP3 will also not be affected—there will be an uninterrupted 100 Hz square wave at OP3 with a ~50 percent duty cycle at all times.

If you configure the C/T to operate in counter mode, load its registers with the desired value ($4800 for a 10 millisecond period) and start it, it will run down to terminal count, set ISR bit 3 (C/T interrupt), roll over to $FFFF and again count down. If bits 3-2 of OPCR are set to %01 and OP3 is pulled up to Vcc, OP3 will go high when the C/T is started and will remain high until the C/T reaches terminal count. At that time, OP3 will be driven low and will remain low until the C/T is stopped. Once stopped, the C/T will remain idle until again started.

I question the value of doing what you want to do. When the C/T is running in timer mode, you don't have to do anything when it interrupts except issue the "stop C/T" command to clear the IRQ (where "stop C/T" means perform a read on register 15). The 'C92 will tell you the C/T was responsible for the IRQ by merely reading the ISR and masking for bit 3. If you are running in counter mode just to get the terminal count status at OP3 you must not only stop the C/T to clear the interrupt, you have to restart it. The implication of doing so is if you are using the C/T as a time base, you will experience a slow drift due to the small but inexorable amount of "lost time" that will occur between when the terminal count interrupt occurs and when the interrupt service routine gets around to clearing the interrupt with "stop C/T" and then restarting the C/T.

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Top
 Profile  
Reply with quote  
PostPosted: Mon Dec 03, 2018 3:17 pm 
Offline

Joined: Wed Sep 11, 2013 8:43 pm
Posts: 207
Location: The Netherlands
I got it working! To summarize:
I’m better off using the regular INTRN line, there is no need for using OP3. Timer mode is what I need, not counter mode.

My goal was to play SID music through a 50Hz or 60Hz interrupt routine like on the C64 (usually). In this case I needed a 100Hz interrupt routine. Despite this is the only interrupt I’m using, I’m checking the DUART’s ISR if the interrupt occurred.

Interrupt Service Routine:
Code:
interrupt
         pha
         phx
         phy
         lda   isr
         and   #%00001000      ;check if interrupt is caused by C/T
         beq   endirq         ;no? exit irq
         jsr   playmusic      ;play music
         lda   rop12         ;clear counter ready interrupt status bit ($bf0f)
endirq
         ply
         plx
         pla
         rti


Counter / Timer Interrupt Setup Routine
Code:
acia_ct_init
         sei            ;disable interrupts
         lda   #<interrupt      ;set interrupt vector
         sta   $fffe         ;RAM on MARC-4
         lda   #>interrupt
         sta   $ffff
         lda   #>$4800         ;set c/t for 100Hz or 10ms
         sta   ctpu
         lda   #<$4800         ;deviser = 3.6864MHz / (2*100) = 18432
         sta   ctpl

         lda   acr         ;enable timer (square wave) X1 mode
         and   #%11101111      ;clear bit 4
         ora   #%01100000      ;set bits 6 & 5
         sta   acr

         lda   #%00001000      ;enable counter ready interrupt
         sta   imr

         lda   sop12         ;start timer by reading register 14

         cli            ;enable interrupts
         rts

_________________
Marco


Top
 Profile  
Reply with quote  
PostPosted: Mon Dec 03, 2018 5:07 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8482
Location: Midwestern USA
Are you running the 65C816 in emulation mode while all this is going on?

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Top
 Profile  
Reply with quote  
PostPosted: Mon Dec 03, 2018 6:15 pm 
Offline

Joined: Wed Sep 11, 2013 8:43 pm
Posts: 207
Location: The Netherlands
Yes, for now I do. I bought a physical copy of "Programming the 65816..." by Eyes & Lichty as a Christmas gift to myself. :) But I’m already studying in the pdf file.
Why are you asking?

_________________
Marco


Top
 Profile  
Reply with quote  
PostPosted: Mon Dec 03, 2018 9:22 pm 
Offline

Joined: Sat Dec 13, 2003 3:37 pm
Posts: 1004
lordbubsy wrote:
Yes, for now I do. I bought a physical copy of "Programming the 65816..." by Eyes & Lichty as a Christmas gift to myself. :) But I’m already studying in the pdf file.
Why are you asking?


I'm guessing it's important since you don't specify what state the processor should be in within the interrupt routine, and it's likely very important to be specific about it unless you know what the state of the processor is in any time that the interrupt may fire.


Top
 Profile  
Reply with quote  
PostPosted: Mon Dec 03, 2018 10:29 pm 
Offline

Joined: Wed Sep 11, 2013 8:43 pm
Posts: 207
Location: The Netherlands
Ah, I see! For now I know what the state of the processor is because the program runs always after a reset and I’m not using native mode at all yet. But indeed, specifying the CPU state seems imminent when I’m going to use native mode, and I’m planning to in the near future.

Currently I’m testing several functions and devices in my computer in emulation mode only, because that’s complex enough as it is. However I’m trying to get familiar with some of the CMOS and ‘816 commands.

_________________
Marco


Top
 Profile  
Reply with quote  
PostPosted: Mon Dec 03, 2018 10:33 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8482
Location: Midwestern USA
lordbubsy wrote:
Yes, for now I do...Why are you asking?

As whartung noted, you aren't saving the full processor state in your ISR's preamble. Failure to do so in native mode could get you into a peck of trouble. :D

The interrupt service routine (ISR) preamble I use in my POC firmware is:

Code:
;iirq: INTERRUPT REQUEST SERVICE ROUTINE
;
iirq     longr                 ;16 bit registers
         phb                   ;preserve machine state
         phd
         pha
         phx
         phy

The postamble is:

Code:
;crti: COMMON INTERRUPT RETURN
;
crti     longr
         ply
         plx
         pla
         pld
         plb
         rti

On a general-purpose computer, you can not make assumptions about what is going on in the foreground, as any hardware interrupt can come at any time. I save both DB and DP because my firmware's kernel expects DB to point at bank $00 and DP to point at the reserved RAM area that starts at $00D800—the kernel has its own "private" direct page. I don't know where either of these registers will be pointing when an interrupt hits, so I push them and then later in the early parts of the ISR, load them with appropriate values:

Code:
;   IRQ indirect return point...
;
iirqa    longa                 ;16 bit accumulator
         lda #kerneldp
         tcd                   ;set kernel's DP
         shortr                ;8 bit registers
         lda #kerneldb
         pha
         plb                   ;set kernel's DB

Quote:
Code:
         lda   acr             ;enable timer (square wave) X1 mode
         and   #%11101111      ;clear bit 4
         ora   #%01100000      ;set bits 6 & 5
         sta   acr

In setting up ACR, I wouldn't worry about jumping through hoops like the above, as what you are doing is initializing the UART. My approach would be:

Code:
         lda #%?1100000
         sta acr               ;timer mode w/X1 clock

The ? bit would depend on which data rate table you are using.

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Top
 Profile  
Reply with quote  
PostPosted: Tue Dec 04, 2018 1:05 am 
Offline

Joined: Sat Dec 13, 2003 3:37 pm
Posts: 1004
BigDumbDinosaur wrote:
The interrupt service routine (ISR) preamble I use in my POC firmware is:

Code:
;iirq: INTERRUPT REQUEST SERVICE ROUTINE
;
iirq     longr                 ;16 bit registers
         phb                   ;preserve machine state
         phd
         pha
         phx
         phy

The postamble is:

Code:
;crti: COMMON INTERRUPT RETURN
;
crti     longr
         ply
         plx
         pla
         pld
         plb
         rti


You (not BDD, the other You :) ) should pay attention to this ISR. Notably, see how he sets up using 'longr', which for his assembler means 16 bit accumulator and 16 bit index registers. This routine is "universal", regardless of what mode your code is in. So, say, your code in using 8 bit index registers, then when BDDs code pushes X and Y, it will push 16 bit versions of them (with the top 8 bits = 0), and, later, pull them as well. When the interrupt returns, it resets the processor state (and the 8/16 bit flags), so you index registers area properly preserved.


Top
 Profile  
Reply with quote  
PostPosted: Tue Dec 04, 2018 10:14 am 
Offline

Joined: Wed Sep 11, 2013 8:43 pm
Posts: 207
Location: The Netherlands
Thanks for the heads up on the ‘816’s native mode. In time I’d like to start a new topic on programming the ‘816 in native mode and using 8 or 16 bit registers. It seems a bit cumbersome to switch between modes, but I guess that’s the price of having 6502 backward compatibility, 16 bits and a lot of linear memory space. But I have to study *the* book a little more before I start to ask questions that are too obvious.

_________________
Marco


Top
 Profile  
Reply with quote  
PostPosted: Tue Dec 04, 2018 6:26 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8482
Location: Midwestern USA
lordbubsy wrote:
In time I’d like to start a new topic on programming the ‘816 in native mode and using 8 or 16 bit registers.

See here for such a topic.

Quote:
It seems a bit cumbersome to switch between modes, but I guess that’s the price of having 6502 backward compatibility, 16 bits and a lot of linear memory space.

After a while, you'll get the "rhythm" of switching register sizes (not modes) and it will become second nature to you.

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 17 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 20 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: