View previous topic :: View next topic |
Author |
Message |
Marcoose
Joined: 09 Dec 2006 Posts: 5 Location: Ontario, Canada
|
Configuring Timer1 Gate on PIC12F1840 |
Posted: Tue Mar 17, 2015 7:48 pm |
|
|
I have spent quite some time with the datasheet and experimenting but I cannot seem to get the Timer1 Gate feature working as expected. I am using a PIC12F1840 and compiler 4.129.
My end goal is to measure a short pulse width by using the Timer1 Gate on pin RA4 to control Timer1 like a stopwatch, and fetch the sampled time in an ISR or by polling between pulses.
I have tried to scale back on any complexity and just enable the T1G at all (without single pulse mode for nw).
Code: |
#include <debug_serial.h>
#use rs232(uart1, baud=19200,parity=N,stop=1,bits=8)
void main() {
printf("Hello\r\n");
// Set to use internal oscillator at 16MHz with no PLL
setup_oscillator(OSC_16MHZ|OSC_INTRC);
// Disable ADC
setup_adc_ports(NO_ANALOGS);
setup_adc(ADC_OFF);
// Disable ECCP
setup_ccp1(CCP_OFF);
setup_comparator(NC_NC_NC_NC);
// Pin 543210
set_tris_a(0b011010);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_4|T1_GATE_INVERTED);
// Main loop
while (true) {
delay_ms(1000);
printf("val: %Lu\r\n", get_timer1());
}
}
|
My expectation would be that this would keep printing "val: ####" over and over where the number #### is unchanged so long as T1G is held low. As soon as T1G is pulled high I would expect Timer1 to start counting (and rolling over) giving some other value.
However, it seems to be continuously running regardless of the state of T1G as my "val" never freezes.
Until I can understand this, it seems pointless to go off writing interrupt handlers and trying to enable the single-pulse mode.
Thanks! |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Tue Mar 17, 2015 9:57 pm |
|
|
how short is "short"?
and what is the interval between pulses ??
that gate could open and close many times in 1000msec ,
and the value you get from timer 1 could be many pulses or just one ,
but since you never CLEAR timer1
all you will get is random accumulating garbage anyway.
you need to very fundamentally rethink what the procedure
should be to use the gate to do what you want.
hint: you need to monitor gate state and clear the counter after a read
. it could be tricky.....
in reality this might be better handled with the capture module
instead.
post a complete program that will actually compile please.. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Mar 18, 2015 1:00 am |
|
|
I made it work. The program below displays the following output on
the terminal window (TeraTerm) when I quickly press a push-button switch
on pin A4 once every 3 to 4 seconds.
If I don't press the switch, the timer value holds the same value and
continuously displays it, once per second. If I press the switch and then
release it, the timer counts up to a new value during the time that I held
the switch down. When I release the switch, it then displays the new
value once per second. You can see that below:
Code: |
val: 0
val: 0
val: 0
val: 0
val: 18708
val: 18708
val: 18708
val: 31844
val: 31844
val: 41731
val: 41731
val: 58043
val: 7849
val: 7849
val: 7849
val: 7849
val: 7849
val: 7849
|
This is a schematic of the push-button switch circuit on Pin A4:
Code: |
+5v
|
<
> 4.7K
< ___ Switch
To | _|_|_
PIC -----------------o o------
pin |
A4 --- GND
-
|
I tested this on a Microchip Low Pin Count board. I added the extra
switch to the board. It doesn't come with it. For serial output, I have
a small Sparkfun RS232 Shifter board. https://www.sparkfun.com/products/449
I have jumpers between the Low Pin Count board and the Shifter board
for Vcc, Gnd, and from Pin A0 on the PIC to the Rx-In pin on the Shifter
board. This was tested with CCS vs. 5.042.
Test program:
Code: |
#include <12F1840.h>
#fuses INTRC_IO, NOWDT, BROWNOUT
#use delay(clock=4M)
#use rs232(baud=9600, UART1)
//======================================
void main()
{
setup_timer_1(T1_INTERNAL | T1_DIV_BY_8 | T1_GATE | T1_GATE_A4);
set_timer1(0);
while(TRUE)
{
delay_ms(1000);
printf("val: %Lu\r\n", get_timer1());
}
} |
|
|
|
Marcoose
Joined: 09 Dec 2006 Posts: 5 Location: Ontario, Canada
|
|
Posted: Wed Mar 18, 2015 3:39 pm |
|
|
Thanks both asmboy and PCM programmer for your feedback!
Apologies for not providing a more clear explanation.
My test setup is eerily similar to what PCM programmer did, and I should have said so. I am using a breadboard with SMD adapter and have the serial connected to PC via a USB-TTL dongle and TeraTerm. Am using a switch exactly as in PCM programmer's example except the logic level inverted (A4 pulled low with 10k resistor, switch pulls it high) -- hence my T1_GATE_INVERTED.
I was trying to produce the exact same result (i.e. unchanging serial output until I tap the switch, not caring what the actual timer count is).
I just flashed PCM programmer's code and reversed my breadboard switch polarity to match. Strangely I am seeing the same behaviour I had before: an output where "val: ###" is constantly changing regardless of the switch state.
At least given that I now have a known-working piece of code and hardware setup from PCM programmer I will have to see if there is something wrong with my incredibly simple circuit (I made sure A4 wasn't floating with a logic probe, but will keep digging). If the hardware checks out, perhaps there is a glitch in my version of the compiler. I'm not sure I understand the assembly listing enough to figure out if a register isn't being set correctly per the data sheet but I'll try.
Apologies for not supplying a full program. I didn't expect anyone to go so far as compiling and testing for me!
Many many thanks!
P.S. the eventual goal is to capture a signal from a hobby RC receiver (1 - 2ms pulse, about 20ms apart). I know this has been done countless times, but I have always seen an interrupt-on-change used and wanted to try my hand at the timer1 gate since it seems perfectly suited. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Mar 18, 2015 4:38 pm |
|
|
Quote: | I am using a PIC12F1840 and compiler 4.129.
perhaps there is a glitch in my version of the compiler
|
Try adding the lines shown in bold below:
Quote: |
#include <12F1840.h>
#fuses INTRC_IO, NOWDT, BROWNOUT
#use delay(clock=4M)
#use rs232(baud=9600, UART1)
#byte APFCON = getenv("SFR:APFCON")
#bit T1GSEL = APFCON.3
//======================================
void main()
{
setup_timer_1(T1_INTERNAL | T1_DIV_BY_8 | T1_GATE | T1_GATE_A4);
set_timer1(0);
T1GSEL = 0;
while(TRUE)
{
delay_ms(1000);
printf("val: %Lu\r\n", get_timer1());
}
} |
|
|
|
Marcoose
Joined: 09 Dec 2006 Posts: 5 Location: Ontario, Canada
|
|
Posted: Wed Mar 18, 2015 7:00 pm |
|
|
And like magic it works! A great many thanks!
In trying to understand better what is going on I had already started digging through the .lst file and "learning" assembly.
Or rather using the datasheet and opcode cheat sheet a line at a time to try and understand what was being put where. I hadn't spotted a problem (was looking for T1GCON) but looking at the diff now it is obvious.
Code: | .................... setup_timer_1(T1_INTERNAL | T1_DIV_BY_8 | T1_GATE | T1_GATE_A4);
00E2: BSF 1D.3
00E3: MOVLW 35
00E4: MOVLB 00
00E5: MOVWF 18
00E6: MOVLW 80
00E7: MOVWF 19
.................... set_timer1(0);
00E8: CLRF 17
00E9: CLRF 16
|
With added code
Code: |
.................... T1GSEL = 0;
00EA: MOVLB 02
00EB: BCF 1D.3 |
The randomly-activated timer gate makes a lot more sense when T1G was assigned to A3/MCLR.
I think this detour into the .lst file has been very helpful to my future troubleshooting! I'm always incredibly hesitant to doubt my tools (I tend to be the one wrong 99% of the time), but it's nice to know I wasn't completely crazy this time.
Thanks again so much for sharing your time and knowledge! |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Thu Mar 19, 2015 8:22 am |
|
|
if you wanted to measure individual pulses - with a value for each one-
you could link pin RA4 to an edge interrupt pin on portB
and use it to detect the falling edge of each pulse -
Then within the interrupt handler for the edge signal -
put the captured timer1 value into a circular buffer - and lastly
reset timer 1 to zero before leaving the int handler.
This would eliminate the malarky with using a delay_ms and
idle time in main AND
you would have a safe record of a number of sequential pulse timings - limited only by capture buffer size and how often you read/ clear buffer entries from main();
a personal note: in my own work, i deprecate using delay calls ( other than delay_cycles) except for barest initial hardware setup.
For "in program" delays - loops using an uncommitted timer - on a polled basis allows other critical activities to go on unimpeded.
delay_ms is great for proof of concept and fundamental screwing around, but IMHO not the way to create reliable complex programs in the time domain. With up to 7 system timers -the 18F46/26K family has a generous number of surplus counters that make this approach so easy to follow. |
|
|
|