CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

24FJ128GA010 - UART2 Tx problem

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
tonymcc



Joined: 27 Jul 2010
Posts: 15
Location: Northampton UK Home of the 'Big Sheds'

View user's profile Send private message

24FJ128GA010 - UART2 Tx problem
PostPosted: Thu Jul 21, 2011 3:38 am     Reply with quote

Hi,
I am using the above processor under IDE V4.119 with ICD-U64 V2.88. I am building a cut-down Modbus interface using UART2 and I am having difficulties in getting the Tx interrupt to operate - Rx is fine. I am happy that my 'surrounding' code is ok as I can get things operating fine using a direct fputc command in place of loading a Tx buffer. The relevant lumps of code are:
Code:
#INT_TBE2                     
void tx_isr()                 //interrupt fires when tx buffer is empty
{  if(t_next_in!=t_next_out)                    //is there still data to send
   {  fputc((tx_buffer[t_next_out] + 48),hart); //send next data
      t_next_out=(t_next_out+1) % BUFFER_SIZE;  //increment data out pointer, rolls round
   }
   else
      disable_interrupts(INT_TBE2);             //last data, stop sending
}

void txUART(unsigned int8 c)           //send data, in nibbles, to tx buffer
{  unsigned int8 restart;              //flag, true if buffer empty
   unsigned int8 ni;
   restart=t_next_in==t_next_out;      //load flag
   tx_buffer[t_next_in] = c;           //load nibble to buffer           
   ni = (t_next_in+1) % BUFFER_SIZE;   //increment data in point, rolls around
   while(ni==t_next_out);              //wait if buffer full
   t_next_in=ni;     
   if(restart)                         //if buffer is empty
      enable_interrupts(INT_TBE2);     //restart interrupt to send this data
}


I enable interrupts on entry to my main. I have checked that my tx_buffer is filling nicely but nothing appears from the UART. Any help would be appreciated.
Cheers,
Tony
Charlie U



Joined: 09 Sep 2003
Posts: 183
Location: Somewhere under water in the Great Lakes

View user's profile Send private message

PostPosted: Thu Jul 21, 2011 8:53 am     Reply with quote

Hi Tony,

Another important bit of code is how you are setting up the uart and setting the #pin_select. Could you show us these as well?

Charlie
tonymcc



Joined: 27 Jul 2010
Posts: 15
Location: Northampton UK Home of the 'Big Sheds'

View user's profile Send private message

24FJ128GA010 - UART2 Tx problem - response
PostPosted: Thu Jul 21, 2011 9:31 am     Reply with quote

Hi Charlie,

No problem. Code follows:
Code:
#include <24FJ128GA010.h>
//#device ICD=TRUE

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES WPOSTS1                  //Watch Dog Timer PostScalar 1:1
#FUSES NOWINDIS                 //Watch Dog Timer in Window mode
#FUSES ICSP1                    //ICD uses PGC1/PGD1 pins
#FUSES NOCOE                    //Device will reset into operational mode
#FUSES DEBUG                    //Debug mode for use with ICD
#FUSES NOWRT                    //Program memory not write protected
#FUSES NOPROTECT                //Code not protected from reading
#FUSES NOJTAG                   //JTAG disabled
#FUSES NOPR                     //Pimary oscillaotr disabled
#FUSES NOOSCIO                  //OSC2 is clock output
#FUSES NOCKSFSM                 //Clock Switching is disabled, fail Safe clock monitor is disabled
#FUSES FRC                      //Internal Fast RC Oscillator
#FUSES NOIESO                   //Internal External Switch Over mode disabled

#use delay(clock=8000000)
#use RS232 (UART2, baud=9600, stream=hart)

I believe that the pin assignments come automatically by selecting UART2. UART2 operates happily using fputc(data,hart) in normal program run, but no luck in interrupt.
Thanks
Tony
Charlie U



Joined: 09 Sep 2003
Posts: 183
Location: Somewhere under water in the Great Lakes

View user's profile Send private message

PostPosted: Thu Jul 21, 2011 8:12 pm     Reply with quote

Hi Tony,

I'm not certain how you managed to get the uart to work without declaring a pin. I don't recall that there is a default pin assigned to the uarts on this part. We are using the PIC24FJ256GA106 and 110 versions and both need to have the uarts specifically assigned to a pin.

Which pins are you using for the uarts rx and tx?

Charlie
tonymcc



Joined: 27 Jul 2010
Posts: 15
Location: Northampton UK Home of the 'Big Sheds'

View user's profile Send private message

PostPosted: Fri Jul 22, 2011 2:24 am     Reply with quote

Hi Charlie,

Thanks for the response. I am using pins F4=Rx F5=Tx, the designated peripheral pins for UART2. The data sheet, in Section 9 - I/O Ports states
Quote:
A parallel I/O port that shares a pin with a peripheral is,
in general, subservient to the peripheral
Anyway, I tried the pin assignment in the #use RS232 statement, but with no success. As I stated before, UART2 Tx is fine using fputc in 'straight' code. I also know that my Tx buffer is building satisfactorily, it's just not being emptied during a Tx interrupt.

Because my message traffic is small I could probably get away with Tx in straight code but it isn't very elegant and I would really like to know why the Tx interrupt fails to work.

Cheers,
Tony
PS what goes on under water in the Great Lakes?
FvM



Joined: 27 Aug 2008
Posts: 2337
Location: Germany

View user's profile Send private message

PostPosted: Fri Jul 22, 2011 7:33 am     Reply with quote

Quote:
I'm not certain how you managed to get the uart to work without declaring a pin. I don't recall that there is a default pin assigned to the uarts on this part. We are using the PIC24FJ256GA106 and 110 versions and both need to have the uarts specifically assigned to a pin.

24FxxxGA010 uses fixed UART pins.
Quote:
I also know that my Tx buffer is building satisfactorily, it's just not being emptied during a Tx interrupt.

I won't guess about the reason without seeing a complete code example.
Charlie U



Joined: 09 Sep 2003
Posts: 183
Location: Somewhere under water in the Great Lakes

View user's profile Send private message

PostPosted: Fri Jul 22, 2011 8:35 am     Reply with quote

To all,

"Whoopsy!!"

If you look closely I have been referring to the PIC24FJ256GA1xx parts, not the PIC24FJ128GA0xx parts. There is a major difference in that the pins of the 1xx parts are configurable while the 0xx parts are not. My mistake.

What goes on under water in the Great Lakes is a scuba diving.

http://www.wrecksandreefs.com/dunderbe.htm
tonymcc



Joined: 27 Jul 2010
Posts: 15
Location: Northampton UK Home of the 'Big Sheds'

View user's profile Send private message

PostPosted: Fri Jul 22, 2011 9:37 am     Reply with quote

Hi Charlie and FvM,

I am sorry about the code fragments and I realise how difficult it is for someone unfamiliar with the code to make a comment. It will not happen again and I shall only include complete, compilable code. Consider my knuckles rapped!

I have stuck together the relevant bits from my main application and proved that it runs, and fails, as I have described above.
Code:
/*
--------------------------------------------------------------------------------------------
Developer      -  Tony McCormick   
Start Date     -  22nd July 2011
HEADER CODE    -                             
PROCESSOR      -  24FJ128GA010
Description    -  Main file - serial routine, debug format
--------------------------------------------------------------------------------------------
Development history for main.c

--------------------------------------------------------------------------------------------
Issue P1       -  Started 22nd July 2011

--------------------------------------------------------------------------------------------
Requirements   - 

*/

//------------------------------------------------------------------------------------------
#include <24FJ128GA010.h>
#device ICD=TRUE

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES WPOSTS1                  //Watch Dog Timer PostScalar 1:1
#FUSES NOWINDIS                 //Watch Dog Timer in Window mode
#FUSES ICSP1                    //ICD uses PGC1/PGD1 pins
#FUSES NOCOE                    //Device will reset into operational mode
#FUSES DEBUG                    //Debug mode for use with ICD
#FUSES NOWRT                    //Program memory not write protected
#FUSES NOPROTECT                //Code not protected from reading
#FUSES NOJTAG                   //JTAG disabled
#FUSES NOPR                     //Pimary oscillaotr disabled
#FUSES NOOSCIO                  //OSC2 is clock output
#FUSES NOCKSFSM                 //Clock Switching is disabled, fail Safe clock monitor is disabled
#FUSES FRC                      //Internal Fast RC Oscillator
#FUSES NOIESO                   //Internal External Switch Over mode disabled

#use delay(clock=8000000)
#use RS232 (UART2, baud=9600, xmit=PIN_F5, rcv=PIN_F4, stream=hart)

#define BUFFER_SIZE     0x40  //general size of receive and transmit buffers
#define MB_03           0x03  //Command 03, read registers
#define MB_16           0x10  //Command 16, write registers
#define bad_comm        0x01  //Error message, bad command code
#define bad_address     0x02  //Error message, bad address or address range
#define bad_reg_num     0x03  //Error message, bad number of registers
#define bad_crc         0x07  //Error message, bad internal processing of registers

//Variables associated with HART communciation

unsigned int8  ext_buffer[BUFFER_SIZE];      //raw ASCII receive buffer
unsigned int8  tx_buffer[BUFFER_SIZE];       //buffer for built response
unsigned int16 buffer_dynamic[BUFFER_SIZE];  //dynamic variable buffer
unsigned int16 received_crc;                 //received CRC
unsigned int8  new_command;                  //received command
unsigned int16 new_address;                  //received address
unsigned int16 new_registers;                //number of registers received
unsigned int8  new_bytes;                    //number of bytest received
unsigned int16 tx_crc;                       //calcualted crc for tx message
unsigned int16 norm_address;                 //address normalised to either 20000 or 30000
unsigned int8  start_bit;                    //dummy to receive start bit
unsigned int8  message_received;             //true if new message

unsigned int8  t_next_out;                   //tx buffer out pointer
unsigned int8  t_next_in;                    //tx buffer in pointer
unsigned int8  r_next_out;                   //rx buffer out pointer
unsigned int8  r_next_in;                    //rx buffer in pointer
unsigned int8  tx_index;                     //tx buffer index
unsigned int8  temp_data;
unsigned int8  temp_byte;

unsigned int8  soft_switch;                  //various hart commands as 8 bit switches
unsigned int8  hart_write_enable;            //true to allow writes from hart

#INT_RDA2
void rx_isr()                 //looks at serial port and interrupts on data available
{  unsigned int8 t;
   ext_buffer[r_next_in]=(fgetc(hart)-48);   //moves data into next available posn in buffer
   t = r_next_in;                            //saves current buffer data in pointer
   r_next_in=(r_next_in+1) % BUFFER_SIZE;    //increments data in pointer, rolls roound
   if(r_next_in==r_next_out)                 //check if buffer full
     r_next_in=t;                            // Buffer full !!
}

#INT_TBE2                     
void tx_isr()                 //interrupt fires when tx buffer is empty
{  if(t_next_in!=t_next_out)                    //is there still data to send
   {  fputc((tx_buffer[t_next_out] + 48),hart); //send next data
      t_next_out=(t_next_out+1) % BUFFER_SIZE;  //increment data out pointer, rolls round
   }
   else
      disable_interrupts(INT_TBE2);             //last data, stop sending
}

void clear_ext_buffer(void)         //clear receive ASCII buffer
{  unsigned int8 i;
   for(i=0;i<BUFFER_SIZE;i++)
      ext_buffer[i] = 0;
}

void clear_buffer_dynamic(void)     //clear dyanmic variable buffer
{  unsigned int8 i;
   for(i=0;i<BUFFER_SIZE;i++)
      buffer_dynamic[i] = 0;
}

void clear_tx_buffer(void)          //clear tx byte buffer
{  unsigned int8 i;
   for(i=0;i<BUFFER_SIZE;i++)
      tx_buffer[i] = 0;
}

void clear_last_data(void)                //clear last address and data length
{  new_command = 0;
   new_address = 0;
   new_registers = 0;
   new_bytes = 0;
   received_crc = 0;
   tx_crc = 0;
   t_next_out = 0;
   t_next_in = 0;
   r_next_out = 0;
   r_next_in = 0;   
   tx_index = 0;
   clear_ext_buffer();
   clear_tx_buffer();
   message_received = 0;
}   

void load_buffer_dynamic(void)      //test word
{  buffer_dynamic[0] = 0x0000;      //first configuration parameters
   buffer_dynamic[1] = 0x1001;
   buffer_dynamic[2] = 0x2002;
   buffer_dynamic[3] = 0x3003;
   buffer_dynamic[4] = 0x4004;
   buffer_dynamic[5] = 0x5005;
   buffer_dynamic[6] = 0x6006;   
   buffer_dynamic[7] = 0x7007;
   buffer_dynamic[8] = 0x0808;
   buffer_dynamic[9] = 0x0909;
   buffer_dynamic[10] = 0x0A0A;
   buffer_dynamic[11] = 0x0B0B;
   buffer_dynamic[12] = 0x0C0C;
   buffer_dynamic[13] = 0x0D0D;
   buffer_dynamic[14] = 0x0E0E;   
   buffer_dynamic[15] = 0x0F0F;
   buffer_dynamic[16] = 0x1010;
   buffer_dynamic[17] = 0x1111;   
   buffer_dynamic[18] = 0x1212;
   buffer_dynamic[19] = 0x1313;

   buffer_dynamic[30] = 0x3030;     //then dynamic status bytes
   buffer_dynamic[31] = 0x3131;
   buffer_dynamic[32] = 0x3232;
   buffer_dynamic[33] = 0x3333;   
   buffer_dynamic[34] = 0x3434;
   
   buffer_dynamic[40] = 1234;       //then dynamic variables
   buffer_dynamic[41] = 5678;
   buffer_dynamic[42] = 8765;
   buffer_dynamic[43] = 4321;
   buffer_dynamic[44] = 0xF5FA;
   
   clear_last_data();
}

#define bkbhit (r_next_in != r_next_out)

unsigned int8 get_rx_nibble(void)
{  unsigned int8 c;
   while(!bkbhit) ;
   c = ext_buffer[r_next_out];
   r_next_out = (r_next_out+1) % BUFFER_SIZE;
   return(c);
}

unsigned int8 make_rx_byte(void)
{  unsigned int8 b;   
   unsigned int8 msb;
   unsigned int8 lsb;
   msb = get_rx_nibble();
   if(msb>9)                           //hex adjust if required
      msb = msb - 7;
   lsb = get_rx_nibble();          //get lower nibble
   if(lsb>9)                           //hex adjust if required
      lsb = lsb - 7;     
   b = msb<<4 | lsb;
   return b;
}

unsigned int16 make_word(void)
{  unsigned int16 w;
   w =(unsigned int16)make_rx_byte()<<8 | make_rx_byte();
   return w;
}
void get_norm_address(void)
{  if(new_address < 30000)                         //get local buffer offset from address
      norm_address = (new_address - 20000);        //normalise address of engineering/config data
   else
      norm_address = (new_address - 30000) + 40;   //normalise address of dynamic variables
}

void update_local_to_hart(void)                 //load latest modified buffer from HART

}

void get_message(void)                             //translate message from Rx buffer
{  unsigned int8 i;
   if((r_next_out == 0) && bkbhit)                 //wait for first character
   {  start_bit = make_rx_byte();                  //get start bit
      new_command = make_rx_byte();                //get new command 3 or 16
      new_address = make_word();                   //get data start address
      new_registers = make_word();                 //get number of registers to be processed
      get_norm_address();                          //get local buffer offset for address
      if(new_command == MB_16)                     //check for write command
      {  if(bit_test(soft_switch,hart_write_enable))           //look for write enabled but not remote setpoint
         {  new_bytes = make_rx_byte();                        //get bytes to be transferred
            for(i=0; i<new_registers;++i)                      //setup to write new data to buffer               
               buffer_dynamic[i + norm_address] = make_word(); //write to local buffer
            update_local_to_hart();                //pull all data from buffer to variables
         }
      }     
      else
         new_bytes = 0;                            //3 command so no byte count taken
      received_crc = make_word();                  //get received CRC
      message_received = 1;                        //set a message received flag
   }   
}

void txUART(unsigned int8 c)           //send data, in nibbles, to tx buffer
{  unsigned int8 restart;              //flag, true if buffer empty
   unsigned int8 ni;
   restart=t_next_in==t_next_out;      //load flag
   tx_buffer[t_next_in] = c;           //load nibble to buffer           
   ni = (t_next_in+1) % BUFFER_SIZE;   //increment data in point, rolls around
   while(ni==t_next_out);              //wait if buffer full
   t_next_in=ni;     
   if(restart)                         //if buffer is empty
      enable_interrupts(INT_TBE2);     //restart interrupt to send this data

// fputc((c + 48),hart);
}

void split_byte(unsigned int8 t_data)     //split a data byte and send or buffer
{  temp_data = (t_data & 0xF0)>>4;        //break out and shift upper nibble
   if(temp_data > 9)                      //adjust to hex
      temp_data = temp_data + 7;          //adjust as appropriate
   txUART(temp_data);                     //send or buffer data
   temp_data = t_data & 0x0F;             //break out lower nibble
   if(temp_data > 9)                      //adjust to hex
      temp_data = temp_data + 7;          //adjust as appropriate
   txUART(temp_data);                     //send or buffer data
}

void send_16_response(void)               //respond to an 16 command
{  tx_index = 0;                          //set buffer at zero
   split_byte(1);                         //load start bit
   split_byte(new_command);               //command reflected back
   temp_byte = make8(new_address,1);      //get upper byte of address
   split_byte(temp_byte);                 //send as two nibbles
   temp_byte = make8(new_address,0);      //get lower byte of address
   split_byte(temp_byte);                 //send as two nibbles
   temp_byte = make8(new_registers,1);    //get upper byte of registers written
   split_byte(temp_byte);                 //send as two nibbles
   temp_byte = make8(new_registers,0);    //get lower byte of registers written
   split_byte(temp_byte);                 //send as two nibbles
   tx_crc = 1234;                         //dummy crc
   temp_byte = make8(tx_crc,1);           //get upper byte
   split_byte(temp_byte);                 //send as two nibbles
   temp_byte = make8(tx_crc,0);           //get lower byte
   split_byte(temp_byte);                 //send as two nibbles
   clear_last_data();
}   

void send_03_response(void)               //respond to an 03 command with data
{  unsigned int8 i;
   split_byte(1);                         //load start bit
   split_byte(new_command);               //command reflected back
   split_byte(new_registers * 2);         //response byte count
   get_norm_address();                    //get local buffer offset from Modbus address
   for(i=0;i<(new_registers);i++)                  //read from local dynamic buffer
   {  temp_byte = make8(buffer_dynamic[i + norm_address],1);   //send msb first
      split_byte(temp_byte);
      temp_byte = make8(buffer_dynamic[i + norm_address],0);   //then lsb
      split_byte(temp_byte);
   }
   tx_crc = 1234;                         //dummy crc
   temp_byte = make8(tx_crc,1);           //get upper byte
   split_byte(temp_byte);                 //send as two nibbles
   temp_byte = make8(tx_crc,0);           //get lower byte
   split_byte(temp_byte);                 //send as two nibbles
   txUART('\r'-48);                       //CR
   txUART ('\n'-48);                      //LF
   while(t_next_in!=t_next_out);    //holds buffer and gives time for interrupt
   clear_last_data();
}

void handle_serial(void)            //get message and respond
{  get_message();                   //get new request
   if(message_received)
   {  if(new_command == 3)             //filter for response
         send_03_response();           //process read request
      if(new_command == 16)
         send_16_response();           //process write request
   }
}     

void io_initialisation(void)
{  load_buffer_dynamic();
   enable_interrupts(INT_RDA2);
   enable_interrupts(INT_RDA2);
   enable_interrupts(INTR_GLOBAL);   
}

void main()
{  io_initialisation();
   do
   {  handle_serial();
   }
   while(true);
}
 
   
//---------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------


I am firing a message 0103753000040A5E from Hyperterminal to trigger an expected response of 01030804D2162E223D10E104D2 i.e. a Modbus-ish request for 4 registers and then the registers returned with dummy data from load_buffer_dynamic().

You can see in txUART that I have a commented out fputc statement that, when replacing the buffer filling code, will transmit satisfactorily. Conversely, if I replace the fputc statement with the buffer filling code and check the tx_buffer, I find it correctly filled but nothing transmitted. I do note that t_next_out = 1 and t_next_in = 0x1C at this point.

I do hope that it is clear what I am trying to achieve. Do have a good weekend, especially the scuba diving, although it sounds cold in the Great Lakes.
Cheers,
Tony
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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