Перейти к содержанию
    

ST Visual Develop для STM8 и математические функции

Господа, понадобилось мне разбить число float для вывода на сегментный индикатор (на целую и дробную части). Залез я в какой-то проект старый для STM32, где у меня это было реализовано - ан нет, не работает, библиотека math.h нужна.

А а ST Visual Development нету такой.

Попытался кормить ему тот файл, что в Keil - компилятор им подавился. Около сотни ошибок, подавляющее большинство из них - "not an argument"

 

Собственно вопросы - кто-нибудь пытался вставить библиотеки от KEIL в ST Visual Development?

Может в STVD есть свои математические функции?

 

Если нет, то как протыми средствами C разделить float на челую и дробную (до двух знаков) части?

 

Попробовал написать свою функцию, точностью до одного знака после запятой.

 

void LCD_Show_Current(float current)
{

    int int_num, decimal_num;
    float decimal_part;
    
    int_num=current;
        decimal_part=current-int_num;

    decimal_num=decimal_part*10;
}

 

Результат странный - decimal part почему-то всегда меньше на 0.1 (соответственно decimal_num меньше на единицу).

 

В функцию число поступает правильное (проверял уже внутри функции). А вот после decimal_part=current-int_num; получается недостача.

 

В упор не вижу, где я ошибся?

 

Тихо сам с собою - когда присваиваю переменной значение скажем 13.7, в отладчике оказывается, что переменная равна 13.69999998....

Что за самодеятельность контроллера?

Изменено пользователем zheka

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

замените на

    decimal_num=((int)(decimal_part*20)+1)>>2;

и все будет работать. 13.69999998 вместо 13.7 у вас и на обычном компьютере будет, если float поставите, это происходит из-за машинного представления чисел с плавающей точкой и того факта, что при преобразовании типов у вас просто все остальные биты отбрасываются, а вы их округлять хотите. В моем варианте выше я вначале взял число плюс еще один бит, насильно его увеличил на 1, а потом таки отбросил последний бит, то есть все, что 13.65 и более (в разумных пределах) даст в десятой 0.7.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Что-то не совсем понял идею...

 

У меня, пока вы писали, формула расчета стала компактнее

 

    unsigned int int_num, decimal_num;
    float decimal_part;
    
    current=current+0.01; // временная мера, "костыль"
    int_num=current;
       decimal_num=(current-(int)current)*10; // собственно формула

Что в ней нужно поменять в соответствии с Вашей идеей?

 

Въехал в вашу идею - так у меня практически то же самое - я фактически добавил куда-то бит, а округлилось само, в процессе выделения дробной части. ПОчему я так сделал? Я заметил, что портится конец числа float, то есть тот разряд, где должен быть 0, уменьшается на единицу, а это меняет и предыдущий разряд (к примеру 0.70 становится 0.69). Я же, зная, что сотые мне не нужны, превращал 0.70 в 0.71. А единичка отбрасывалась сама потом.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Въехал в вашу идею - так у меня практически то же самое

некоторая разница все-таки есть, в большинстве случаев не так чтоб существенна, но:

1. если у вас будет число 13.76, то в моем случае у вас на дисплее получится 13.8, а в вашем 13.7, что не верно с точки зрения округления,

2. в моем случае на одну операцию с плавающей точкой будет меньше, а если у вас плавающая точка в режиме эмулятора, то ваше вычисление будет выполняться существенно дольше :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Если нет, то как протыми средствами C разделить float на челую и дробную (до двух знаков) части?

Разобрать байты согласно IEEE754

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Example

 

/* MODF.C */

 

#include <math.h>

#include <stdio.h>

 

void main( void )

{

double x, y, n;

 

x = -14.87654321; /* Divide x into its fractional */

y = modf( x, &n ); /* and integer parts */

 

printf( "For %f, the fraction is %f and the integer is %.f\n",

x, y, n );

}

 

Output

 

For -14.876543, the fraction is -0.876543 and the integer is -14

 

А самое правильное - умножить интересуещее на 100, результат в ЦЕЛОМ числе.

От него получить остаток и частное деления на 100 (div / ldiv) и вывести на индикатор с учетом знака (у остатка знак не показывать).

 

Для одной десятой - три компонента для отображения.

static int tempsign(int t)
{
    return t < 0 ? '-' : '+';
}

static int temp1(int t)
{
    t = t < 0 ? - t : t;
    return t / 10;
}

static int temp01(int t)
{
    t = t < 0 ? - t : t;
    return t % 10;
}

 

зы: а может не переходить к плавающим там, где оно образуется? Чую, что это обработка датчика температуры... Так считайте в целых!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Чую, что это обработка датчика температуры...

Нет, это реверс инжиниринг вот такой штуки https://ru.aliexpress.com/item/10-100/32848....274233edrwCM4z

 

Температура там, кстати, тоже есть)

 

Полез я кстати в это самое утройство (до этого работал с отладочной платой), в нем чип залочен. Оно и понятно. Но мне не читать прошивку надо, а стереть.

в ST Visual Programmer пункт меню Erase неактивен, даже с незалоченным чипом. Так как его стереть то?

Неуже ли STшники не могли унифицировать и сделать так, чтобы STM8 можно было работать в привычном Keil?

 

нашел... блин, как все сложно STM8....

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Нет, это реверс инжиниринг вот такой штуки [url="http://electronix.ru/redirect.php?https://ru.aliexpress.com/item/10-100/32848423838.html?

И на кой там вообще float сдалось?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Неуже ли STшники не могли унифицировать и сделать так, чтобы STM8 можно было работать в привычном Keil?
Не договорились с Keil, чтобы те компилятор написали. И сами не удосужились. "Из коробки" только ассемблер. :(

Кстати, с STVD какой компилятор используете? Cosmic, Raisonance? Или кто-то прикрутил туда SDCC?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я использую COSMIC

Ну и нарисовалась очередная проблема...

#error clnk Debug\ups_stm8_project.lkf:1 segment .text size overflow (1465)

Ковырялся в настройках, единственное, что сделал, так это поменял Memory Model на Long Stack. Не помогло.

 

Контроллер STM8S003F3P6 - флеша 8 кб, RAM - 1 кб

А программка то у меня небольшая, вряд ли, чтоб я успел вылезти за пределы памяти.

 

Посмотрите на код, может у меня нерационально как-то с переменными? Куда копать?

 

 

/* MAIN.C file
* 
* Copyright © 2002-2005 STMicroelectronics
*/

#include "stm8s.h"
#include "stm8s_adc1.h"
#include "stm8s_clk.h"
#include "stm8s_tim1.h"

#define BACKLIGHT GPIO_PIN_3
#define BACKLIGHT_PORT GPIOA

#define TM1722_POWER GPIO_PIN_4
#define STB GPIO_PIN_5
#define SCLK GPIO_PIN_6
#define DIO GPIO_PIN_7

#define IO_PORT GPIOC

#define LATENCY 1


#define LED GPIO_PIN_5
#define LED_PORT GPIOB

unsigned char DIGITS[10]={0xFA,0x60,0xD6,0xF4,0x6C,0xBC,0xBE,0xE0,0xFE,0xFC};
unsigned char BATTERY_LEVELS[9]={0x00,0x10,0x11,0x19,0x1D,0x1F,0x3F,0x7F,0xFF};



typedef struct
{
 float VOLTAGE;
float CURRENT;
float TEMPERATURE;
unsigned char PERCENT;
unsigned char BATTERY_LEVEL;
unsigned char SPARK_sign;
unsigned char current_right_parameter; // 0 - off, 1 -voltage, 2 - temperature
unsigned char current_left_parameter; // 0- off, 1 - percent, 2 - current

} tLCD_VAR;

tLCD_VAR LCD_VAR;


void delay(uint32_t t) {
   while(--t);
}

volatile uint16_t adc2;

 INTERRUPT_HANDLER(ADC1_IRQHandler,22)
{


   ADC1_ClearITPendingBit(ADC1_IT_EOC);

   adc2 = ADC1_GetBufferValue(ADC1_CHANNEL_4);
}  

void send_command(unsigned char code)
{

uint8_t n;

 GPIO_WriteHigh(IO_PORT, STB);
 delay(LATENCY);
 GPIO_WriteLow(IO_PORT, STB);
 delay(LATENCY);
  for (n=0;n<8;n++)
   {
    GPIO_WriteLow(IO_PORT, SCLK);
    if ((code>>n)&0x01) GPIO_WriteHigh(IO_PORT, DIO); else  GPIO_WriteLow(IO_PORT, DIO);
    delay(LATENCY);
    GPIO_WriteHigh(IO_PORT, SCLK);
   delay(LATENCY);
   }
delay(LATENCY);

}	

void send_data(unsigned char code)
{
unsigned char n;

 for (n=0;n<8;n++)
  {
   GPIO_WriteLow(IO_PORT, SCLK);
    if ((code>>n)&0x01) GPIO_WriteHigh(IO_PORT, DIO); else  GPIO_WriteLow(IO_PORT, DIO);
    delay(LATENCY);
     GPIO_WriteHigh(IO_PORT, SCLK);
		delay(LATENCY);
  }


}	

void LCD_Clear(unsigned char a)
{
unsigned char x;
send_command(0x00);
send_command(0x40);
send_command(0x02);
for (x=0;x<16;x++) send_data(0x00);
send_command(0x97);
}

void LCD_Update()
{

}

void LCD_Show_Voltage(float voltage)
{
unsigned int int_num, decimal_num;
float decimal_part;
unsigned char percent_sign=0;

if (LCD_VAR.current_left_parameter==1) percent_sign=1; 

voltage=voltage+0.01;
int_num=voltage;
 decimal_num=(voltage-(int)voltage)*10;
 send_command(0x00);
send_command(0x44);
send_command(0xC7);
if (int_num<10) send_data(0x00+percent_sign);else send_data(DIGITS[int_num/10]+percent_sign);
send_command(0xCB);
send_data(DIGITS[int_num%10]);
send_command(0xCE);
send_data(DIGITS[decimal_num]+1);
send_command(0xCA);
send_data(0x4); 
send_command(0x97);	
LCD_VAR.current_right_parameter=1; 
}

void LCD_Show_Temperature(float temperature)
{
unsigned int int_num, decimal_num;
float decimal_part;
unsigned char percent_sign=0;

if (LCD_VAR.current_left_parameter==1) percent_sign=1; 

temperature=temperature+0.01;
int_num=temperature;
 decimal_num=(temperature-(int)temperature)*10;

 send_command(0x00);
send_command(0x44);
send_command(0xC7);
if (int_num<10) send_data(0x00+percent_sign);else send_data(DIGITS[int_num/10]+percent_sign);
send_command(0xCB);
send_data(DIGITS[int_num%10]);
send_command(0xCE);
send_data(DIGITS[decimal_num]+1);
send_command(0xCA);
send_data(0x08); 
send_command(0x97);	
LCD_VAR.current_right_parameter=2; 
}



void LCD_Show_Battery_Level(unsigned char battery_level)
{
send_command(0xCF);
send_data(BATTERY_LEVELS[battery_level]);
send_command(0x97);	
}


void LCD_Show_Current(float current)
{
unsigned int int_num, decimal_num;
float decimal_part;

current=current+0.01;
int_num=current;
 decimal_num=(current-(int)current)*10;

 send_command(0x00);
send_command(0x44);
send_command(0xC2);
if (int_num<10) send_data(0x00+LCD_VAR.SPARK_sign);else send_data(DIGITS[int_num/10]+LCD_VAR.SPARK_sign);
send_command(0xC3);
send_data(DIGITS[int_num%10]+1);
send_command(0xC6);
send_data(DIGITS[decimal_num]+1);
send_command(0x97);	
LCD_VAR.current_left_parameter=2; 
if (LCD_VAR.current_right_parameter==1) LCD_Show_Voltage(LCD_VAR.VOLTAGE);
if (LCD_VAR.current_right_parameter==2) LCD_Show_Temperature(LCD_VAR.TEMPERATURE);
 if (LCD_VAR.current_right_parameter==0) {send_command(0xC7);send_data(0x00); send_command(0x97);	}
}

void LCD_Show_Percent(unsigned char percent)
{
send_command(0x00);
send_command(0x44);
send_command(0xC2);
if (percent<100) send_data(0x00+LCD_VAR.SPARK_sign);else send_data(DIGITS[percent/100]+LCD_VAR.SPARK_sign);
send_command(0xC3);
if (percent<10) send_data(0x00);else send_data(DIGITS[(percent%100)/10]);
send_command(0xC6);
send_data(DIGITS[percent%10]);
send_command(0xC7);
send_command(0x97);	
LCD_VAR.current_left_parameter=1; 
if (LCD_VAR.current_right_parameter==1) LCD_Show_Voltage(LCD_VAR.VOLTAGE);
if (LCD_VAR.current_right_parameter==2) LCD_Show_Temperature(LCD_VAR.TEMPERATURE);
 if (LCD_VAR.current_right_parameter==0) {send_command(0xC7);send_data(0x01); send_command(0x97);	}

}


void LCD_Show_Spark(unsigned char dummy)
{
LCD_VAR.SPARK_sign=1;
if (LCD_VAR.current_left_parameter==1) LCD_Show_Percent(LCD_VAR.PERCENT);
if (LCD_VAR.current_left_parameter==2) LCD_Show_Current(LCD_VAR.CURRENT);
 if (LCD_VAR.current_left_parameter==0) {send_command(0xC2);send_data(0x01); send_command(0x97);	}
}

void LCD_Hide_Spark(unsigned char dummy)
{
LCD_VAR.SPARK_sign=0;
if (LCD_VAR.current_left_parameter==1) LCD_Show_Percent(LCD_VAR.PERCENT);
if (LCD_VAR.current_left_parameter==2) LCD_Show_Current(LCD_VAR.CURRENT);
 if (LCD_VAR.current_left_parameter==0) {send_command(0xC2);send_data(0x01); send_command(0x97);	}
}




main()
{


CLK_DeInit();

   CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1);
   CLK_SYSCLKConfig(CLK_PRESCALER_HSIDIV1); // set 16 MHz for CPU





	GPIO_DeInit(LED_PORT);
   GPIO_Init(LED_PORT, LED, GPIO_MODE_OUT_PP_LOW_FAST);


  GPIO_DeInit(BACKLIGHT_PORT);
   GPIO_Init(BACKLIGHT_PORT, BACKLIGHT, GPIO_MODE_OUT_PP_LOW_FAST);
   GPIO_WriteHigh(BACKLIGHT_PORT, BACKLIGHT);

   GPIO_DeInit(IO_PORT);
   GPIO_Init(IO_PORT, STB|SCLK|DIO|TM1722_POWER, GPIO_MODE_OUT_PP_LOW_FAST);
   GPIO_WriteHigh(BACKLIGHT_PORT, BACKLIGHT);

   GPIO_WriteHigh(IO_PORT, TM1722_POWER);

 TIM1_DeInit();
  TIM1_TimeBaseInit(32 - 1, TIM1_COUNTERMODE_UP, 1250 - 1, 0);
  TIM1_SelectOutputTrigger(TIM1_TRGOSOURCE_UPDATE);

	// ------------ ADC1 -------------------
   GPIO_Init(GPIOD,GPIO_PIN_3,GPIO_MODE_IN_FL_NO_IT);

  //
  ADC1_DeInit();
  ADC1_ScanModeCmd(ENABLE);
  ADC1_DataBufferCmd(ENABLE);
  ADC1_ITConfig(ADC1_IT_EOCIE, ENABLE);
  ADC1_ClearITPendingBit(ADC1_IT_EOC);
  ADC1_Init(ADC1_CONVERSIONMODE_SINGLE, ADC1_CHANNEL_1,
        ADC1_PRESSEL_FCPU_D18, ADC1_EXTTRIG_TIM, ENABLE, ADC1_ALIGN_RIGHT,
        ADC1_SCHMITTTRIG_CHANNEL0 | ADC1_SCHMITTTRIG_CHANNEL1, DISABLE);

  // разрешаем прерывания
  enableInterrupts();

  // разрешаем работу таймера
  TIM1_Cmd(ENABLE);
   LCD_Clear(0);	

   while(1)
   {

ADC1_StartConversion();        
       delay(60000);
      LCD_Show_Voltage(11);
       delay(60000);
   }


   LCD_Clear(0);

 LCD_VAR.BATTERY_LEVEL=5;
LCD_VAR.CURRENT=18.2;
LCD_VAR.PERCENT=78;
LCD_VAR.VOLTAGE=13.5;
LCD_VAR.TEMPERATURE=25.6;


 LCD_Show_Current(30.4);
LCD_Show_Voltage(LCD_VAR.VOLTAGE);
delay(600000);
	LCD_Show_Percent(LCD_VAR.PERCENT);
	delay(600000);
	LCD_Show_Temperature(LCD_VAR.TEMPERATURE);
	delay(600000);
	LCD_Show_Percent(LCD_VAR.PERCENT+11);
	delay(600000);
	LCD_Show_Current(LCD_VAR.CURRENT);
  delay(600000);
  LCD_Show_Voltage(LCD_VAR.VOLTAGE);
 delay(600000);
 LCD_Show_Spark(0);
  delay(600000);
 LCD_Hide_Spark(0);
 delay(600000);
 LCD_Show_Percent(LCD_VAR.PERCENT);
  delay(600000);
 LCD_Show_Spark(0);
  delay(600000);
 LCD_Hide_Spark(0);
LCD_Show_Battery_Level(6);


while(1)
{


}

}


#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line){
   while (1);
}
#endif

 

Решил через RTFM )))

 

Open the project via ST Visual Develop. Right click on the project in workspace panel. Click "Settings". Click "C Compiler" tab. Choose "Optimizations" from the "Category" combobox. Select "Customize" from the "Optimizations" combobox. Make sure "Split Functions in Separate Sections" checkbox checked. And you're done.
Изменено пользователем zheka

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А программка то у меня небольшая, вряд ли, чтоб я успел вылезти за пределы памяти.

Надо не гадать, а заглянуть в map-файл.

 

PS: А вообще - с таким уменьем программирования, как в "программке", на STM8 делать нечего. Разве что хелло ворд под виндой писать... :crying:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

с таким уменьем программирования, как в "программке"

 

Вы про незнание мной назначения MAP файла? Так я не сразу и нашел его в проекте. После 2-х дней работы с STVD я этому даже не удивился уже. Компилятор меня отослал к lkf файлу (я решил, что это аналог map), а там белеберда какая-то.

Или вы в целом про стиль, который усматривается из текста (вы же упомянули "программку") Если второе, то, былбы благодарен услышать вашу критику моего уменья в более конкретных выражениях) Стиль? Конкретные ошибки? ИЛи еще что?

Я любитель) Консерваториев на заканчивал. Моим каждодневным занятием программирование не является.

Изменено пользователем zheka

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Аппаратный SPI там никак не задействовать? Кривая китайская схема? ;)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Аппаратный SPI там никак не задействовать? Кривая китайская схема? ;)

Лучше не надо, там ведь хитрые манипуляции в пином STB. Можно конечно попробовать, но зачем - используя уже реализованный ногодрыг, я, по крайней мере был уверен что у меня все так как на "осцилограмме" в даташите. Да и работает уже вроде все.

 

А китайцы - они не могут без сюрпризов:

Я нарисовал схему этой платы. Про черную паяльную маску, чтобы бледнолицым было труднее, я вообще молчу. Так вот по схеме очевидно просматривался пин, на который поступает напряжение для измерения. Подключил я аккумулятор на 12 вольт. Ткнул щупом в пин входа АЦП - что-то на нем невнятное 0.2-0.3 вольт. А осцилограф у меня был переключен в диапазон до 20 вольт, так что я этот сигнал и не воспринял всерьез. Ну, думаю, ошибся наверное. Потыкал по другим пинам и не нашел входа, куда поступает напряжение для измерения.

Как оказалось, китайцы рассчитали эту схему на 100 вольт (!!!!) входного напряжения. 100 вольт!!! Понятно, что для этого потребовался делитель, который при 12 вольтах выдает 0.3 вольт на вход АЦП. А цена одного шага АЦП составляет около 0.1 вольт.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Как оказалось, китайцы рассчитали эту схему на 100 вольт (!!!!) входного напряжения. 100 вольт!!!

Так в приведенной вами выше ссылке так прямо сразу и написано - 100В.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...