View previous topic :: View next topic |
Author |
Message |
vtrx
Joined: 11 Oct 2017 Posts: 142
|
PORTB interrupt on change |
Posted: Wed Jun 29, 2022 5:29 pm |
|
|
I'm testing some routines for reading a rotary encoder.
I intend to use the example from the link, but using the 18F45K50.
https://simple-circuit.com/pic18f46k22-rotary-encoder-7-segment-ccs/
the example defines:
#BYTE IOCB = 0x0F62 register address,but in the datasheet I didn't find this reference, I found 0x0F86.
what is the reference for the 18F45K50?
the code works with both(0x0F62/0x0F86 ). |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Jun 29, 2022 6:30 pm |
|
|
The example code uses 18F46K22. It uses 0xF62 for the IOCB register.
Your PIC is the 18F45K50. It uses 0xF86 for the IOCB register address.
Different PICs. Different addresses. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Thu Jun 30, 2022 12:18 am |
|
|
And, this is why in recent years, we have tended to advise people to use
the compiler's ability to locate registers by name, rather than coding an
address into the program:
#BYTE IOCB = getenv("SFR:IOCB")
Makes the program much more portable and easy to move from chip
to chip.
It is 'interesting' that it works with either. The address is listed as UCNFG
(presumably 'unconfigured'), but that it works, suggests it may have a
'ghost' copy of the IOCB register actually there!... |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9226 Location: Greensville,Ontario
|
|
Posted: Thu Jun 30, 2022 5:41 am |
|
|
Hay Mr. T...
where did you see the UNCFG for the address ?
My datasheet copy...
DS40001412G-page 82
also looked at the PortB reg stuuf, IOC area, can't see 'UNCFG'
either I'm going more blind or maybe you have a different version of the datasheet ?
jest curious
Jay |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Thu Jun 30, 2022 7:06 am |
|
|
The whole point is it is not the interrupt on change area.
On the 48K22, the IOCB register is 0xF62. In the 45K50, the IOCB
register is 0xF86. If you look at 0xF62, in the register listing for the
45K50, (and only here), it says UNCFG. Doesn't refer to it anywhere
else.
Now what is interesting is that the poster found that this did work as
well. Suggests that it does actually contain a ghost copy of the register. |
|
|
JAM2014
Joined: 24 Apr 2014 Posts: 138
|
|
Posted: Thu Jun 30, 2022 7:54 am |
|
|
Hi All,
To the OP, have you see this thread?: http://www.ccsinfo.com/forum/viewtopic.php?t=59778
It's a complete, working example of using a rotary encoder with the PIC you are using.
Also, why mess with manipulating individual registers? Look at the .h file for this PIC, and you'll see that CCS supports enabling individual pins on Port B for IOC. I'm not sure why doing it otherwise is being suggested here? You'll note that the example code does not manipulate the IOCB register directly.
Jack |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9226 Location: Greensville,Ontario
|
|
Posted: Thu Jun 30, 2022 7:57 am |
|
|
OK, now it makes sense, needed to look at the 45K50 regs NOT the 46K22.
too many PICs !! |
|
|
vtrx
Joined: 11 Oct 2017 Posts: 142
|
|
Posted: Thu Jun 30, 2022 3:49 pm |
|
|
I tested four codes and the best one for my hardware was the one in the link I posted, but sometimes the count 'fails' in 1 step. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9226 Location: Greensville,Ontario
|
|
Posted: Thu Jun 30, 2022 5:47 pm |
|
|
simple test...
increase the clock ! It's only 8MHz, chip is good for 64 MHz though you'll have to recalculate the timer values... |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Fri Jul 01, 2022 12:21 am |
|
|
Have you a data sheet for the encoder you are using?.
If so, post a link.
There is always the potential to miss a count, if the encoder does not
guarantee a minimum pulse width, in the situation when you reverse
direction. If the encoder does give such a guarantee, then the key is
to design the handler to be able to deal with this minimum time.
Going faster will help massively to do this.
Do you need to use any other interrupt at the same time?. Remember
you can use polling for a lot of things. If not, then the fastest encoder
'possible' (though at this point somebody will post a version that is faster),
is the routine I published some years ago, and then copied here:
http://www.ccsinfo.com/forum/viewtopic.php?t=44491
This has in the past merrily handled a 10000 line encoder at up to 100RPM
and has never missed a count (always precisely re-indexing when the
system goes back to zero), on a PIC at 40MHz, for now in some cases
over twenty years. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9226 Location: Greensville,Ontario
|
|
Posted: Fri Jul 01, 2022 6:00 am |
|
|
hmm encoder.....
wondering how 'clean' the signals are from it ?
if 'noisy', that can lead to miscounts...
perhaps stiffer pullups, use an ST buffer ?? |
|
|
vtrx
Joined: 11 Oct 2017 Posts: 142
|
|
Posted: Fri Jul 01, 2022 8:52 am |
|
|
I found this method, reading is very fast, I changed it to use interrupt on change.
There is a problem, the result jumps two values, eg 1,3,5,7 both forward and backward.
Any ideas how I can get around this?
Code: | #include <18F45K50.h>
...
#use delay(int, clock=48MHz, USB_FULL, act=USB)
...
#use fast_io(b)
...
#BYTE IOCB = getenv("SFR:IOCB")
...
int16 counter = 0;
int8 currentStateCLK;
int8 previousStateCLK;
...
#INT_RB // PORTB interrupt on change ISR
void RB_IOC_ISR(void)
{
// Read the current state of inputCLK
currentStateCLK = input_state(PIN_B4); //clk
clear_interrupt(INT_RB); // clear RB IOC flag
// If the previous and the current state of the inputCLK are different then a pulse has occured
if (currentStateCLK != previousStateCLK){
// If the inputDT state is different than the inputCLK state then
// the encoder is rotating counterclockwise
if (input_state(PIN_B7) != currentStateCLK) {
counter -= 1;
} else {
// Encoder is rotating clockwise
counter += 1;
}
}
// Update previousStateCLK with the current state
previousStateCLK = currentStateCLK;
}
//-----------------------------------------------------------------------------
void main(void)
{
SETUP_ADC(ADC_OFF);
setup_adc_ports(NO_ANALOGS);
setup_comparator(NC_NC_NC_NC);
SETUP_CCP1(CCP_OFF);
SETUP_CCP2(CCP_OFF);
port_b_pullups(0b11111111);
...
enable_interrupts(GLOBAL); // enable global interrupts
clear_interrupt(INT_RB); // clear RB IOC flag
enable_interrupts(INT_RB); // enable RB IOC
IOCB = 0b00010000; // enable RB4 & RB7 IOC
currentStateCLK = input(PIN_B4); //clk
while(true)
{
usb_task();
usb_debug_task();
//.............................................................................
if(usb_enumerated())
{
if(usb_kbhit(1))
{
usb_get_packet(1, in_data,USB_EP1_RX_SIZE);
if(in_data[0] =='4')
{
out_data[0] = make8(counter,1);
out_data[1] = make8(counter,0);
usb_put_packet(1,out_data,USB_EP1_TX_SIZE,USB_DTS_TOGGLE);
}
if(in_data[0] =='5')
{
}
}
}
//.............................................................................
delay_ms(5);
}
}
... |
I intend to use a joystick interface, the result is transferred using USB HID.
I'm monitoring the results using my own program that makes the HID reading of the result every 5 milliseconds.
I did a test disabling the interrupt and using the reading inside the loop to check if there were two interrupts in a row, but it didn't change anything. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Fri Jul 01, 2022 10:06 am |
|
|
Two values for what?.
Understand that the normal use for a quadrature interface, would give
four counts for a single cycle of either signal. Four edges. This is the
'quad' bit of quadrature. A single count is a single edge on either of the
two signals.
Now you are only triggering off one signal, so a 'birature' count would
be expected. Two counts for a single pulse. One on the rising edge, and
one on the falling edge. So if you are testing with your own pulse, two
counts would be expected for a single pulse. |
|
|
vtrx
Joined: 11 Oct 2017 Posts: 142
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Mon Jul 04, 2022 12:52 am |
|
|
You have IOC available on Port C as well. C0, C1, C2, C4, C5, C6, C7. |
|
|
|