0
\$\begingroup\$

Okay so I'm using MPLAB X and basically I'm trying to achieve a Software UART for this little PIC running at 4 MHZ. At first I tried with delays because it's been said that even at 4 MHZ, you could achieve 9600 bps (which is what I need, no less) but for some reason I couldn't even achieve 1200 bps. I've read that Timers are a lot more accurate so I started googling around and came out with this sample code to blink a LED:

#define _XTAL_FREQ 4000000

// Configuration Bits
#pragma config FOSC = INTRCIO   // Oscillator Selection bits (INTOSC oscillator: I/O function on GP4/OSC2/CLKOUT pin, I/O function on GP5/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-Up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = OFF      // GP3/MCLR pin function select (GP3/MCLR pin function is digital I/O, MCLR internally tied to VDD)
#pragma config BOREN = ON       // Brown-out Detect Enable bit (BOD enabled)
#pragma config CP = OFF         // Code Protection bit (Program Memory code protection is disabled)
#pragma config CPD = OFF        // Data Code Protection bit (Data memory code protection is disabled)

#define LED GP2

#include <xc.h>

unsigned int count = 0;

void interrupt ISR() {
  count++;           // Interrupt causes count to be incremented by 1
  if(count == 18) {
   LED = ~LED;       // Toggle LED every sec
   count = 0;
  }
  TMR0 = 192;        // TMR0 returns to its initial value
  INTCONbits.T0IF = 0;  // Bit T0IF is cleared so that the interrupt could reoccur
}

void main(void) {
    ANSEL = 0;
    ADCON0 = 0;
    CMCON = 0x07;
    VRCON = 0;
    TRISIO = 0b111011;
    GPIO = 0b000100;

    OPTION_REG = 0x07; // Prescaler (1:256) is assigned to the timer TMR0
    TMR0 = 192;          // Timer T0 counts from 192 to 255
    INTCON = 0xA0;     // Enable interrupt TMR0 and Global Interrupts 

    while (1) {
        // No code here
    }

    return;
}

I set TMR0 to 192 because that's 3/4 of 255, so the LED would blink every fourth of a second. When programmed and tested, I could see the LED blink a few times and then just stay on. Sometimes it would blink 10 times, some just 3 and then just stay on. Weird thing that it stays on cause it could also stay off. Maybe it has something to do with the problem. If you could point me anywhere on this I'd really appreciate it.

EDIT:

Here's a photo of the PIC setup. I left that 10k resistor from GP4 to ground because I was using it for another code now that I remember. There was also a wire coming from that pin that was floating around.

Maybe that resistor could've brought problems.

\$\endgroup\$
  • \$\begingroup\$ Can you show your configuration bits? \$\endgroup\$ – Roger Rowland Aug 2 '17 at 6:52
  • \$\begingroup\$ @RogerRowland Sure, just edited. \$\endgroup\$ – Joaquin Guevara Aug 2 '17 at 6:55
  • \$\begingroup\$ May I suggest something: Why don't you modify your code and toggle the LED every second instead of every fourth of a second and see what happens? Perhaps the capacitance at the output pin is too high for such a small time period. Just a thought of course, I could be wrong. How have you connected the LED by the way? \$\endgroup\$ – nickagian Aug 2 '17 at 7:26
  • 1
    \$\begingroup\$ I compiled and ran your code on a 12F675 and it worked fine, blinking continuously at ~1.7Hz (25 minutes so far, and still going strong!). If the code is OK then you must have a hardware problem. Show us a photo of your setup. \$\endgroup\$ – Bruce Abbott Aug 2 '17 at 8:06
  • 1
    \$\begingroup\$ I asked the same thing previously, but you didn't reply. You shouldn't just connect the LED directly to the MCU and expect it to work. Put a series resistor to limit the current to a specific value. That could also be the cause of your problems. Also have in mind that the output current available from the Pickit3 to the MCU is small (I think around 30mA only). That could also be the cause, especially since you just drew the max current available from GP2 (without the series resistor). You should use a proper power supply. And like others said, use a decoupling capacitor between VDD and VSS. \$\endgroup\$ – nickagian Aug 2 '17 at 9:07
2
\$\begingroup\$

It ended up being a hardware problem. By placing the LED directly in between the output pin and GND, the current would be approx 16.5 mA, which would take the MCU to the limit, atleast this small one in particular. Apparently, the PIC stalls because of some security mechanism or just an error inside the PIC. I'm not sure because when I uploaded a blink code with delays instead of Timer0, it worked fine. The thing is that a series resistor should be placed in series with the LED, although it should be taken into account that even 10mA is almost as much as the whole MCU can provide amongst it's pins.

Here's a photo of it working WITHOUT the decoupling capacitor, as a demonstration that the problem was the excess current drawing, but it should be used always anyways since it reduces noise and will save you future headaches when debugging the PIC.

enter image description here

\$\endgroup\$
1
\$\begingroup\$

To get a reliable periodic interrupt using timer 0, you need to add into it each interrupt, not reload it. This way the interrupt latency and possible jitter is cancelled out.

The processor is running at 4 MHz, so the instruction clock rate is 1 MHz. That means there are 104 instruction cycles per bit slot at 9600 baud. That is plenty to implement a firmware UART, although with unknown compiler overhead in the way, you don't really know what's happening. I would definitely code the interrupt routine in assembler. Do the UART stuff as immediately after the interrupt as you can, to reduce jitter. Add into timer 0 after that, since that is jitter-independent.

Since the details of what to add to timer 0 are a little tricky, I use a macro for that. This is from my STD.INS.ASPIC file available as part of my free PIC development environment:

;********************
;
;   Macro TIMER0_PER cy
;
;   Update timer 0 so that it next wraps CY cycles from the previous wrap.  This
;   can be useful in a timer 0 interrupt routine to set the exact number of
;   cycles until the next timer 0 interrupt.  Timer 0 is assumed to be running
;   from the instruction clock.  The appropriate value is added into timer 0,
;   so this macro does not need to be invoked a fixed delay after the last
;   timer 0 wrap.  CY must be a constant.
;
;   The timer sets its interrupt flag when counting from 255, which wraps back
;   to 0.  If left alone, the timer therefore has a period of 256 instruction
;   cycles.  When adding a value into the timer, the increment is lost during
;   the add instruction, and the timer is not incremented for two additional
;   cycles when the TMR0 register is written to.  This effectively adds 3 more
;   cycles to the timer 0 wrap period.  These additional cycles are taken
;   into account in computing the value to add to TMR0.
;
timer0_per macro cy
         dbankif tmr0
         movlw   256 + 3 - (cy)
         addwf   tmr0
         endm

Of course, there are obvious easier ways to solve this overall problem:

  1. Use a PIC with a UART.

  2. Use timer 2, since it has a built-in period register.

\$\endgroup\$
  • \$\begingroup\$ Thanks a lot. I'm already implementing the UART Firmware at 300 bps to test with the RX of an Arduino Uno. I've of course done the UART pin-state-switching as soon as the interrupt starts, although I'm making it in C since I'm on MPLAB X. I don't get assembler much but I'll give it a try. \$\endgroup\$ – Joaquin Guevara Aug 2 '17 at 12:01
  • \$\begingroup\$ Using MPLAB X is not a reason for or against C or assembler. MPASM is included in the free suite of software that comes with MPLAB. \$\endgroup\$ – Olin Lathrop Aug 2 '17 at 12:03
  • \$\begingroup\$ Sure, I'm just kinda new, it's the third day that I started with PICs. Maybe I could decompile the final C code and just retouch the interrupt routine in assembler. BTW I am not managing to make it work even at 300 bps. Guess I should make another question \$\endgroup\$ – Joaquin Guevara Aug 2 '17 at 12:06
  • \$\begingroup\$ With a 1:256 prescaler you shouldn't lose Timer0 counts on reload, but you still loose a few cycles because any write to timer0 (including adding to it) clears the prescaler. The only ways to avoid this are to either not use the prescaler (= very high interrupt frequency) or just let it roll over and accept a time period of 256*2^n cycles. \$\endgroup\$ – Bruce Abbott Aug 2 '17 at 15:29
  • \$\begingroup\$ @Bruce: The OP is trying to time 104 cycles, so no prescaler. \$\endgroup\$ – Olin Lathrop Aug 2 '17 at 16:11
0
\$\begingroup\$

the PIC stalls because of some security mechanism

You turned out bor. I have used similar setup successfully but no bor.

Your timer code can be improved as well. To maintain long time accuracy, try not to load up the offset repeatedly.

Edit: when you load to the timer counter, you have destroyed the value it may have accumulated since rollover, either in the timer counter or the prescaler.

So the best way to get long term accuracy in a timer like timer0 is to never reload it again once it starts.

Like this

TMR0 = -TMR_PR; //LOAD UP TMRPR LSB. .... Get timer 0 going.

In the ist If (TMR_PR) TMR_PR -= 0X100; //update tmrpr , assuming 8 bit tmr0. else TMR_PR has expired.

What it does is that regardless of how long the desired time period is, the counter is only reloaded once, at the very beginning.

But your problem is unlikely the timer issue here. So rather than thinking it is you think it is, post your soft Hart routines and let other people help you.

\$\endgroup\$
  • \$\begingroup\$ Thanks, I don't get what you mean by try not to load up the offset repeatedly. \$\endgroup\$ – Joaquin Guevara Aug 2 '17 at 11:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.