1
\$\begingroup\$

I am programming PIC16F886 to receive and process a few AT commands for sending and receiving SMS. At first, it works correctly, but by growing the code (it occupied only 50% of the flash memory) the PIC started strange behaviors while receiving serial data; Sometimes the high nibble of RCSTA register changes; Sometimes the T2CON register changes; Sometimes the CCP1CON register changes and PWM starts (I have no code related to CCP in my project at all). This strange behavior changes just by adding some NOP to my code. What's the problem? I'm really confused!
I am using mikroC PRO compiler and processing 1200 bps serial data on interrupt routine. I do just a simple AT command detection and data buffering on the interrupt, processing and responding is done on the main routine. BTW, I also tested just buffering on interrupt and full processing on main routine, but the problem does not solved.
This is a part of my code:

#define AT_DTMF        100 
#define AT_CMTI        101
#define AT_CMGR_TXT    102
#define AT_OK          200
#define AT_RING        201
#define AT_ERROR       202
#define AT_OtherRes    203
#define AT_TxtInput    204
#define AT_NONE        255

#define UInState_Discard            0x00
#define UInState_Result             0x10
#define UInState_Result_SecondChar  0x11
#define UInState_ResponseIdentifier 0x20
#define UInState_Response           0x40
#define UInState_Counter_Mask       0x0F
#define UInState_Type_Mask          0xF0
#define UInBuff_SIZE                  80

bit Ln2ndChar, NLCRareData;
char UInState, UInBuffPos, UInPrevChr, QoutCounter, UInCapCounter;
unsigned UInId;
volatile char UInBuff[UInBuff_SIZE], SMSSender;
volatile char resp[2], respLDI[2];
volatile unsigned CallTmr, AlertTmr, FlushTmr;
char USSDDCS;
volatile char Recip;

void SetResponse(char r, char ldi) {
  resp[1] = resp[0];
  resp[0] = r;
  respLDI[1] = respLDI[0];
  respLDI[0] = ldi;
}

char TranslateUInChar(char c) {
  if ('I' == c) return 0x1;
  if ('R' == c) return 0x2;
  if ('M' == c) return 0x3;
  if ('P' == c) return 0x4;
  if ('N' == c) return 0x5;
  if ('U' == c) return 0x6;
  if ('T' == c) return 0x7;
  if ('S' == c) return 0x8;
  if ('G' == c) return 0x9;
  //if ('' == c) return 0xa;
  if ('B' == c) return 0xb;
  if ('C' == c) return 0xc;
  if ('D' == c) return 0xd;
  if ('E' == c) return 0xe;
  if ('F' == c) return 0xf;
  return 0;
}

void Add2UInBuff(char d) {
  if (!d) UInCapCounter = 0;
  if (UInCapCounter >= UInBuff_SIZE) return;
  UInBuff[UInBuffPos] = d;
  UInBuffPos++;
  UInCapCounter++;
  if (UInBuff_SIZE == UInBuffPos) UInBuffPos = 0;
}

void interrupt() {
  char d, c;
  if (RBIF_bit) {
    RBIF_bit = 0b0;
  }
  if (RCIF_bit) {
    if (OERR_bit) {
      CREN_bit = 0b0;
      asm NOP;
      CREN_bit = 0b1;
      if (!CREN_bit) CREN_bit = 0b1;
      if (!SPEN_bit) RCSTA = 0b10010000;
    } else if (FERR_bit) {
      d = RCREG;
      if (!CREN_bit) CREN_bit = 0b1;
      if (!SPEN_bit) RCSTA = 0b10010000;
    } else {
      d = RCREG; 
      if ((13 == d) || (10 == d)) {
        c = 0;
        if ((Ln2ndChar) && (NLCRareData)) {  
          if ('0' == UInPrevChr) c = AT_OK;
          else if ('2' == UInPrevChr) c = AT_RING;
          else if ('4' == UInPrevChr) c = AT_ERROR;
          else if ((UInPrevChr > '0') && (UInPrevChr <= '9')) c = AT_OtherRes; 
          if (c) NLCRareData = 0b0;
        }
        if (NLCRareData)
          Add2UInBuff(d);
        else {
          if ((13 == UInPrevChr) || (10 == UInPrevChr)) return;
          if (UInState_Response == UInState) {
            UInState = UInState_Result;
            if (0xc415 == UInId) SetResponse(AT_CPIN, UInBuffPos);
            if (0xc2e9 == UInId) SetResponse(AT_CREG, UInBuffPos);
            if (0xd73f == UInId) SetResponse(AT_DTMF, UInBuffPos);
            if (0xc371 == UInId) SetResponse(AT_CMTI, UInBuffPos);
            if (0xc392 == UInId) {
              SetResponse(AT_CMGR, UInBuffPos);
              if (SMSSender < 10) Recip = SMSSender;
              UInState = UInState_Response;
              UInId = 1;
              NLCRareData = 0b1;
              Add2UInBuff(0);
            }
            if (1 == UInId) SetResponse(AT_CMGR_TXT, UInBuffPos);
            if (0xc4bf == UInId) SetResponse(AT_CPBF, UInBuffPos);
            if (0xc68d == UInId) SetResponse(AT_CUSD, UInBuffPos);
          } else
            UInState = UInState_Result;
          if (c) SetResponse(c, 0);
        }
      } else if (UInState_Discard != UInState) {
        if (UInState_Result == UInState) {
          if ('O' == d) UInState = UInState_Result_SecondChar;
          else if ('0' == d) SetResponse(AT_OK, 0);
          else if ('2' == d) SetResponse(AT_RING, 0);
          else if ('4' == d) SetResponse(AT_ERROR, 0);
          else if ((d > '0') && (d <= '9')) SetResponse(AT_OtherRes, 0);
          else if ('+' == d) { UInState = UInState_ResponseIdentifier; UInId = 0; } 
          else if ('>' == d) SetResponse(AT_TxtInput, 0);
          else UInState = UInState_Discard;
        } else if (UInState_Result_SecondChar == UInState) {
          if ('K' == d) SetResponse(AT_OK, 0); else UInState = UInState_Discard;
        } else if (UInState_ResponseIdentifier == (UInState & UInState_Type_Mask)) {
          c = UInState & UInState_Counter_Mask;
          if ((':' == UInPrevChr) && (' ' == d)) {
            QoutCounter = 0;
            SMSSender = 255;
            USSDDCS = 0;
            UInState = UInState_Response;
            Add2UInBuff(0);
          } else if ((':' != d) && (c < 4)) {
            UInState++;
            d = TranslateUInChar(d);
            UInId = (UInId << 4) + d;
            if (!d) UInState = UInState_Discard;
          } else if (':' != d)
            UInState = UInState_Discard;
        } else if (UInState_Response == UInState) {
          Add2UInBuff(d);
          if (('U' == d) && (5 == QoutCounter) && ('"' == UInPrevChr)) SMSSender = 254;
          if ((2 == QoutCounter) && (0xc68d == UInId) && (d >= '0') && (d <= '9')) USSDDCS = USSDDCS * 10 + (d - '0');
          if ('"' == d) {
            QoutCounter++;
            if ((6 == QoutCounter) && (254 == SMSSender)) SMSSender = UInPrevChr - '0';
            if (UInId != 1) NLCRareData = !NLCRareData;
          }
        }
      }
      Ln2ndChar = (((10 == UInPrevChr) || (13 == UInPrevChr)) && (10 != d) && (13 != d));
      UInPrevChr = d;
    }
  }
  if (TMR1IF_bit) { // every 0.1048576 second
    CallTmr++;
    AlertTmr++;
    TMR1IF_bit = 0b0;
  }
  if (TMR2IF_bit) { // every 0.0131072 second
    FlushTmr++;
    TMR2IF_bit = 0b0;
  }
}

void main() {
  bit u;
  char k, j, o, b, l, m, n;
  // setup PIC
  PORTA = 0;               // all pins low
  PORTB = 0;               // all pins low
  PORTC = 0;               // all pins low
  PORTE = 0;               // all pins low
  OPTION_REG = 0b11011111; // internal TMR0, max prescaler to WDT (~2.2 sec WDT reset period)
  ANSEL = 0;               // all pins digital
  ANSELH = 0;              // all pins digital
  TRISA = 0;               // all outputs, unused
  TRISB = 0b00110000;      // two lines input, others output
  TRISC = 0b10000001;      // RX input, others output
  TRISE = 0;               // all outputs, unused
  IOCB = 0b00110000;       // enable input lines interrupt on change
  BRG16_bit = 0b1;         // 16-bit baud rate generator  
  SPBRGH = 0x04;           // 1200 bps
  SPBRG = 0x11;            // 1200 bps
  TXSTA = 0b00100000;      // enable TX
  RCSTA = 0b10010000;      // enable serial port and reception
  T1CON = 0b00110000;      // 1:8 prescaler
  T2CON = 0b01111010;      // 16 for both scalers
  PIE1 = 0b00100011;       // enable UART recieve and timer1 and timer2 interrupts
  INTCON = 0b01000000;     // enable PortB pin change and peripheral interrupts
  // initializing
  resp[0] = AT_NONE;
  UInState = UInState_Result;
  UInBuffPos = 0;
  Ln2ndChar = 0b0;
  NLCRareData = 0b0;
  Recip = 0;
  GIE_bit = 0b1; 
  // main loop
  while (1) {
    // processing received serial data
    ...
    // serial errors check
    if (OERR_bit) {
      CREN_bit = 0b0;
      asm NOP;
      CREN_bit = 0b1;
    }
    if (FERR_bit) j = RCREG;
    if (!CREN_bit) CREN_bit = 0b1;
    if (!SPEN_bit) RCSTA = 0b10010000;
    // watchdog reset
    asm CLRWDT;
  }
}
\$\endgroup\$
  • \$\begingroup\$ Missing volatile qualifiers, stack overflow, buffer overruns, floating inputs, etc. etc. It might be useful to show a schematic and at least some relevant portions of code. \$\endgroup\$ – Roger Rowland Mar 11 '17 at 16:34
  • \$\begingroup\$ I have 3 input pins that all of them have pull-up resistors. I have volatile before each variable that is used in both threads. It works before the code growing up so I don't think it is the serial FIFO overrun. \$\endgroup\$ – Vahid Mar 11 '17 at 20:48
  • \$\begingroup\$ The mikroC compiler always warns stack overflows, but it did not said that. \$\endgroup\$ – Vahid Mar 11 '17 at 20:59
  • \$\begingroup\$ @vahid - Although someone might spot your problem(s), I don't think that this question, as currently presented, is a good "fit" for this site. Effectively, you're asking for debugging help on a "non-minimal" amount of sparsely-commented code, with multiple symptoms. IMHO it would be much more efficient to have someone local to you, or who can connect to your machine, discuss and agree a debugging process, and then work through it with you, in order to teach you. When you ask for remote help, it can lead to a long series of Q&A, just to get enough information to start the debugging process. \$\endgroup\$ – SamGibson Mar 12 '17 at 2:54
  • 1
    \$\begingroup\$ "UInBuff[UInBuff_SIZE] from 80 to 40 fixed the problem" - perhaps a bank select bit is getting corrupted, causing writes to the wrong bank. Examine the assembly code listing for the 80 byte version - what memory locations is UInBuff set to? Would some of them correspond to RCSTA, T2CON etc. if switched to bank 0? \$\endgroup\$ – Bruce Abbott Mar 16 '17 at 0:00
1
\$\begingroup\$

Thanks to Bruce Abbott comment, I found the problem, I told him to write the answer but he did not so I answered it myself.
When using arrays in PICs the compiler uses INDF and FSR registers to access array element and if the array is stored in banks 3 or 4 the IRP bit must be set before accessing it (the thing that MikroC compiler does not itself and it must be done in the code). All I did was arranging small arrays in the first two banks using absolute keyword and arranging my large array in the 3rd bank and setting IRP bit before accessing my array and clearing IRP bit after that.

\$\endgroup\$

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.