|
|
View previous topic :: View next topic |
Author |
Message |
tonymcc
Joined: 27 Jul 2010 Posts: 15 Location: Northampton UK Home of the 'Big Sheds'
|
24FJ128GA010 - UART2 Tx problem |
Posted: Thu Jul 21, 2011 3:38 am |
|
|
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
|
|
Posted: Thu Jul 21, 2011 8:53 am |
|
|
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'
|
24FJ128GA010 - UART2 Tx problem - response |
Posted: Thu Jul 21, 2011 9:31 am |
|
|
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
|
|
Posted: Thu Jul 21, 2011 8:12 pm |
|
|
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'
|
|
Posted: Fri Jul 22, 2011 2:24 am |
|
|
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
|
|
Posted: Fri Jul 22, 2011 7:33 am |
|
|
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
|
|
Posted: Fri Jul 22, 2011 8:35 am |
|
|
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'
|
|
Posted: Fri Jul 22, 2011 9:37 am |
|
|
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 |
|
|
|
|
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
|