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 CCS Technical Support

sprintf/fprintf behaviour
Goto page Previous  1, 2, 3, 4  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
newguy



Joined: 24 Jun 2004
Posts: 1934

View user's profile Send private message

PostPosted: Thu Jun 30, 2016 12:20 pm     Reply with quote

A soft UART is a "bit banged" pseudo-UART. Transmits are handled basically by setting the transmit line, waiting the bit time, then setting/resetting as required. Reads are handled by polling the receive line at least as often as the expected bit time.

A hardware UART handles all that for you. Need to transmit a byte? Hand it to the UART and it will automatically "clock it out" for you. It also continually listens to the receive line without any need for you to "micro manage" the transaction. If it receives a valid byte (something it recognized as valid, anyway), it will put it in the queue. If you have interrupts enabled, the receive interrupt (receive data available, or INT_RDA) will "fire". If you don't have interrupts enabled, you need to periodically check to see if a byte is waiting for you in the queue.

Now think about this, very carefully. A soft UART requires very strict timing - the processor basically needs to focus its attention on the soft UART leaving very little time for anything else. If that's all you're asking the processor to do, chances are it will manage to keep up with the demands of the soft UART and not miss characters (coming or going). If, however, you now start enabling other interrupts, as you have done, what do you think will happen if your soft UART, in the middle of transmitting a character, gets "pulled aside" by an interrupt which takes a relatively "long" time? What happened to the bit-banged timing of the transmitted sequence? How will the device on the other end of the line like/handle/interpret that pause? Same goes for data the soft UART is receiving.
Ttelmah



Joined: 11 Mar 2010
Posts: 20061

View user's profile Send private message

PostPosted: Thu Jun 30, 2016 12:32 pm     Reply with quote

There was a fault on many V4 compilers that they accidentally disabled the global interrupt inside printf. Caused a huge number of problems, but would allow this to work. I don't remember V3 doing this, but it is possible he had a version that did.
You can get a better version of the same behaviour, by adding the command 'DISABLE_INTS' to each software UART #USE RS232.
It's awful programming. At least one of the UART's could be using the hardware (it's there to do the job better)....
I assume he has other interrupts (with the single ones shown, using 'high' would be pointless, and this is short enough that it probably would not interfere too badly with the serial).
Using the software UART's, in any code that does work, Interrupts, will be being missed, unless the frequency is very low.

The whole thing is an abortion.
BLL



Joined: 11 Nov 2006
Posts: 181
Location: Birmingham, UK

View user's profile Send private message

fprintf etc
PostPosted: Thu Jun 30, 2016 12:42 pm     Reply with quote

Hi Jeremiah, Thanks for the reply. I have a knowledge problem here! Ever since I started with PICs (the ones with the uv window on top), I configured serial ports as in my current program. I was not aware of the HW UART or how to use it! I have googled and searched the CCS user forums and info is sparse to say the least! The help file also says little. It seems, if I have it right, that I need to add uart1 to the #use_rs232 statement, to tell it to use the HW uart? This I have done and it has made absolutely NO difference, so I am still in the dark! I am only a hobbyist PIC programmer and don't have the knowledge some obviously have, hence my pleas on the forum. My program needs 3 serial ports:
One for the CS5490 - this is working fine using RC0 xmit from 5490, RC1 for receive and RB0 for pulse output.
One to send/receive data from the connected Raspberry Pi at 9600Bd. RasPi requests data once a second and PIC sends a comma delimited string.
Finally, the pain, the LCD.
Now from what I glean, the display does most work so it should have the h/w usart?
What, other than adding uart1 to the
Code:
#use rs232(baud=19200,uart1,xmit=PIN_C6,stream=lcd)

do I need to do please?
Ttelmah



Joined: 11 Mar 2010
Posts: 20061

View user's profile Send private message

PostPosted: Thu Jun 30, 2016 1:05 pm     Reply with quote

To use the hardware UART, you need to use the pins on the chip that have this.
Using 'UART1', will automatically use these pins.

No, if you use the DISABLE_INTS option on the software UARTs, the ones that most need the hardware, are those receiving, and taking the longest time. Probably the Rasperry Pi?.
Ttelmah



Joined: 11 Mar 2010
Posts: 20061

View user's profile Send private message

PostPosted: Thu Jun 30, 2016 1:29 pm     Reply with quote

As a further comment, increase the baud rates if you can.

The CS chip will happily run at something like 57600bps. Using 'disable_ints' at this rate, will only disable interrupts for 0.1736mSec at a time
newguy



Joined: 24 Jun 2004
Posts: 1934

View user's profile Send private message

Re: fprintf etc
PostPosted: Thu Jun 30, 2016 1:44 pm     Reply with quote

BLL wrote:
What, other than adding uart1 to the
Code:
#use rs232(baud=19200,uart1,xmit=PIN_C6,stream=lcd)

do I need to do please?


I'm afraid that what you want to do would be a challenge even for the most experienced people here. You need to monitor 3 different serial links. Even if you properly moved one of them to the hardware UART on your 18LF2620, that leaves two soft UARTs.

I seem to remember a post in the code library from a very long time ago where someone did implement a 3 UART program (1 hardware UART, 2 soft UARTs) on something like an 18F452, but I can't find the post. Grab a coffee (or a beer) and start to look through the library. I definitely remember the post, but as I said, I just can't find it at the moment.
BLL



Joined: 11 Nov 2006
Posts: 181
Location: Birmingham, UK

View user's profile Send private message

fprintf etc
PostPosted: Thu Jun 30, 2016 2:04 pm     Reply with quote

Hi both
Thanks Ttelmah, I'll look into this. However, the PIC isn't even coded in my test program and the LCD is completely happy with the 5490 running. It's as soon as I try and use the RB0 interrupt that everything goes awry. Unless I comment out the isr, the LCD is a mess.

As for newguy, it all works fine already if I compile with v3.248, so it's possible! It's only in trying to update to v5 that the wheel has come off, BADLY!
Seems like reverse progress to me!
Worst come to the worst, it's the bin for v5 and I'll just have to stay old-fashioned!

Brian
jeremiah



Joined: 20 Jul 2010
Posts: 1411

View user's profile Send private message

Re: fprintf etc
PostPosted: Thu Jun 30, 2016 3:15 pm     Reply with quote

BLL wrote:

What, other than adding uart1 to the
Code:
#use rs232(baud=19200,uart1,xmit=PIN_C6,stream=lcd)

do I need to do please?


Generally to specify a hardware uart you do something like:
Code:
#use rs232(baud=19200,uart1,stream=lcd,errors)


and software uart is more like:

Code:
#use rs232(baud=9600,xmit=PIN_C0,rcv=PIN_C1,stream=cs5490fast,DISABLE_INTS)


notice how no pins are specified for hardware UART and errors is used while for software you specify the pins and use DISABLE_INTS.

As for which UART to use as hardware. For me it is whichever one you expect to use the receive pin on. Receiving in software uarts can be very tricky to get right. They may appear to work correctly, but it is very easy to mess up the receive as you make other changes and it limits what you can do in the code.

The other challenge: to use the software UARTS you have to disable ints during use. This can make the hardware UARTS fail to receive as the buffer overflows while you are transmitting out on one of the software UARTS.


Last edited by jeremiah on Fri Jul 01, 2016 7:17 am; edited 1 time in total
Ttelmah



Joined: 11 Mar 2010
Posts: 20061

View user's profile Send private message

PostPosted: Fri Jul 01, 2016 1:28 am     Reply with quote

Good follow up. Some more to this:

First of all, we can ignore the 600baud stream. This is only used initially to configure the CS5490, so it'd be perfectly reasonable to only enable interrupts at all, after this has been configured.

Then the key points Jeremiah is making are about receive, and time.
With a software UART, the code has to be sitting 'waiting' for bytes to arrive. There is no receive buffer, or receipt of data if it arrives 'unprompted'.
Hence we need to be looking at using the hardware for any streams that may deliver data 'unprompted'.

So the CS5490, makes good sense for a device to use a software serial, since it will only send data 'when asked'. If this is made to have each byte transaction run faster than anything happening 'unprompted' (so a baud rate faster than 9600bps), then the hardware buffer will be able to cope with any data arriving on the 'unprompted' stream, while this is occurring. The baud rate this uses is programmed in the initial setup, and can go up to 500Kbaud. The value loaded into the BRG bits in the configuration determine this, with the formula:

BRG=baud*(524288/Mclk)

So assuming the standard 4.096MHz Mclk, a baud rate of 19200bps, would just involve loading the BRG with:

19200*(524288/4096000) = 2457

Similarly, the LCD, only requires transmit, and again this can therefore be easily handler by the software, especially given that the data rate being used is already twice that involved in the PI link.

Then add an interrupt ring buffer to the hardware UART (PI) - look at ex_sisr.c, and anything that arrives while the LCD and CS5490 are being handled, will be buffered in the gaps between the individual characters.
BLL



Joined: 11 Nov 2006
Posts: 181
Location: Birmingham, UK

View user's profile Send private message

fprintf etc
PostPosted: Fri Jul 01, 2016 2:41 am     Reply with quote

Hi all, Thanks again for the replies. I have now increased the CS5490 to 57600Bd from 9600. As I suspected, it has made not one jot of difference.
Next, I removed the input to RB0, which gets pulses from the 5490's DO output. This did not cure the display problem, even though now the isr should not be called and should therefore have no effect. However, if I comment out the isr code, the display is fine! Why? This suggests that this function is being called even though no interrupt has been requested. This surely suggests that the PIC is polling, not acting on interrupts?
If the problem with the display is lost characters, as it appears, then I am surprised that the wrong display is ALWAYS the same with the SAME characters being lost. I would have expected changing lost characters, but this is not so.
Also, if disable_interrupts does as it says on the tin, the isr should not surely be called whilst the display is being written to?
The PIC is a red herring at the moment as it isn't coded in my test program yet.
So, I still don't understand fully what is happening.

My head hurts!!

Brian
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Fri Jul 01, 2016 6:45 am     Reply with quote

Quote:
Next, I removed the input to RB0, which gets pulses from the 5490's DO output.

After removing the connection between RB0 and the CS5490, did you put
a pull-up resistor on pin RB0 ? Or is it left unconnected ?
BLL



Joined: 11 Nov 2006
Posts: 181
Location: Birmingham, UK

View user's profile Send private message

fprintf etc
PostPosted: Fri Jul 01, 2016 7:50 am     Reply with quote

Hi,
I left it unconnected! I'll try again with it grounded.

I have also discovered that in the code at the start of the loop, the display is OK provided I rem out the
Code:
 lidiv = ldiv(used_today, 1000);

This is mad!

Brian
jeremiah



Joined: 20 Jul 2010
Posts: 1411

View user's profile Send private message

PostPosted: Fri Jul 01, 2016 11:34 am     Reply with quote

That's probably because taking it out changes "when" the interrupt occurs relative to the display routine. Before the interrupt was happening at the same time as the display routine. With the (slow) division operation gone, they now happen at different times.

EDIT:

We've discussed a lot of things and you've made some various changes. Can we get a point of reference? From your earlier post you had a point where leaving in an interrupt (even with the hardware disconnect) resulted in the display being wonky but when you commented out the interrupt it worked fine. Can you post the "working" version of that post in compilable form. Leave in the interrupt code (still commented out though).
BLL



Joined: 11 Nov 2006
Posts: 181
Location: Birmingham, UK

View user's profile Send private message

fprintf etc
PostPosted: Fri Jul 01, 2016 1:08 pm     Reply with quote

Hi all, The whole thing is on a knife edge. If I rem out parts of main and leave in the isr, it's fine. If I rem out the isr, it will work with all in main!
Changing the LCD to the h/w uart made no difference; upping the 5490's baud rate made no difference!" It's crazy.
The following code works as shown:

Code:
#include <TestLCD.h>
#include <DS3232.c>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <internal_eeprom.c>

#define SLOW 0
#define FAST 1
#define Send_to_5490(x) if(baud == SLOW) fputc(x, cs5490slow);\
                        else fputc(x, cs5490fast)
                       
void clear_eeprom(boolean);
void CS5490Init();
void write_register(int32 value, int8 page, int8 regnum);
int32 read_register(int8 page, int8 regnum);
int8 from_chip();
float round(float);
char VAW[18]; //holds V, I & W, ready for sending
int8 pulse = 0;
//used today in Wh
int16 used_today;
int1 baud = SLOW;
float value;
int16 days_elapsed = 0;
float total_units = 0, units_charged = 0;
//if this function is remmed out, the LCD is fine but with it,
//even if it is empty, it completely ruins the LCD!

//reads meter pulses (each pulse is 1 Watt-hour)
#int_ext HIGH //RB0 interrupt
void ext_isr()
{
 pulse++;
}

//---------------------------------------------------------------------------
void main()
{
char const dayOfWeek[8][4] =
 {
  "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
 };//days of week, 0 = Sun

//used for quot & remd of used_today;
ldiv_t lidiv;
byte year, month, day, dow, hour, minute, second;
int32 VAC = 0, value = 0;
float IAC = 0, PAC = 0;
pulse = 0;
setup_spi(false);
setup_CCP1(CCP_OFF);
setup_CCP2(CCP_OFF);
setup_timer_0(RTCC_INTERNAL);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
setup_timer_3(T3_DISABLED);
setup_comparator(NC_NC_NC_NC);
setup_vref(false);
setup_low_volt_detect(false);
setup_uart(19200);
//pulse edge for EPG (RB0) (pulses from CS5490)
ext_int_edge(H_TO_L);
//if restarted during the day, load Wh used so far
//used_today = (1000 * read_int16_eeprom(0)) + (10 * read_int16_eeprom(2));
//line above temporarily replaced as after programming, don't know what eeprom
//"data" will be present!
write_int16_eeprom(0, 0L);
write_int16_eeprom(2, 0L);

DS3232init();
delay_ms(300);
//setup serial LCD
fputs("?S0", lcd);   //no display screen on boot
fputs("?f", lcd);    //clear lcd
fputs("?G420", lcd); //4x20 LCD
fputs("?c0", lcd);   //no cursor
fputs("?BFF", lcd);  //display at full brightness
delay_ms(300);

//define Euro symbol for LCD
fputs("?D003041E081E040300", lcd);
//define Pound symbol for LCD
fputs("?D10609081E08081F00", lcd);
//define ßeta symbol for LCD
fputs("?D20C12121412111116", lcd);

fputs("?f", lcd);   //clear lcd
delay_ms(300);

fputs("?y0?x00Caravan monitoring", lcd);
fputs("?y1?x00unit, version 5.00", lcd);
fputs("?y2?x00(c) 2016 B.L. Lonnon", lcd);
delay_ms(2700);
fputs("?f", lcd);   //clear lcd
delay_ms(300);

//value written at compile time
//if(read_eeprom(35) != 23)
 //clear_eeprom(0);
CS5490Init();

for(;;)
 {
  //update here so very little is done in isr
  //pulses have been received since last processing if pulse > 0
 
  if(pulse > 0)
   {
    //reset pulse value
    pulse = 0;
    //add to today's total
    used_today += pulse;
    //integral part stored in eeprom(0) in kWh
    //fractional part stored in eeprom(2)
    //eg. 2.99kWh stored as 2 (eeprom(0)) and 99 (eeprom(2))
    //with this line commented out, the LCD is fine
    //with this line in, the LCD is rubbish!
    //used_today += (1000 * read_int16_eeprom(0)) + 10 * read_int16_eeprom(2);
    //lidiv = ldiv(used_today, 1000L);
    //integral number of kWh
    //write_int16_eeprom(0, lidiv.quot);
    //fractional part of kWh
    //write_int16_eeprom(2, lidiv.rem);
    delay_ms(10);
   }//end if(pulse..

  //read the RTC
  DS3232getDate(dow, day, month, year);
  DS3232getTime(hour, minute, second);
 
  //disable interrupts while writing to LCD
  //disable_interrupts(int_ext);
  //disable_interrupts(global);
  delay_ms(10);
  fputs("?f", lcd);   //clear lcd
  delay_ms(300);
  //home cursor
  fprintf(lcd, "?a");
  delay_ms(10);
  fprintf(lcd, "?y0?x00%s %02u/%02u/%02u %02u:%02u   ",
            dayOfWeek[dow], day, month, year, hour, minute);
  delay_ms(10);
  //get readings from CS5490 for line 2 of LCD   
  //read Vrms, page 16, address 7
  VAC = read_register(16, 7) / 47246;

  //read Irms, page 16, address 6
  value = read_register(16, 6);
  IAC = (float)value / 554905;

  //read Power, page 16, address 20
  value = read_register(16, 20);
   
  PAC = (float)value / 781;
  if(IAC == 0)
   PAC = 0;
  delay_ms(10);
  if(PAC < 1000)
   {
    fprintf(lcd, "?y1?x00%luV @ %2.1fA = %3.0fW", VAC, IAC, PAC);
    //assemble values in case PIC is asked to send them
    //done here because of W/kW units
    sprintf(VAW, "%lu,%2.1f,%3.0f,", VAC, IAC, PAC);
   }
  else
   {
    PAC /= 1000; //to kW
    PAC = round(PAC);
    delay_ms(10);
    fprintf(lcd, "?n?y1?x00%luV @ %2.1fA = %1.1fkW?n", VAC, IAC, PAC);
   }//end else 
   
   //line 2 of LCD, used today and daily avg (not yet included)
  fprintf(lcd, "?y2?x00Today:%2lu.%02lu", read_int16_eeprom(0), read_int16_eeprom(2));
  //enable_interrupts(int_ext);
  //enable_interrupts(global);
  delay_ms(2000);
 }//end for(;;)
}//end main
//-----------------------------------------------------------------------------
void CS5490Init()
{
//reset CS5490
output_low(PIN_B4);
delay_ms(100);
output_high(PIN_B4);
delay_ms(80);

delay_ms(100);
output_high(PIN_B4);
delay_ms(80);

//Cirrus recommended to prevent Vref problems
write_register(0x000016, 0, 28);
write_register(0x0C0008, 0, 30);
write_register(0x000000, 0, 28);

//switch to 9600Bd (0x0204CD) or 57600 (0x021CCD)
write_register(0x021CCD, 0, 7);
delay_ms(200);
baud = FAST;

//enable HPFs
write_register(0x10060A, 16, 0);

//set EPG
//256us pulse width, step 1, p20
write_register(0x80000, 0, 8);

write_register(0x3121EB, 18, 28);
//step 3
write_register(0, 0, 9);
//step 4
write_register(0x11EEEE, 0, 1);
//step 5 (> 0.1s)
delay_ms(150);
//step 6
write_register(0x11EEE0, 0, 1);
//continuous conversions, step 7
send_to_5490(0xD5);
delay_ms(100);
}
//---------------------------------------------------------------------------
void write_register(int32 value, int8 page, int8 regnum)
{
send_to_5490(page | 0x80);     //select page
send_to_5490(regnum | 0x40);   //select register write
send_to_5490(make8(value, 0));
send_to_5490(make8(value, 1));
send_to_5490(make8(value, 2)); //send 24bits
}
//---------------------------------------------------------------------------
int32 read_register(int8 page, int8 regnum)
{
int8 lo, mid, hi;
send_to_5490(page | 0x80); //select page
send_to_5490(regnum);      //select register read
lo = from_chip();          //LSB
mid = from_chip();
hi = from_chip();          //MSB
return(make32(hi, mid, lo));
}
//---------------------------------------------------------------------------
int8 from_chip()
{
if(baud == SLOW)
 return fgetc(cs5490slow);
return fgetc(cs5490fast);
}
//---------------------------------------------------------------------------
float round(float val)
{
val *= 10;
val += 0.5;
floor(val);
return val/10;
}
//---------------------------------------------------------------------------
void clear_eeprom(boolean new_values)
{
int16 i;
//this value stops eeprom being cleared at power up,
//except after the run following programming
if(read_eeprom(35) != 23)
 write_eeprom(35, 23);

fputs("?f", lcd);
delay_ms(200);
fputs("?y0Erasing stored data:", lcd);
fputs("?y1?x00.", lcd);
//clear eeprom to all 0's
for(i = 0; i < 1023; i++)
 {
  write_eeprom(i, 0);
  if(i % 56 == 0)
   fputs("?y1.", lcd);
 }
if(!new_values)
 {
  fputs("?y2?x00All data erased!   ", lcd);
  delay_ms(1000);
 }//endif
write_eeprom(35, 23);
used_today = 0;
total_units = 0;
days_elapsed = 0;
write_int16_eeprom(12, 0);
delay_ms(500);
fputs("?f", lcd);    //clear lcd
delay_ms(200);
}


Code:
#include <18LF2620.h>
#device *=16 ADC=10 HIGH_INTS=TRUE

#FUSES EBTRB                    //memory protected from table reads
#FUSES NOWDT                    //No Watch Dog Timer
#FUSES HS                       //High speed Osc (> 4MHz)
#FUSES PROTECT                  //Code protected from reading
#FUSES BROWNOUT                 //Reset when brownout detected
#FUSES NOPUT                    //No Power Up Timer
#FUSES NOCPD                    //Data EEPROM Code not protected
#FUSES STVREN                   //Stack full/underflow will cause reset
#FUSES NODEBUG                  //No Debug mode for ICD
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES WRT                      //Program memory write protected
#FUSES NOIESO                   //Internal External Switch Over mode disabled
#FUSES NOFCMEN                  //Fail-safe clock monitor disabled
#FUSES NOPBADEN                 //PORTB pins are not configured as analog input channels on RESET
#FUSES NOWRTC                   //configuration registers are not write protected
#FUSES WRTB                     //Boot block write protected
#FUSES CPB                      //Boot Block code protection
#FUSES MCLR                     //Master Clear pin enabled
#FUSES NOXINST                  //Extended set extension and Indexed Addressing mode disabled (Legacy mode)


#use delay(crystal=10MHz)

//LCD117 (SMT version runs at 19K2)
#use rs232(baud=19200,uart1,xmit=PIN_C6,stream=lcd)

//CS5490
#use rs232(baud=57600,xmit=PIN_C0,rcv=PIN_C1,ERRORS,DISABLE_INTS,stream=cs5490fast)
#use rs232(baud=600,xmit=PIN_C0,rcv=PIN_C1,ERRORS,stream=cs5490slow)


DISABLE_INTS also made no difference!

Brian
Ttelmah



Joined: 11 Mar 2010
Posts: 20061

View user's profile Send private message

PostPosted: Fri Jul 01, 2016 1:20 pm     Reply with quote

I have to ask a question.

Why on earth are you using interrupts at all?.

If all you are ever doing with interrupts, is counting pulses, then the PIC has hardware to do this. You can just feed your pulse into a timer input and count them. No interrupts needed at all.....

On your chip either timer1, or timer3, can be set to count external pulses.

You seem determined to 'not use hardware' at every opportunity, even if this makes the job harder.....
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page Previous  1, 2, 3, 4  Next
Page 2 of 4

 
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