|
|
View previous topic :: View next topic |
Author |
Message |
CcsNewbie Guest
|
Hex to float |
Posted: Tue Jan 30, 2007 4:22 am |
|
|
I have data in the hex format format 0x0000 to 0x0fff. I wish to get a float value ranging from 0 - 5V and transmit it to through hyperterminal. Is there a function I can use? Thanks! |
|
|
Ttelmah Guest
|
|
Posted: Tue Jan 30, 2007 6:03 am |
|
|
First,is '0FFF', 5v, or 4.998v?. If this is the output of a normal 12bit ADC, the behaviour is typically, that the 'reference' voltage (5v), is never actually 'reached', and would correspond to one count more than the full scale of the ADC.
Now, is this really a 'hex' number (ie., held as four ASCII characters), or is this a 16bit binary number that can be represented as the hex values 0000 to 0FFF?. If the latter, then the function, would simply be:
printf("%04.3f",val*1.2207E-3);
where 'val', is the 16bit value, with the output going to stdio. This will send the value, in the form '0.002', with a leading zero, and three dgits after the decimal point.
If instead the value is actually in hex, then a 'hex to bin' conversion will be needed first. The function 'atol', contains the ability to convert such a string to a number (needs '0x' added to the front).
Best Wishes |
|
|
CcsNewbie Guest
|
|
Posted: Wed Jan 31, 2007 5:07 am |
|
|
Its e output from a external 12 bit adc connected to my pic. i get a range of 002c to 0fff when i place it to 0 and 5V respectively. I've been tryin to send output to my Visual C# serial port program. Previously i sent it using
printf("%lx\n\r",data)
I've read ure earlier reply to another person and put this in visual C#.
int intValue = (BitConverter.ToInt16(buffer, 0));
//convert value to a range from 0-5V using 12 bit ADC
float voltage = intValue * (5 / 4096);
But I'm sampling @ 250Hz and each time i do a serialport.read(buffer, 0, bytes) I get flooded with around 14-28 bytes in my serial buffer instead of the ideal 2 bytes i tot would be coming in the range 002c to 0fff as seen in hyperterminal. Is there a solution to the problem in the PIC side? Thanks in advance.
Ttelmah wrote: | First,is '0FFF', 5v, or 4.998v?. If this is the output of a normal 12bit ADC, the behaviour is typically, that the 'reference' voltage (5v), is never actually 'reached', and would correspond to one count more than the full scale of the ADC.
Now, is this really a 'hex' number (ie., held as four ASCII characters), or is this a 16bit binary number that can be represented as the hex values 0000 to 0FFF?. If the latter, then the function, would simply be:
printf("%04.3f",val*1.2207E-3);
where 'val', is the 16bit value, with the output going to stdio. This will send the value, in the form '0.002', with a leading zero, and three dgits after the decimal point.
If instead the value is actually in hex, then a 'hex to bin' conversion will be needed first. The function 'atol', contains the ability to convert such a string to a number (needs '0x' added to the front).
Best Wishes |
|
|
|
SherpaDoug
Joined: 07 Sep 2003 Posts: 1640 Location: Cape Cod Mass USA
|
|
Posted: Wed Jan 31, 2007 10:23 am |
|
|
So you have in output in integer A/D counts, and you want it in Volts. I suggest you convert it to integer millivolts (0 to 5000), then just print the thousands, print the decimal point, then print the remaining digits using a putc() for each digit. This avoids floating point and printf() both of which take lots of system resources. _________________ The search for better is endless. Instead simply find very good and get the job done. |
|
|
Help needed Guest
|
Delay in conversion |
Posted: Thu Mar 15, 2007 10:29 pm |
|
|
Hi,
Can i ask how do i find out the time spent in executing this code?
Code: | printf("%04.3f",val*1.2207E-3); |
I'm doing data acq and I suspect this code is taking too much time to execute and I may be having an aliasing problem. Thanks!! |
|
|
davekelly
Joined: 04 Oct 2006 Posts: 53 Location: Berkshire, England
|
Re: Delay in conversion |
Posted: Fri Mar 16, 2007 4:28 am |
|
|
Help needed wrote: | Hi,
Can i ask how do i find out the time spent in executing this code?
Code: | printf("%04.3f",val*1.2207E-3); |
I'm doing data acq and I suspect this code is taking too much time to execute and I may be having an aliasing problem. Thanks!! |
Simplest way is to use MPLAB, with the simulator as the debugger.
It has a Stopwatch function, which you can reset just before this instruction, then check result after it. Make sure the xtal frequency is set correctly though. |
|
|
frequentguest Guest
|
|
Posted: Fri Mar 16, 2007 6:31 am |
|
|
Or, with access to an oscilloscope, set a pin high before the command in question and set it low after. Use the o-scope to measure the elapsed time. |
|
|
Ttelmah Guest
|
|
Posted: Fri Mar 16, 2007 7:52 am |
|
|
Seriously, consider using a scaled integer, as as already been mentioned. Floats are _slow_. Remember that to output the digits, involves repeated float divisions by ten. Also, since there are more than two digits, the chip will be waiting for the serial on latter characters (use an interrupt driven serial driver to avoid this).
If you perform:
printf("%04.3w",(INT32)val*1220L);
Then rewrite to use interrupt driven serial transmission through a buffer, the timings will be massively better (orders of magnitude!...).
Best Wishes |
|
|
Need Help Guest
|
|
Posted: Sun Mar 18, 2007 7:28 pm |
|
|
Hi, thanks for the input for everyone. May i ask what is an interrupt driven serial driver? Thanks again! |
|
|
Ttelmah Guest
|
|
Posted: Mon Mar 19, 2007 4:26 am |
|
|
OK.
The problem with writing directly to the serial port, is that after two characters are sent, you have to start waiting for the hardware buffer to clear, before another character can be sent. This means that a print, that generates (say) ten characters of output, at 9600bps, 8N1 (ten bit times/char), will take 80/9600th second to complete, even if the maths is instantaneous. 8.3mSec. This is an 'age' in computer terms, and can cause problems with timings on other jobs etc..
The 'answer', is to use the same approach as for the serial receive, and perfrom the actual transmission under interrupt control, with a buffer.
Code: |
#define TX_BUFFER_SIZE (32)
int8 tx_buffer[TX_BUFFER_SIZE],tx_out=0,tx_in=0;
#define tx_empty (tx_out==tx_in) //test if buffer is empty
#define incbuff(x) ((x+1) & TX_BUFFER_SIZE) //increment buffer pointer
#define tx_full (incbuff(tx_in)==tx_out) //test if buffer is full
void tx_putc(int8 chr) {
//ensure an interrupt cnnot occur during the buffer update
disable_interrupts(INT_TBE);
while (tx_full) {
//here buffer is full, so wait...
enable_interrupts(INT_TBE);
delay_us(10);
disable_interrupts(INT_TBE)
}
//put character in buffer
tx_buffer[tx_in]=chr;
//increment input pointer
tx_in=incbuff(tx_in);
//and enable interrupt to ensure transmission starts
enable_interrupts(INT_TBE);
}
#INT_TBE
void serial_tx(void) {
//Here a serial transmitter buffer empty interrupt has occured
if (tx_empty) { //if software buffer empty
disable_interrupts(INT_TBE); //turn off transmission
}
else {
//send character
putc(tx_buffer[tx_out]);
//and update pointer
tx_out=incbuff(tx_out);
}
}
|
Now you use this, by sending characters to 'tx_putc', rather than directly to 'putc', and for 'printf' for example, use:
printf(tx_putc,"Test message");
The global interrupt must be enabled, but you don't enable the int_tbe in the main code (this is done automatically, when data is sent).
There are various ways of tweaking this, which may be more efficient but in general an advantage in one area, comes at a cost in another. So (for instance), you can elect to disable the interrupt when the last charater has been sent (cuts the need for one final interrupt call to disable the interrupt, after the data is sent). Or you can elect to send the first character without using the interrupt. However generally, these add complexity, and don't really help overall performance.
As shown, the code will wait if the software buffer is full. Obviously, there are other possible solutions to this (throwing away data for instance). The decision of what is 'best', will depend on the application.
Best Wishes |
|
|
Need Help Guest
|
|
Posted: Tue Mar 20, 2007 8:02 am |
|
|
Thanks Ttelmah for your help, may I ask how do i transmit the int32 using the tx_putc? |
|
|
Ttelmah Guest
|
|
Posted: Tue Mar 20, 2007 8:26 am |
|
|
Er. Exactly the same way as you'd send it using putc...
Look at the example printf shown.
Best Wishes |
|
|
Need Help Guest
|
|
Posted: Wed Mar 21, 2007 12:45 am |
|
|
Hi Ttelmah,
I've tried using the scaled integer but i keep getting 7 digits in hyperterminal although I'm using %04.0w or %04.0lu. How do i truncate the rest when i need only the top 4 digits?
I added the interrupts to my code by my transmit pin keeps outputting a High signal with no output at hyperterminal.
Code: | #include <16F877.h>
#device ADC=10
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock = 20000000)
#use rs232(baud=9600, xmit = PIN_C6, rcv = PIN_C7)
#define TX_BUFFER_SIZE (32)
int8 tx_buffer[TX_BUFFER_SIZE],tx_out=0,tx_in=0;
#define tx_empty (tx_out==tx_in) //test if buffer is empty
#define incbuff(x) ((x+1) & TX_BUFFER_SIZE) //increment buffer pointer
#define tx_full (incbuff(tx_in)==tx_out) //test if buffer is full
void tx_putc(int8 chr) {
//ensure an interrupt cnnot occur during the buffer update
disable_interrupts(INT_TBE);
while (tx_full) {
//here buffer is full, so wait...
enable_interrupts(INT_TBE);
delay_us(10);
disable_interrupts(INT_TBE);
}
//put character in buffer
tx_buffer[tx_in]=chr;
//increment input pointer
tx_in=incbuff(tx_in);
//and enable interrupt to ensure transmission starts
enable_interrupts(INT_TBE);
}
#INT_TBE
void serial_tx(void) {
//Here a serial transmitter buffer empty interrupt has occured
if (tx_empty) { //if software buffer empty
disable_interrupts(INT_TBE); //turn off transmission
}
else {
//send character
putc(tx_buffer[tx_out]);
//and update pointer
tx_out=incbuff(tx_out);
}
}
void main()
{
int16 data;
setup_adc_ports(ALL_ANALOG);
setup_adc(ADC_CLOCK_DIV_32);
set_adc_channel(0);
enable_interrupts(INT_TBE);
enable_interrupts(GLOBAL);
delay_us(20);
while(TRUE){
while(input(PIN_B0))
{
data = read_adc();
data = (INT32)data*4883L;
printf(tx_putc,"%04.0w\r",data);
delay_us(40);
}
}
} |
|
|
|
Need Help Guest
|
|
Posted: Wed Mar 21, 2007 7:32 am |
|
|
Sorry, posted wrong file, the printf in the main I used was:
Code: |
printf(tx_putc,"%04.0w\r",(INT32)data*4883L);
|
|
|
|
Ttelmah Guest
|
|
Posted: Wed Mar 21, 2007 8:06 am |
|
|
You don't...
If you only want the digits in front of the virtaul 'decimal place', then just divide the number by 1000, and print it as a normal decimal. The 'point' about the %w for, is to treat the number as if it has a fixed number of decimal places, so if you use %7.3w, the value will be displayed with three decimal places. To just display the stuff 'in front' of the virtual decimal, just do:
printf(tx_putc,"%04Ld.\r",((INT32)data*4883L)/1000L);
This will add the decimal point.
Best Wishes |
|
|
|
|
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
|