I am using the STM32F4 discovery board - http://www.st.com/st-web-ui/static/active/en/resource/technical/document/data_brief/DM00037955.pdf
And I am trying to debug via "printf"-like statements using the Serial Wire Viewer in the ST Micro STLink software: http://www.st.com/st-web-ui/static/active/en/resource/technical/document/user_manual/CD00262073.pdf
However I cannot see any results in my SWO Viewer despite setting the system clock to 168000000 Hz and stimulus port to 'All'. The (relevant) software I have running on the chip is below. This demo is set up to change the LED lights based on pressing the user button.
static uint8_t lastButtonStatus = RESET;
int main() {
init();
do {
loop();
} while (1);
}
void init() {
initLeds();
initButton();
}
void loop() {
static uint32_t counter = 0;
uint8_t currentButtonStatus = GPIO_ReadInputDataBit(GPIOA, USER_BUTTON);
if (lastButtonStatus != currentButtonStatus
&& currentButtonStatus != RESET) {
++counter;
GPIO_ResetBits(GPIOD, LEDS);
GPIO_SetBits(GPIOD, LED[counter % 4]);
// Test SWD output
SWV_puts("hello from stm32f4\n");
SWV_printfloat(1.98254, 2);
}
lastButtonStatus = currentButtonStatus;
}
Here are the SWV_ printing functions:
void SWV_puts(const char *s )
{
while (*s) ITM_SendChar(*s++);
}
/**
* #brief This function sends numbers to the serial wire viewer.
* #param number: number to be displayed on SWV
* #retval None
*/
void SWV_printnum(long number)
{
unsigned char buf[8 * sizeof(long)]; // Assumes 8-bit chars.
unsigned int i = 0;
//if number is 0
if (number == 0)
{
ITM_SendChar('0'); //if number is zero
return;
}
//account for negative numbers
if (number < 0)
{
ITM_SendChar('-');
number = number * -1;
}
while(number > 0)
{
buf[i++] = number % 10; //display in base 10
number = number / 10;
//NOTE: the effect of i++ means that the i variable will be at number of digits + 1
}
for(; i > 0; i--)
{
ITM_SendChar((char)('0' + buf[i-1]));
}
}
/**
* #brief This function sends numbers to the serial wire viewer.
* #param number: number to be displayed on SWV
* #param digits: number of digits after decimal point
* #retval None
*/
void SWV_printfloat(double number, int digits)
{
int i = 0;
//handle negative numbers
if(number < 0.0)
{
ITM_SendChar('-');
number = -number;
}
//round correctly so that uart_printfloat(1.999, 2) shows as "2.00"
double rounding = 0.5;
for(i = 0; i < digits; ++i) rounding = rounding / 10.0;
number = number + rounding;
//extract the integer part of the number and print it
unsigned long int_part = (unsigned long) number;
double remainder = (double)(number - (double)int_part);
SWV_printnum(int_part); //print the integer part
if(digits > 0) ITM_SendChar('.'); //print decimal pint
int toprint;
while(digits-- > 0)
{
remainder = remainder * 10.0;
toprint = (int)remainder;
SWV_printnum(toprint);
remainder = remainder - toprint;
}
}
I can confirm that the code compiles with no errors or warnings.
I have tried your code (nothing else) on my STMF4-discovery and it works fine.
I think you have to update any thing to last version:
STM32 ST/LINK utility v3.4.0
STLinkUSBDriver.dll v4.3.3.0
ST-LINK_CLI.exe v2.0.0
STM32F4 discovery board ST-LINK Firmware version : V2J21S0
STLink dongle 1.1.0.0 (10/12/2013)
good luck
Related
I am trying to create a modulated waveform out of 2 sine waves.
To do this I need the modulo(fmodf) to know what amplitude a sine with a specific frequency(lo_frequency) has at that time(t). But I get a hardfault when the following line is executed:
j = fmodf(2 * PI * lo_frequency * t, 2 * PI);
Do you have an idea why this gives me a hardfault ?
Edit 1:
I exchanged fmodf with my_fmodf:
float my_fmodf(float x, float y){
if(y == 0){
return 0;
}
float n = x / y;
return x - n * y;
}
But still the hardfault occurs, and when I debug it it doesn't even jump into this function(my_fmodf).
Heres the whole function in which this error occurs:
int* create_wave(int* message){
/* Mixes the message signal at 10kHz and the carrier at 40kHz.
* When a bit of the message is 0 the amplitude is lowered to 10%.
* When a bit of the message is 1 the amplitude is 100%.
* The output of the STM32 can't be negative, thats why the wave swings between
* 0 and 256 (8bit precision for faster DAC)
*/
static int rf_frequency = 10000;
static int lo_frequency = 40000;
static int sample_rate = 100000;
int output[sample_rate];
int index, mix;
float j, t;
for(int i = 0; i <= sample_rate; i++){
t = i * 0.00000001f; // i * 10^-8
j = my_fmodf(2 * PI * lo_frequency * t, 2 * PI);
if (j < 0){
j += (float) 2 * PI;
}
index = floor((16.0f / (lo_frequency/rf_frequency * 0.0001f)) * t);
if (index < 16) {
if (!message[index]) {
mix = 115 + sin1(j) * 0.1f;
} else {
mix = sin1(j);
}
} else {
break;
}
output[i] = mix;
}
return output;
}
Edit 2:
I fixed the warning: function returns address of local variable [-Wreturn-local-addr] the way "chux - Reinstate Monica" suggested.
int* create_wave(int* message){
static uint16_t rf_frequency = 10000;
static uint32_t lo_frequency = 40000;
static uint32_t sample_rate = 100000;
int *output = malloc(sizeof *output * sample_rate);
uint8_t index, mix;
float j, n, t;
for(int i = 0; i < sample_rate; i++){
t = i * 0.00000001f; // i * 10^-8
j = fmodf(2 * PI * lo_frequency * t, 2 * PI);
if (j < 0){
j += 2 * PI;
}
index = floor((16.0f / (lo_frequency/rf_frequency * 0.0001f)) * t);
if (index < 16) {
if (!message[index]) {
mix = (uint8_t) floor(115 + sin1(j) * 0.1f);
} else {
mix = sin1(j);
}
} else {
break;
}
output[i] = mix;
}
return output;
}
But now I get the hardfault on this line:
output[i] = mix;
EDIT 3:
Because the previous code contained a very large buffer array that did not fit into the 16KB SRAM of the STM32F303K8 I needed to change it.
Now I use a "ping-pong" buffer where I use the callback of the DMA for "first-half-transmitted" and "completly-transmitted":
void HAL_DAC_ConvHalfCpltCallbackCh1(DAC_HandleTypeDef * hdac){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET);
for(uint16_t i = 0; i < 128; i++){
new_value = sin_table[(i * 8) % 256];
if (message[message_index] == 0x0){
dac_buf[i] = new_value * 0.1f + 115;
} else {
dac_buf[i] = new_value;
}
}
}
void HAL_DAC_ConvCpltCallbackCh1 (DAC_HandleTypeDef * hdac){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);
for(uint16_t i = 128; i < 256; i++){
new_value = sin_table[(i * 8) % 256];
if (message[message_index] == 0x0){
dac_buf[i] = new_value * 0.1f + 115;
} else {
dac_buf[i] = new_value;
}
}
message_index++;
if (message_index >= 16) {
message_index = 0;
// HAL_DAC_Stop_DMA (&hdac1, DAC_CHANNEL_1);
}
}
And it works the way I wanted:
But the frequency of the created sine is too low.
I cap at around 20kHz but I'd need 40kHz.
I allready increased the clock by a factor of 8 so that one is maxed out:
.
I can still decrease the counter period (it is 50 at the moment), but when I do so the interrupt callback seems to take longer than the period to the next one.
At least it seems so as the output becomes very distorted when I do that.
I also tried to decrease the precision by taking only every 8th sine value but
I cant do this any more because then the output does not look like a sine wave anymore.
Any ideas how I could optimize the callback so that it takes less time ?
Any other ideas ?
Does fmodf() cause a hardfault in stm32?
It is other code problems causing the hard fault here.
Failing to compile with ample warnings
Best code tip: enable all warnings. #KamilCuk
Faster feedback than Stackoverflow.
I'd expect something like below on a well enabled compiler.
return output;
warning: function returns address of local variable [-Wreturn-local-addr]
Returning a local Object
Cannot return a local array. Allocate instead.
// int output[sample_rate];
int *output = malloc(sizeof *output * sample_rate);
return output;
Calling code will need to free() the pointer.
Out of range array access
static int sample_rate = 100000;
int output[sample_rate];
// for(int i = 0; i <= sample_rate; i++){
for(int i = 0; i < sample_rate; i++){
...
output[i] = mix;
}
Stack overflow?
static int sample_rate = 100000; int output[sample_rate]; is a large local variable. Maybe allocate or try something smaller?
Advanced: loss of precision
A good fmodf() does not lose precision. For a more precise answer consider double math for the intermediate results. An even better approach is more involved.
float my_fmodf(float x, float y){
if(y == 0){
return 0;
}
double n = 1.0 * x / y;
return (float) (x - n * y);
}
Can I not use any function within another ?
Yes. Code has other issues.
1 value every 10uS makes only 100kSPS whis is not too much for this macro. In my designs I generate > 5MSPS signals without any problems. Usually I have one buffer and DMA in circular mode. First I fill the buffer and start generation. When the half transmition DMA interrupt is trigerred I fill the first half of the buffer with fresh data. The the transmition complete interrupt is trigerred I fill the second half and this process repeats all over again.
based on some tutorial code I've found I coded a little synthesizer with three oscilators and four different waveform. It works well and I want to add an LFO to module the sounds. Since I didn't coded everything on my own I'm a bit confused of how I could fit the LFO formula on my code. This is more or less what I tried in order to implement the LFO formula on a sinewave.(This formula is something like this: sinewaveFormula + 0.5 * Sinefreq * sin(2pi*1) * time)
double normalize(double phase)
{
double cycles = phase/(2.0*pi);
phase -= trunc(cycles) * 2.0 * pi;
if (phase < 0) phase += 2.0*pi;
return phase;
}
double sine(double phase)
{ phase = normalize(phase); return (sin(phase));}
static void build_sine_table(int16_t *data, int wave_length) {
double phase_increment = (2.0f * pi) / (double)wave_length;
double current_phase = 0;
for(int i = 0; i < wave_length; i++) {
int sample = synthOsc(current_phase, oscNum, selectedWave, selectedWave2, selectedWave3, intensity, intensity2, intensity3) + 0.5 * ((current_phase* wave_length) / (2*pi)) * sin(2*pi*(1.0)) * wave_length;
data[i] = (int16_t)sample;
current_phase += phase_increment;
}
}
static void write_samples(int16_t *s_byteStream, long begin, long end, long length) {
if(note > 0) {
double d_sample_rate = sample_rate;
double d_table_length = table_length;
double d_note = note;
// get correct phase increment for note depending on sample rate and table length.
double phase_increment = (get_pitch(d_note) / d_sample_rate) * d_table_length;
// loop through the buffer and write samples.
for (int i = 0; i < length; i+=2) {
phase_double += phase_increment;
phase_int = (int)phase_double;
if(phase_double >= table_length) {
double diff = phase_double - table_length;
phase_double = diff;
phase_int = (int)diff;
}
if(phase_int < table_length && phase_int > -1) {
if(s_byteStream != NULL) {
int16_t sample = sine_waveform_wave[phase_int];
target_amp = update_envelope();
if(smoothing_enabled) {
// move current amp towards target amp for a smoother transition.
if(current_amp < target_amp) {
current_amp += smoothing_amp_speed;
if(current_amp > target_amp) {
current_amp = target_amp;
}
} else if(current_amp > target_amp) {
current_amp -= smoothing_amp_speed;
if(current_amp < target_amp) {
current_amp = target_amp;
}
}
} else {
current_amp = target_amp;
}
sample *= current_amp; // scale volume.
s_byteStream[i+begin] = sample; // left channel
s_byteStream[i+begin+1] = sample; // right channel
}
}
}
}
}
The code compile but there's no LFO on the sine. I don't understand how I could make this formula work with this code.
It may help to get a basic understanding of how a LFO actually works. It is not that difficult - as an LFO is just another oscillator that is mixed to the waveform you want to modulate.
I would suggest to remove your LFO formular from your call of synthOsc(), then you get a clean oscillator signal again. As a next step, create another oscillator signal for which you can use a very low frequency. Mix both signals together and you are done.
Expresssed in simple math, it is like this:
int the_sample_you_want_to_modulate = synthOsc1(...);
int a_sample_with_very_low_frequency = synthOsc2(...);
Mixing two waveforms is done through addition:
int mixed_sample = the_sample_you_want_to_modulate + a_sample_with_very_low_frequency;
The resulting sample will sweep now based on the frequency you have used for synthOsc2().
As you can see, to implement an LFO you actually do not need a separate formular. You already have the formular when you know how to create an oscillator.
Note that if you add two sine oscillators that have the exact same frequency, the resulting signal will just get louder. But when each has a different frequency, you will get a new waveform. For LFOs (which are in fact just ordinary oscillators - like in your build_sine_table() function) you typically set a very low frequency: 1 - 10 Hz is low enough to get an audible sweep. For higher frequencies you get chords as a result.
I've coded a simple sequencer in C with SDL 1.2 and SDL_mixer(to play .wav file). It works well and I want to add some audio synthesis to this program. I've look up the and I found this sinewave code using SDL2(https://github.com/lundstroem/synth-samples-sdl2/blob/master/src/synth_samples_sdl2_2.c)
Here's how the sinewave is coded in the program:
static void build_sine_table(int16_t *data, int wave_length)
{
/*
Build sine table to use as oscillator:
Generate a 16bit signed integer sinewave table with 1024 samples.
This table will be used to produce the notes.
Different notes will be created by stepping through
the table at different intervals (phase).
*/
double phase_increment = (2.0f * pi) / (double)wave_length;
double current_phase = 0;
for(int i = 0; i < wave_length; i++) {
int sample = (int)(sin(current_phase) * INT16_MAX);
data[i] = (int16_t)sample;
current_phase += phase_increment;
}
}
static double get_pitch(double note) {
/*
Calculate pitch from note value.
offset note by 57 halfnotes to get correct pitch from the range we have chosen for the notes.
*/
double p = pow(chromatic_ratio, note - 57);
p *= 440;
return p;
}
static void audio_callback(void *unused, Uint8 *byte_stream, int byte_stream_length) {
/*
This function is called whenever the audio buffer needs to be filled to allow
for a continuous stream of audio.
Write samples to byteStream according to byteStreamLength.
The audio buffer is interleaved, meaning that both left and right channels exist in the same
buffer.
*/
// zero the buffer
memset(byte_stream, 0, byte_stream_length);
if(quit) {
return;
}
// cast buffer as 16bit signed int.
Sint16 *s_byte_stream = (Sint16*)byte_stream;
// buffer is interleaved, so get the length of 1 channel.
int remain = byte_stream_length / 2;
// split the rendering up in chunks to make it buffersize agnostic.
long chunk_size = 64;
int iterations = remain/chunk_size;
for(long i = 0; i < iterations; i++) {
long begin = i*chunk_size;
long end = (i*chunk_size) + chunk_size;
write_samples(s_byte_stream, begin, end, chunk_size);
}
}
static void write_samples(int16_t *s_byteStream, long begin, long end, long length) {
if(note > 0) {
double d_sample_rate = sample_rate;
double d_table_length = table_length;
double d_note = note;
/*
get correct phase increment for note depending on sample rate and table length.
*/
double phase_increment = (get_pitch(d_note) / d_sample_rate) * d_table_length;
/*
loop through the buffer and write samples.
*/
for (int i = 0; i < length; i+=2) {
phase_double += phase_increment;
phase_int = (int)phase_double;
if(phase_double >= table_length) {
double diff = phase_double - table_length;
phase_double = diff;
phase_int = (int)diff;
}
if(phase_int < table_length && phase_int > -1) {
if(s_byteStream != NULL) {
int16_t sample = sine_wave_table[phase_int];
sample *= 0.6; // scale volume.
s_byteStream[i+begin] = sample; // left channel
s_byteStream[i+begin+1] = sample; // right channel
}
}
}
}
}
I don't understand how I could change the sinewave formula to genrate other waveform like square/triangle/saw ect...
EDIT:
Because I forgot to explain it, here's what I tried.
I followed the example I've seen on this video series(https://www.youtube.com/watch?v=tgamhuQnOkM). The source code of the method provided by the video is on github, and the wave generation code is looking like this:
double w(double dHertz)
{
return dHertz * 2.0 * PI;
}
// General purpose oscillator
double osc(double dHertz, double dTime, int nType = OSC_SINE)
{
switch (nType)
{
case OSC_SINE: // Sine wave bewteen -1 and +1
return sin(w(dHertz) * dTime);
case OSC_SQUARE: // Square wave between -1 and +1
return sin(w(dHertz) * dTime) > 0 ? 1.0 : -1.0;
case OSC_TRIANGLE: // Triangle wave between -1 and +1
return asin(sin(w(dHertz) * dTime)) * (2.0 / PI);
}
Because the C++ code here uses windows soun api I could not copy/paste this method to make it work on the piece of code I've found using SDL2.
So I tried to this in order to obtain a square wave:
static void build_sine_table(int16_t *data, int wave_length)
{
double phase_increment = ((2.0f * pi) / (double)wave_length) > 0 ? 1.0 : -1.0;
double current_phase = 0;
for(int i = 0; i < wave_length; i++) {
int sample = (int)(sin(current_phase) * INT16_MAX);
data[i] = (int16_t)sample;
current_phase += phase_increment;
}
}
This didn't gave me a square wave but more a saw wave.
Here's what I tried to get a triangle wave:
static void build_sine_table(int16_t *data, int wave_length)
{
double phase_increment = (2.0f * pi) / (double)wave_length;
double current_phase = 0;
for(int i = 0; i < wave_length; i++) {
int sample = (int)(asin(sin(current_phase) * INT16_MAX)) * (2 / pi);
data[i] = (int16_t)sample;
current_phase += phase_increment;
}
}
This also gave me another type of waveform, not triangle.
You’d replace the sin function call with call to one of the following:
// this is a helper function only
double normalize(double phase)
{
double cycles = phase/(2.0*M_PI);
phase -= trunc(cycles) * 2.0 * M_PI;
if (phase < 0) phase += 2.0*M_PI;
return phase;
}
double square(double phase)
{ return (normalize(phase) < M_PI) ? 1.0 : -1.0; }
double sawtooth(double phase)
{ return -1.0 + normalize(phase) / M_PI; }
double triangle(double phase)
{
phase = normalize(phase);
if (phase >= M_PI)
phase = 2*M_PI - phase;
return -1.0 + 2.0 * phase / M_PI;
}
You’d be building tables just like you did for the sine, except they’d be the square, sawtooth and triangle tables, respectively.
In short, I am reading a .wav file in MATLAB for the purposes of sending it to the ESP32 for an FFT analysis. The .wav file in question contains a recording of a Corona effect. My file has 96223 samples when inputted into MATLAB.
For now, I am trying to just get back a checksum so I can know that the data is sent correctly.
I have already tried using the code I've written for smaller sample sizes. For example, I get back the correct checksum when I send 200 samples although the code takes longer than I want it to take which is not good. More than that though, and I never get anything back because of timeouts.
This is my MATLAB code:
esp = serial('COM3');
set(esp, 'DataBits' , 8);
set(esp, 'StopBits', 1);
set(esp, 'BaudRate', 9600);
set(esp, 'Parity', 'none');
set(esp, 'terminator', 'LF');
%filename = 'test100.wav';
%corona = audioread(filename);
load('corona')
fopen(esp);
pause(0.1)
for i = 1:200
fprintf(esp, '%5.9f\n', corona(i,1));
pause(0.1);
end
output = fscanf(esp, '%f\n') %read the checksum
fclose(instrfind);
And this is my Arduino code:
#include <Arduino.h>
float sentData[200]; //initialize data array
int i = 0;
const int ledPin = 26;
float checksum = 0;
int CNT = 0;
void printFloat(float value, int places);
void setup()
{
Serial.begin(9600);
pinMode(ledPin, OUTPUT);
while (Serial.available() < 200)
{
digitalWrite(ledPin, HIGH); //keep the LED on while the data is being sent
}
while (Serial.available() != 0)
{
sentData[i] = Serial.parseFloat(); //parse the data to the array
i++;
}
Serial.flush();
delay(500);
digitalWrite(ledPin, LOW); //turn off the LED when data is fully parsed
for (size_t x = 0; x < 200; ++x)
{
checksum += sentData[x]; //calculate the sum of all elements in the sentData array
}
printFloat(checksum, 10); //send the checksum to the serial port for reading
}
void loop()
{
}
void printFloat(float value, int places)
{
// this is used to cast digits
int digit;
float tens = 0.1;
int tenscount = 0;
int i;
float tempfloat = value;
// if this rounding step isn't here, the value 54.321 prints as 54.3209
// calculate rounding term d: 0.5/pow(10,places)
float d = 0.5;
if (value < 0)
d *= -1.0;
// divide by ten for each decimal place
for (i = 0; i < places; i++)
d /= 10.0;
tempfloat += d;
// first get value tens to be the large power of ten less than value
if (value < 0)
tempfloat *= -1.0;
while ((tens * 10.0) <= tempfloat)
{
tens *= 10.0;
tenscount += 1;
}
// write out the negative if needed
if (value < 0)
Serial.print('-');
if (tenscount == 0)
Serial.print(0, DEC);
for (i = 0; i < tenscount; i++)
{
digit = (int)(tempfloat / tens);
Serial.print(digit, DEC);
tempfloat = tempfloat - ((float)digit * tens);
tens /= 10.0;
}
// if no places after decimal, stop now and return
if (places <= 0)
return;
// otherwise, write the point and continue on
Serial.print('.');
// now write out each decimal place by shifting digits one by one into the ones place and writing the truncated value
for (i = 0; i < places; i++)
{
tempfloat *= 10.0;
digit = (int)tempfloat;
Serial.print(digit, DEC);
// once written, subtract off that digit
tempfloat = tempfloat - (float)digit;
}
}
I expected to get back a checksum but I get a timeout when using very large sample sizes. I should also add that even though the ESP32 should be able to handle my file, I can't just push the whole file into the serial port because I get a buffer overflow error. Is there a solution to this?
First %5.9f doesn't make sense to me.
Thats minimum 5 characters with 9 digit precision. That 5 doesn't make sense as you'll always have at least 11 characters with 9 digit precision
Then let me do some maths for you:
96000 samples, 12 characters each (including \n) is a total of 10368000 bits.
At 9600 baud that's 1080 seconds of transfer time. -> 18 minutes.
As you add 0.1s pause after each sample you add another 9600 seconds to that.
Which leaves you with a total of 178 minutes (3 hours) of transfer time.
What do you expect?
For 200 samples its still 22,25s.
I am using code from the Waveshare website ( for use with the ADDA Waveshare board put on a RPi3 ) : http://www.waveshare.com/wiki/File:High-Precision-AD-DA-Board-Code.7z
*********************************************************************************************************
* name: main
* function:
* parameter: NULL
* The return value: NULL
*********************************************************************************************************
*/
int main()
{
uint8_t id;
int32_t adc[8];
int32_t volt[8];
uint8_t i;
uint8_t ch_num;
int32_t iTemp;
uint8_t buf[3];
if (!bcm2835_init())
return 1;
bcm2835_spi_begin();
bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_LSBFIRST ); // The default
bcm2835_spi_setDataMode(BCM2835_SPI_MODE1); // The default
bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_1024); // The default
bcm2835_gpio_fsel(SPICS, BCM2835_GPIO_FSEL_OUTP);//
bcm2835_gpio_write(SPICS, HIGH);
bcm2835_gpio_fsel(DRDY, BCM2835_GPIO_FSEL_INPT);
bcm2835_gpio_set_pud(DRDY, BCM2835_GPIO_PUD_UP);
//ADS1256_WriteReg(REG_MUX,0x01);
//ADS1256_WriteReg(REG_ADCON,0x20);
// ADS1256_CfgADC(ADS1256_GAIN_1, ADS1256_15SPS);
id = ADS1256_ReadChipID();
printf("\r\n");
printf("ID=\r\n");
if (id != 3)
{
printf("Error, ASD1256 Chip ID = 0x%d\r\n", (int)id);
}
else
{
printf("Ok, ASD1256 Chip ID = 0x%d\r\n", (int)id);
}
ADS1256_CfgADC(ADS1256_GAIN_1, ADS1256_15SPS);
ADS1256_StartScan(0);
ch_num = 8;
//if (ADS1256_Scan() == 0)
//{
//continue;
//}
while(1)
{
while((ADS1256_Scan() == 0));
for (i = 0; i < ch_num; i++)
{
adc[i] = ADS1256_GetAdc(i);
volt[i] = (adc[i] * 100) / 167;
}
for (i = 0; i < ch_num; i++)
{
buf[0] = ((uint32_t)adc[i] >> 16) & 0xFF;
buf[1] = ((uint32_t)adc[i] >> 8) & 0xFF;
buf[2] = ((uint32_t)adc[i] >> 0) & 0xFF;
printf("%d=%02X%02X%02X, %8ld", (int)i, (int)buf[0],
(int)buf[1], (int)buf[2], (long)adc[i]);
iTemp = volt[i]; /* uV */
if (iTemp < 0)
{
iTemp = -iTemp;
printf(" (-%ld.%03ld %03ld V) \r\n", iTemp /1000000, (iTemp%1000000)/1000, iTemp%1000);
}
else
{
printf(" ( %ld.%03ld %03ld V) \r\n", iTemp /1000000, (iTemp%1000000)/1000, iTemp%1000);
}
}
printf("\33[%dA", (int)ch_num);
bsp_DelayUS(100000);
}
bcm2835_spi_end();
bcm2835_close();
return 0;
}
Please help me figure out what this piece does in the main():
for (i = 0; i < ch_num; i++)
{
adc[i] = ADS1256_GetAdc(i);
volt[i] = (adc[i] * 100) / 167;
}
The constants (being 100 and 167) are not explained. What exactly are they trying to do in this 'calibration' and what do these constants depend upon?
Learning to read datasheets is an important skill for embedded programming.
The ADC on this chip returns a 24 bit signed value. The datasheet says
The ADS1255/6 full-scale input voltage equals ±2VREF/PGA.
Full-scale is 0x7FFFFF or 8388607.
I believe VRef is 2.5V, and the code sets PGA to 1 with ADS1256_CfgADC(ADS1256_GAIN_1, ADS1256_15SPS);
So an adc[i] value of 1 represents 1/0x7FFFFF of (2*2.5/1)Volts or 0.596 microvolts. We can convert the raw reading to microvolts by multiplying by 5000000 / 8388607. By multiplying by a number that large would overflow a 32 bit int, so let's reduce.
5000000 / 8388607 ~= 500/839 ~= 100/167.
(It would be possible to get a little more precision without overflow by multiplying by 250/419)
Looking specifically at this piece of code (which seems to be the actual question
for (i = 0; i < ch_num; i++)
{
adc[i] = ADS1256_GetAdc(i);
volt[i] = (adc[i] * 100) / 167;
}
It reads a number of ADC channels (0 through ch_num-1) and then does a crude conversion of each ADC value to a percentage of 167.
Consider the way we normally do percentage calculations as (a/b)*100. In other words, work out what fraction a is of b and then multiply by 100. But with integer math, the initial division will yield zero for all values < b, and the multiply won't affect that.
So we rewrite (a/b)*100 as (a*100)/b. Now the division occurs after the multiply and you will get non-zero results.
More specifically consider this example
(100/167) * 100 should become 0.59 * 100 = 59%. But with integer math you get (100/167) * 100 = (0) * 100 = 0%. After the rewrite you have (100*100)/167 = (10000)/167 = 59.
This is a standard trick with integer math.