What is the best way to convert adc values - type-conversion

I need to convert my ADC result value from an hexadecimal value to a float value and a percentage.
For example I've a 12bit resolution stored into a uint16_t(becouse I've possibility to change resolution). So VREF should be 0x0FFF and GND 0x0000.
Now, I need to convert this values into volts and percentage.
I wanted to do this:
float volt, perc, vref;
vref = 3.3;
uint16_t adc_value = ADC_RESULT;
volt = (vref/0x0FFF)*adc_value;
perc = adc_value/(0x0FFF/100);
Since I'm performing this operation in a MCU, I wanted to make it more efficent. How do you advice me to do and which type of variable should I use(or made conversions)?

Related

Frequency Adjusting with STM32 DAC

I used STM32F407VG to create a 30 khz sine wave. Timer settings are; Prescaler = 2-1, ARR = 1, also the clock is 84 Mhz(the clock which runs DAC).
I wrote a function called generate_sin();
#define SINE_ARY_SIZE (360)
const int MAX_SINE_DEGERI = 4095; // max_sine_value
const double BASLANGIC_NOKTASI = 2047.5; //starting point
uint32_t sine_ary[SINE_ARY_SIZE];
void generate_sine(){
for (int i = 0; i < SINE_ARY_SIZE; i++){
double deger = (sin(i*M_PI*360/180/SINE_ARY_SIZE) * BASLANGIC_NOKTASI) + BASLANGIC_NOKTASI; //double value
sine_ary[i] = (uint32_t)deger; // value
}
This is the function which creates sine wave. I used HAL DMA to send DAC output variables.
HAL_TIM_Base_Start(&htim2);
generate_sine();
HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, sine_ary, SINE_ARY_SIZE, DAC_ALIGN_12B_R);
These are the codes i used to do what i want. But im having a trouble to change frequency without changing prescaler or ARR.
So here is my question. Can i change frequency without changing timer settings ? For example i want to use buttons and whenever i push button i want my frequency to change.
The generate_sine function will give you one period of a sine wave which has SINE_ARY_SIZE of samples.
To increase the frequency you need to make the period shorter (for 2x frequency, you would have half the number of samples per period). So you should calculate the array for smaller SINE_ARY_SIZE (which will fill just part of the original buffer with a shorter sine wave) and also put this smaller value in the HAL_DAC_Start_DMA function.
Decreasing the frequency will require making the array longer.
You should declare the sine_ary with a maximum length that you will need (for lowest frequency). Make sure it fits in RAM.
#define MAXIMUM_ARRAY_LENGTH 360
uint32_t usedArrayLength = 180;
const double amplitude = 2047.5;
uint32_t sine_ary[MAXIMUM_ARRAY_LENGTH];
void generate_sine(){
for (int i = 0; i < usedArrayLength; i++){
double value = (sin(i*M_PI*2/usedArrayLength) * amplitude) + amplitude;
sine_ary[i] = (uint32_t)value; // value
}
This will have two times higher frequency than the original code, because it only has 180 samples per period, compared to 360.
Start it using
HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, sine_ary, usedArrayLength, DAC_ALIGN_12B_R);
To change the frequency, stop DAC, change the value of usedArrayLength (smaller value means higher frequency, must be less or equal to MAXIMUM_ARRAY_LENGTH). Then call the generate_sine function and start the DAC again by the same function (that now uses new usedArrayLength).
Frequency will be: Clock/prescaler/ARR/usedArrayLength
Also, you should use uint16_t for the array (values are from 0 to 4095, the DAC is 12bit I suppose) and DMA should be set to Half-word (2 bytes per value).

How to calibrate internal temperature sensor value in STM32 L496ZGT4

I'm creating simple project using STM32 L946ZGT4. I'd like to use the internal temperature sensor. I configured ADC and i can get value from this. My problem is with calibrate this sensor. Using Reference Manual and Datasheet instructions my final value in celsius degrees equals -17. The ADC value is about 800. Here is my code for sensor calibrating.
#include "Myfun.h"
#include "HD44780.h"
extern uint16_t tab[100];
char buf[16];
float sum, avg;
#define TS_CAL1((uint16_t*)((uint32_t) 0x1FFF75A8))
#define TS_CAL2((uint16_t*)((uint32_t) 0x1FFF75CA))
#define TS_CAL1_TEMP 30.0 f
#define TS_CAL2_TEMP 130.0 f
int32_t temperature;
int main(void)
{
SysTick_Config(4000000 / 1000);
LCD_Init();
Led_Conf();
ADC_Conf_DMA_TempSensor();
while (1)
{
sum = 0;
for (int i = 0; i < 100;)
{
sum = sum + tab[i];
++i;
}
avg = sum / 100; // here is my value from ADC
temperature = (int32_t)(((TS_CAL2_TEMP - TS_CAL1_TEMP) / ((float)(*TS_CAL2) - (float)(*TS_CAL1))) *
(avg - (float)(*TS_CAL1)) + 30.0);
sprintf(buf, "%d C", temperature);
LCD_Clear();
LCD_WriteText(buf);
delay_ms(60);
}
}
you have to convert your ADC reading to volt by:
ADC_value / ADC_maxvalue * ADC_refvoltage
with
ADC_value: the value you get back from the ADC
ADC_maxvalue: 2^12 = 4096, when you are sampling in the 12-bit mode
ADC_revoltage: 3.3 Volt, typically - except you are using a different reference voltage
Then apply the formula from the datasheet, chapter "Electrical Characteristics" --> "Operating Conditions" --> "Temperature Sensor", where you find the offset and the scale (plus the formula) to convert the voltage to temperature ...

Expand a range of float numbers ? (keeping a uniform distribution)

Expand a random range from 0-6 to 0-7. Given a function rand6() that
returns a random float in the range [0,6] with a uniform distribution,
write a function that returns a random float in the range [0,7] using
only rand6() and keeping an uniform distribution.
Since we're working with float numbers, can i just do the following ?
x = rand6() / 6 * 7
Yes, absolutely. Your distribution will be scaled as well

Pack two floats within range into one float

In HLSL, how would I go about packing two floats within the range of 0-1 into one float with an optimal precision. This would be incredibly useful to compress my GBuffer further.
//Packing
float a = 0.45;
float b = 0.55;
uint aScaled = a * 0xFFFF;
uint bScaled = b * 0xFFFF;
uint abPacked = (aScaled << 16) | (bScaled & 0xFFFF);
float finalFloat = asfloat(abPacked);
//Unpacking
float inputFloat = finalFloat;
uint uintInput = asuint(inputFloat);
float aUnpacked = (uintInput >> 16) / 65535.0f;
float bUnpacked = (uintInput & 0xFFFF) / 65535.0f;
Converting floating point numbers to fixed point integers is an error prone idea, due to floats covering much larger magnitudes. Say unpacking sRGB will give you pow(255,2.2) values, which are larger than 0xffff, and you will need several times than amount for robust HDR. Generally fixed point code is very fragile, obfuscated and a nightmare to debug. People invented floats for a good reason.
There are several 16-bit float formats. IEEE 16-bit float one is optimized for numbers between -1.0 to 1.0, but also support numbers up to 0x10000, just in case you need HDR, still so you will need to normalize your larger floats for it, Then there is bfloat16, which behaves like normal 32-bit float, just with less precision. IEEE 16-bit floats are widely supported by modern CPUs and GPUs, and can also be converted quickly even in software. bfloat16 is just gaining popularity, so you will have to research if it is suitable for your needs. Finally you can introduce your own 16-bit float format, using integer log function, which is provided by most CPUs as a single instruction.

iPhone - AVAudioPlayer - convert decibel level into percent

I like to update an existing iPhone application which is using AudioQueue for playing audio files. The levels (peakPowerForChannel, averagePowerForChannel) were linear form 0.0f to 1.0f.
Now I like to use the simpler class AVAudioPlayer which works fine, the only issue is that the levels which are now in decibel, not linear from -120.0f to 0.0f.
Has anyone a formula to convert it back to the linear values between 0.0f and 1.0f?
Thanks
Tom
Several Apple examples use the following formula to convert the decibels into a linear range (from 0.0 to 1.0):
double percentage = pow (10, (0.05 * power));
where power is the value you get from one of the various level meter methods or functions, such as AVAudioPlayer's averagePowerForChannel:
Math behind the Linear and Logarithmic value conversion:
1. Linear to Decibel (logarithmic):
decibelValue = 20.0f * log10(linearValue)
Note: log is base 10
Suppose the linear value in the form of percentage range from [ 0 (min vol) to 100 (max vol)] then the decibelValue for half of the volume (50%) is
decibelValue = 20.0f * log10(50.0f/100.0f) = -6 dB
Full volume:
decibelValue = 20.0f * log10(100.0f/100.0f) = 0 dB
Complete mute:
decibelValue = 20.0f * log10(0/100.0f) = -infinity
2. Decibel(logarithmic) to Linear:
LinearValue = pow(10.0f, decibelValue/20.0f)
Apple uses a lookup table in their SpeakHere sample that converts from dB to a linear value displayed on a level meter.
I moulded their calculation in a small routine; see here.