Interrupt on pin change
Interrupt on pin change
Currently I am working on a design of a retro digital clock, based on WDC6502. It already works, but I want to add an additional module with SPO256 voice synthesizer chip and some circuitry providing DCF77 radio time synchronization, which requires timer for measuring pulse length and pin change interrupt its edges. Timer is easy - i already used MC6840 in my earlier designs. But what about pin change interrupt?
Re: Interrupt on pin change
I believe the 6522 VIA and/or 6521 PIA can be set up for this.
Re: Interrupt on pin change
Atlantis wrote:
Timer is easy - i already used MC6840 in my earlier designs. But what about pin change interrupt?
Re: Interrupt on pin change
Crossed in the post... my idea is more homebrew! It might be that with the VIA the ISR would have to set the sense of the next expected edge? Or are there two inputs one could use, set one up for positive edge and the other for negative.
This was my idea, anyhow:
If the pulse length is much longer than the clock, you can use the clock and a flop and an XOR to produce a pulse on each edge. (But, you need to be sure the clock is slow enough that the pulse is long enough to be sure to take the interrupt... or you could add in a set-reset latch, and use an output port to clear it from the ISR.)
This was my idea, anyhow:
If the pulse length is much longer than the clock, you can use the clock and a flop and an XOR to produce a pulse on each edge. (But, you need to be sure the clock is slow enough that the pulse is long enough to be sure to take the interrupt... or you could add in a set-reset latch, and use an output port to clear it from the ISR.)
Re: Interrupt on pin change
BigEd wrote:
If the pulse length is much longer than the clock, [...]
Re: Interrupt on pin change
Yep, sounds like a good application for a 6522 VIA. I'll assume that you have a clean, properly conditioned signal coming in (getting that right is mostly an analogue-domain problem) and that you only need to determine the time interval between rising and falling edges to a precision of 25ms.
The VIA has two 16-bit timers, and you can set one of them to count a specific number of CPU (Phi2) clock cycles repeatedly, and the other to count the number of cycles of the first timer. If you have a 1MHz Phi2 clock, 25ms is 25,000 cycles, which is comfortably within the range of the VIA's timers. The second timer will then increment by 40 every second, or by approximately 4, 8, 32, 36, 72, and 76 for each of the periods listed above. You can distinguish these by examining only the low-order byte of the timer.
The CA1, CA2, CB1, CB2 lines can each be set up to trigger an interrupt on a rising or falling edge. I think it's perfectly sensible to listen first for the rising edge in order to find the leading end of the pulse, record the timer value when the interrupt fires, then set it to listen instead for the falling edge. When that fires, you simply subtract the first timer value from the new one, and find the nearest value from the above list.
The VIA has two 16-bit timers, and you can set one of them to count a specific number of CPU (Phi2) clock cycles repeatedly, and the other to count the number of cycles of the first timer. If you have a 1MHz Phi2 clock, 25ms is 25,000 cycles, which is comfortably within the range of the VIA's timers. The second timer will then increment by 40 every second, or by approximately 4, 8, 32, 36, 72, and 76 for each of the periods listed above. You can distinguish these by examining only the low-order byte of the timer.
The CA1, CA2, CB1, CB2 lines can each be set up to trigger an interrupt on a rising or falling edge. I think it's perfectly sensible to listen first for the rising edge in order to find the leading end of the pulse, record the timer value when the interrupt fires, then set it to listen instead for the falling edge. When that fires, you simply subtract the first timer value from the new one, and find the nearest value from the above list.
Re: Interrupt on pin change
Chromatix wrote:
The VIA has two 16-bit timers, and you can set one of them to count a specific number of CPU (Phi2) clock cycles repeatedly, and the other to count the number of cycles of the first timer. If you have a 1MHz Phi2 clock, 25ms is 25,000 cycles, which is comfortably within the range of the VIA's timers. The second timer will then increment by 40 every second, or by approximately 4, 8, 32, 36, 72, and 76 for each of the periods listed above. You can distinguish these by examining only the low-order byte of the timer.
Originally I considered building edge detecting circuit with TTL/HCT chips. But now I consider using GAL chip. I could implement two od those detectors for each edge and store result in SR latches with tri-state outputs (connected to the data bus). Latch outputs would have been activated by correct sequence of address nad RD signals. Plus an additional output to drive OC INT buffer and input to reset latches. This could be achievable by GAL20V8, maybe even together with simple address decoder in single chip.
EDIT: I just realized that MC6840 has pulse width compare mode. It raises an interrupt if pulse at GATE input is shorter or longer than value in register. With slow enough clock signal it may be useful...
Re: Interrupt on pin change
For the avoidance of doubt, the W65C22S and W65C22N are still in production, so it's easy to get more of them. The MC6840 is not.
But I can see how the MC6840 can be used for this purpose. Here you need to physically connect the output of one timer (in free-run mode, generating the 25ms reference pulses) to the input of another (in pulse width comparison mode). In interrupt-on-shorter-pulse mode, with the second timer set to a high value on reset, the counter will be reset and start counting on the rising edge of the pulse, and will stop counting and generate an interrupt on the falling edge; you can then read the precise pulse width out of the timer. That is exactly what you need for this application.
But I can see how the MC6840 can be used for this purpose. Here you need to physically connect the output of one timer (in free-run mode, generating the 25ms reference pulses) to the input of another (in pulse width comparison mode). In interrupt-on-shorter-pulse mode, with the second timer set to a high value on reset, the counter will be reset and start counting on the rising edge of the pulse, and will stop counting and generate an interrupt on the falling edge; you can then read the precise pulse width out of the timer. That is exactly what you need for this application.
Re: Interrupt on pin change
Chromatix wrote:
For the avoidance of doubt, the W65C22S and W65C22N are still in production, so it's easy to get more of them. The MC6840 is not.
Quote:
But I can see how the MC6840 can be used for this purpose. Here you need to physically connect the output of one timer (in free-run mode, generating the 25ms reference pulses) to the input of another (in pulse width comparison mode).
Re: Interrupt on pin change
Looks like you need to (fairly reliably) detect the difference between a 100ms pulse and a 200ms pulse. I suppose a relatively uncalibrated delay could manage that.
https://en.wikipedia.org/wiki/DCF77#Amp ... modulation
(All the time field data is parity-protected, so that helps a bit to detect and ignore miscaptured data.
https://en.wikipedia.org/wiki/DCF77#Amp ... modulation
(All the time field data is parity-protected, so that helps a bit to detect and ignore miscaptured data.
Re: Interrupt on pin change
I just realized I do not need additional hardware timer. Single timer from MC6840 can be used for both: generating 25ms reference pulses for DCF77 pulse length measuring AND 25ms interrupts "systick" functionality. This leaves one free timer that can be used to generate tones.
Re: Interrupt on pin change
Ok, I just returned to this project. Can you tell me I didn't get anything wrong and I do understand the MC6840 work correctly?
The initialization code looks as follows:
There is an interrupt routine for TIMER1 (set in pulse width mode):
This is a dcf_analyze function:
Tell mi if I understand it correctly:
1. At the beginning, timer should be preloaded with 0xFFFF.
2. It stays still until GATE1 signal goes down.
3. It is decrementing initial timer value (0xFFFF) by 1 with every clock cycle on C1 input (which in my case is 25ms).
4. When GATE1 goes up, it starts counting and interrupt is executed.
5. I need to read the value stored in TIMER1 register, remembering to swap the bytes, because 6502 is little endian.
6. To get the actual number of "ticks" counted while GATE1 was low, I need to subtract this value from 0xFFFF.
7. Then I need to load TIMER1 with 0xFFFF once again, because otherwise it would start the next count from its last value. It doesn't reload itself automatically, Am I right?
Is there some mistake in my thinking?
The initialization code looks as follows:
Code: Select all
void __fastcall__ mc6840_init (void) {
MC6840_CON13 = TM_COUNTER_OUTPUT_ENABLE | TM_INTERUPT_DISABLE | TM_CONT_OP_MODE1 | TM_NORMAL_16BIT | TM_SYS_CLK | TMCR3_T3_CLK_NO_PRESCALER; //CON3 first by default after reset. TIMER3 generates sound. Output enable and sys clk
MC6840_CON2 = TM_COUNTER_OUTPUT_ENABLE | TM_INTERUPT_ENABLE | TM_CONT_OP_MODE1 | TM_NORMAL_16BIT | TM_SYS_CLK | TMCR2_WRITE_CR1; //CON2 accessed directly..TIMER2 is used for systick and time base for DCF77. Output enabled and sys clk.
MC6840_CON13 = TM_COUNTER_OUTPUT_DISABLE | TM_INTERUPT_ENABLE | TM_PULSE_WIDTH_COMP_MODE1 | TM_NORMAL_16BIT | TM_EXT_CLK | TMCR1_ALL_TIMERS_ALLOWED; //CON1. TIMER1 to measure DCF77 pulses length, so external source and interrupt enabled.
//Remember about endianess - MC6800 family is big endian, 6502 is little endian. Remember that timer is decremented.
MC6840_TIMER1 = Swap2Bytes(0xFFFF);
MC6840_TIMER2 = Swap2Bytes(0x9E57); //25ms interrupt (0xFFFF - 25000) - it is decremented!
MC6840_TIMER3 = Swap2Bytes(0xF82F); //500 Hz signal on audio output
}
Code: Select all
LDA MC6840_STA ; Load TIMER status register
AND #$01 ; Check if TIMER1 IRQ flag is set
BEQ irq_chk_rtc ; If flag is cleared, go to the next stage
LDA MC6840_TIMER1 ; You must read T1 to clear interrupt flag
TAX ; This is MSB, transfer it to X
LDA MC6840_TIMER1+1 ; This is LSB, it stays in A
JSR _dcf_analyze ; DCF77 being processed here
LDA #$FF ; Reload timer
STA MC6840_TIMER1 ; MSB Fir
STA MC6840_TIMER1 + 1 ; Then LSB
Code: Select all
void __fastcall__ dcf_analyze (uint16_t len) {
uint8_t tmp, tmp2;
uint16_t pulse_len;
pulse_len = 0xFFFF - len; //Timer counts in reverse, so we need to convert
tmp=dcf_count/8;
tmp2=dcf_count%8;
if (pulse_len < 3) { //Pulse shorter than 75ms (3*25ms) - invalid
dcf_count = 0; //Clear bit count
}
else if (pulse_len >= 3 && pulse_len <= 5) { //Valid bit 0 75-125 ms (100 ms)
dcf_data[tmp] = dcf_data[tmp] & (~(1<<tmp2)); //writnig 0
dcf_count++; //next bit
}
else if (pulse_len >= 7 && pulse_len <= 9) { //Valid bit 1 175-225 ms (200 ms)
dcf_data[tmp] = dcf_data[tmp] | (1<<tmp2); //writnig 1
dcf_count++; //next bit
}
else if (pulse_len > 39 && pulse_len < 84) { //Valid synchro null 59bit (975-2100 ms)
dcf_count = 0;
}
if (dcf_count > 58) { //End of receiving, now validate data
dcf_count = 0; //Prevent buffer overflow
memcpy(dcf_work_buffer, dcf_data, 8); //Copy received data to work buffer, it will be processed in main loop
dcf_frame_received = 1;
}
}
1. At the beginning, timer should be preloaded with 0xFFFF.
2. It stays still until GATE1 signal goes down.
3. It is decrementing initial timer value (0xFFFF) by 1 with every clock cycle on C1 input (which in my case is 25ms).
4. When GATE1 goes up, it starts counting and interrupt is executed.
5. I need to read the value stored in TIMER1 register, remembering to swap the bytes, because 6502 is little endian.
6. To get the actual number of "ticks" counted while GATE1 was low, I need to subtract this value from 0xFFFF.
7. Then I need to load TIMER1 with 0xFFFF once again, because otherwise it would start the next count from its last value. It doesn't reload itself automatically, Am I right?
Is there some mistake in my thinking?