|
|
View previous topic :: View next topic |
Author |
Message |
Eugeneo
Joined: 30 Aug 2005 Posts: 155 Location: Calgary, AB
|
Software Usart using timer1 compare and ext int |
Posted: Sat Sep 10, 2005 12:01 am |
|
|
I just wrote this disasterous code. I know I should have went with a chip with a hardware RS232 usart, but this seems to work really well. There is enough cpu power to write to a flash spi(osc/4) chip at 9600 bps with back to back data. My question is, are there any better ways of doing this .... of course excluding hardware usart.
#byte OSCCON = 0x8F
#bit EXT_INT_FLAG = 0x8B.1
#bit TMR0_INT_FLAG = 0x8B.2
#bit TMR1_INT_FLAG = 0x0C.0
#bit CCP1_INT_FLAG = 0x0c.2
#bit SPI_CKP = 0x14.4 // microchip datasheet has better definitions
#bit SPI_CKE = 0x94.6
#bit SPI_SMP = 0x94.7
ext_int_edge(h_TO_l); // 232 trigger method
enable_interrupts(global); // EI
EXT_INT_FLAG = 0; // Clear interrupt flag
enable_interrupts(int_ext); // serial interrupt
TMR0_INT_FLAG = 0; // clear timer0 int flag
disable_interrupts(INT_RTCC); // Enable rpm input interrupt
setup_timer_0(RTCC_INTERNAL |RTCC_DIV_256); //
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1); // timer 1 interval
disable_interrupts(INT_TIMER1); // overflow interrupts
CCP1_INT_FLAG = 0; // Clear interrupt flag
setup_ccp1(CCP_COMPARE_INT); // Configure CCP1 to set
disable_interrupts(INT_CCP1); // disable for now
CCP_1=165;
setup_spi(SPI_MASTER | SPI_CLK_DIV_4);
SPI_CKP = 0;
SPI_CKE = 1;
SPI_SMP = 0;
It uses a the timer1 and int on ccp
#int_ext // *********** Detect serial input *********** //
void start_bit_isr(void) {
output_high(LED); // Turn on the led
output_high(TEST_PIN); // Turn scope check pin on
set_timer1(0); // Reset timer
output_low(TEST_PIN); // Turn off scope check pin
CCP1_INT_FLAG = 0; // ensure no prior CCP flags
enable_interrupts(INT_CCP1); // start bit bang timer for serial in
ser_byte_in=0; // Clear current serial byte
ser_bit_in=0; // Clear serial bit index
disable_interrupts(int_ext); // disable for the duration of 8 bits
EXT_INT_FLAG = 0; // clear interrupt flag
output_low(LED); // Turn off the led
set_timer1(0); // Reset timer
}
#int_ccp1 // *********** bit to bit interrupt ************//
void next_serial_bit(void)
{
output_high(TEST_PIN);
set_timer1(0); // Reset timer
output_low(TEST_PIN);
if(ser_bit_in==8) // 0-7 bits all in
{
disable_interrupts(INT_CCP1); //
enable_interrupts(int_ext); // wait for start bit again
EXT_INT_FLAG = 0; // clear interrupt flag
CCP1_INT_FLAG = 0;
ser_data_ready = 1;
data_in[data_in_index]=ser_byte_in; //transfer byte to buffer
data_in_index+=1; //advance ring buffer
if(data_in_index==BUFFER_SIZE)
data_in_index=0;
return;
} |
|
|
Ttelmah Guest
|
|
Posted: Sat Sep 10, 2005 3:01 am |
|
|
What you describe is a perfectly reasonable way of doing this. There are a few comments though.
First the compiler now has a 'clear_interrupt' instruction, so you don't need to access the flags with definitions - makes code more portable if you change chip. This has applied for about the last six months, and is desribed in the 'readme', but not (yet) in the standard manual pages.
You don't need to clear the interrupt in the handler. The compiler does this for you on exiting the handler (obviously you do for the 'other interrupt whan switching between the two sources).
Interrupts are disabled by default, so the disables shown in what I presume is an 'extract' from your main code, are not needed. However as a further comment here, clear the interrupts _after_ configuring the device, not before.
The biggest comment though is timing. You set the CCP value once in the main code. You need to change this latter.
Remember that if you have a serial pattern, your 'edge' interrupt will occur on the falling edge of the start bit. Your sampling point for the first bit, needs to be 1.5 bit times latter, then for the subsequent bits at 1 bit time intervals.
Now your CCP timer should be zeroed as soon as possible in the interrupt handler, and the time interval chosen for the first gap, should be 1.5 bit times, _less_ two interrupt latency times (the time to arrive in the edge interrupt, and the time then to reach the 'sampling' interrupt). In the CCP interrupt, the CCP interval should be set to one bit time (the latencies have already been handled). Don't reset the timer in the CCP handler, have the CCP set to automatically reset it's timer. This way there will not be a cumulative error from the latencies.
I don't see where the 'byte' is actually receivd in the code...
If you set a mask to '1', at the same time as you zero the counter, then in the CCP handler have:
Code: |
#int_ccp1 // *********** bit to bit interrupt ************//
void next_serial_bit(void) {
CCP_1=BITTIME;
output_high(TEST_PIN);
//Not needed if you make the CCP do this - set_timer1(0);
output_low(TEST_PIN);
if(ser_bit_in==8) // 0-7 bits all in {
disable_interrupts(INT_CCP1); //
CCP_1=BIT_AND_HALF_TIME;
enable_interrupts(int_ext); // wait for start bit again
EXT_INT_FLAG = 0; // clear interrupt flag
//Not needed CCP1_INT_FLAG = 0;
ser_data_ready = 1;
data_in[data_in_index]=ser_byte_in; //transfer byte to buffer
data_in_index+=1; //advance ring buffer
if(data_in_index==BUFFER_SIZE)
data_in_index=0;
}
else {
//Here a bit is arriving
if (input(IP_BIT)) ser_byte_in |= mask;
mask*=2;
++ser_bit_in;
}
}
|
The byte is generated about as efficiently as possible (IP_BIT is the bit to actually 'test' - presumably PIN_B0).
What you have is probably working (with the actual input code which is not shown), because the interrupt latency is enough to be inside the bit time for the sampling events, and though the sample point then gets later on each interrupt (because of software resetting the timer), is not getting out of the width of the bit.
It is not made very clear in the data, but 'CCP_COMPARE_RESET_TIMER', both resets the timer, and generates an interrupt. In the chip data sheet, this is the 'special event trigger', which is described as:
CCPxIF bit is set, CCPx pin is unaffected, CCP1 resets TMR1.
This is exactly what you want. :-)
Best Wishes |
|
|
Guest
|
|
Posted: Sat Sep 10, 2005 9:43 pm |
|
|
Ttelmah wrote: |
First the compiler now has a 'clear_interrupt' instruction, so you don't need to access the flags with definitions - makes code more portable if you change chip. This has applied for about the last six months, and is desribed in the 'readme', but not (yet) in the standard manual pages.
I've just ran out of compiler updates. I'm going to purchase PCWH soon so that is good to know.
You don't need to clear the interrupt in the handler. The compiler does this for you on exiting the handler (obviously you do for the 'other interrupt whan switching between the two sources).
Did not know that.
However as a further comment here, clear the interrupts _after_ configuring the device, not before.
The biggest comment though is timing. You set the CCP value once in the main code. You need to change this latter.
Remember that if you have a serial pattern, your 'edge' interrupt will occur on the falling edge of the start bit. Your sampling point for the first bit, needs to be 1.5 bit times latter, then for the subsequent bits at 1 bit time intervals.
If you notice I clear the timer1 twice and have a test pin to sync my input. All that code actually offsets .5 of a bit time and the next bit lands right in the center of the lsb. Messy but it works because the latency calculations didn't work for me. Is it possible the interrupt latency is actually more than 2?.
Now your CCP timer should be zeroed as soon as possible in the interrupt handler, and the time interval chosen for the first gap, should be 1.5 bit times, _less_ two interrupt latency times (the time to arrive in the edge interrupt, and the time then to reach the 'sampling' interrupt). In the CCP interrupt, the CCP interval should be set to one bit time (the latencies have already been handled). Don't reset the timer in the CCP handler, have the CCP set to automatically reset it's timer. This way there will not be a cumulative error from the latencies.
As you can see i'm using a int on compare. Can I also and that with clear on match?
I don't see where the 'byte' is actually receivd in the code...
If you set a mask to '1', at the same time as you zero the counter, then in the CCP handler have:
Code: |
#int_ccp1 // *********** bit to bit interrupt ************//
void next_serial_bit(void) {
CCP_1=BITTIME;
output_high(TEST_PIN);
//Not needed if you make the CCP do this - set_timer1(0);
output_low(TEST_PIN);
if(ser_bit_in==8) // 0-7 bits all in {
disable_interrupts(INT_CCP1); //
CCP_1=BIT_AND_HALF_TIME;
So your saying set the CCP time here to avoid latency during the startbit int? Will timer 1 continue to increment even if it is disabled?
enable_interrupts(int_ext); // wait for start bit again
EXT_INT_FLAG = 0; // clear interrupt flag
//Not needed CCP1_INT_FLAG = 0;
ser_data_ready = 1;
data_in[data_in_index]=ser_byte_in; //transfer byte to buffer
data_in_index+=1; //advance ring buffer
if(data_in_index==BUFFER_SIZE)
data_in_index=0;
}
else {
//Here a bit is arriving
if (input(IP_BIT)) ser_byte_in |= mask;
mask*=2;
ok this is just syntax but it makes no sence to me
++ser_bit_in;
}
}
|
The byte is generated about as efficiently as possible (IP_BIT is the bit to actually 'test' - presumably PIN_B0).
What you have is probably working (with the actual input code which is not shown), because the interrupt latency is enough to be inside the bit time for the sampling events, and though the sample point then gets later on each interrupt (because of software resetting the timer), is not getting out of the width of the bit.
It is not made very clear in the data,
but 'CCP_COMPARE_RESET_TIMER', both resets the timer, and generates an interrupt. In the chip data sheet, this is the 'special event trigger', which is described as:
CCPxIF bit is set, CCPx pin is unaffected, CCP1 resets TMR1.
Ohh.. I didn't see that
This is exactly what you want. :-)
Best Wishes |
Wow, thanks for taking the time to look at it. I've learned quite a bit with this post.
Greatly Appreciated. |
|
|
Guest
|
|
Posted: Sat Sep 10, 2005 9:44 pm |
|
|
Ttelmah wrote: |
First the compiler now has a 'clear_interrupt' instruction, so you don't need to access the flags with definitions - makes code more portable if you change chip. This has applied for about the last six months, and is desribed in the 'readme', but not (yet) in the standard manual pages.
I've just ran out of compiler updates. I'm going to purchase PCWH soon so that is good to know.
You don't need to clear the interrupt in the handler. The compiler does this for you on exiting the handler (obviously you do for the 'other interrupt whan switching between the two sources).
Did not know that.
However as a further comment here, clear the interrupts _after_ configuring the device, not before.
The biggest comment though is timing. You set the CCP value once in the main code. You need to change this latter.
Remember that if you have a serial pattern, your 'edge' interrupt will occur on the falling edge of the start bit. Your sampling point for the first bit, needs to be 1.5 bit times latter, then for the subsequent bits at 1 bit time intervals.
If you notice I clear the timer1 twice and have a test pin to sync my input. All that code actually offsets .5 of a bit time and the next bit lands right in the center of the lsb. Messy but it works because the latency calculations didn't work for me. Is it possible the interrupt latency is actually more than 2?.
Now your CCP timer should be zeroed as soon as possible in the interrupt handler, and the time interval chosen for the first gap, should be 1.5 bit times, _less_ two interrupt latency times (the time to arrive in the edge interrupt, and the time then to reach the 'sampling' interrupt). In the CCP interrupt, the CCP interval should be set to one bit time (the latencies have already been handled). Don't reset the timer in the CCP handler, have the CCP set to automatically reset it's timer. This way there will not be a cumulative error from the latencies.
As you can see i'm using a int on compare. Can I also and that with clear on match?
I don't see where the 'byte' is actually receivd in the code...
If you set a mask to '1', at the same time as you zero the counter, then in the CCP handler have:
Code: |
#int_ccp1 // *********** bit to bit interrupt ************//
void next_serial_bit(void) {
CCP_1=BITTIME;
output_high(TEST_PIN);
//Not needed if you make the CCP do this - set_timer1(0);
output_low(TEST_PIN);
if(ser_bit_in==8) // 0-7 bits all in {
disable_interrupts(INT_CCP1); //
CCP_1=BIT_AND_HALF_TIME;
So your saying set the CCP time here to avoid latency during the startbit int? Will timer 1 continue to increment even if it is disabled?
enable_interrupts(int_ext); // wait for start bit again
EXT_INT_FLAG = 0; // clear interrupt flag
//Not needed CCP1_INT_FLAG = 0;
ser_data_ready = 1;
data_in[data_in_index]=ser_byte_in; //transfer byte to buffer
data_in_index+=1; //advance ring buffer
if(data_in_index==BUFFER_SIZE)
data_in_index=0;
}
else {
//Here a bit is arriving
if (input(IP_BIT)) ser_byte_in |= mask;
mask*=2;
ok this is just syntax but it makes no sence to me
++ser_bit_in;
}
}
|
The byte is generated about as efficiently as possible (IP_BIT is the bit to actually 'test' - presumably PIN_B0).
What you have is probably working (with the actual input code which is not shown), because the interrupt latency is enough to be inside the bit time for the sampling events, and though the sample point then gets later on each interrupt (because of software resetting the timer), is not getting out of the width of the bit.
It is not made very clear in the data,
but 'CCP_COMPARE_RESET_TIMER', both resets the timer, and generates an interrupt. In the chip data sheet, this is the 'special event trigger', which is described as:
CCPxIF bit is set, CCPx pin is unaffected, CCP1 resets TMR1.
Ohh.. I didn't see that
This is exactly what you want. :-)
Best Wishes |
Wow, thanks for taking the time to look at it. I've learned quite a bit with this post.
Greatly Appreciated. |
|
|
|
|
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 vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|