реклама на сайте
подробности

 
 
3 страниц V   1 2 3 >  
Reply to this topicStart new topic
> ST Visual Develop для STM8 и математические функции
zheka
сообщение Aug 11 2018, 08:59
Сообщение #1


Гуру
******

Группа: Участник
Сообщений: 2 072
Регистрация: 14-01-06
Пользователь №: 13 164



Господа, понадобилось мне разбить число 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 - Aug 11 2018, 08:45
Go to the top of the page
 
+Quote Post
iiv
сообщение Aug 11 2018, 10:00
Сообщение #2


вопрошающий
*****

Группа: Свой
Сообщений: 1 726
Регистрация: 24-01-11
Пользователь №: 62 436



замените на
Код
    decimal_num=((int)(decimal_part*20)+1)>>2;

и все будет работать. 13.69999998 вместо 13.7 у вас и на обычном компьютере будет, если float поставите, это происходит из-за машинного представления чисел с плавающей точкой и того факта, что при преобразовании типов у вас просто все остальные биты отбрасываются, а вы их округлять хотите. В моем варианте выше я вначале взял число плюс еще один бит, насильно его увеличил на 1, а потом таки отбросил последний бит, то есть все, что 13.65 и более (в разумных пределах) даст в десятой 0.7.
Go to the top of the page
 
+Quote Post
zheka
сообщение Aug 11 2018, 11:11
Сообщение #3


Гуру
******

Группа: Участник
Сообщений: 2 072
Регистрация: 14-01-06
Пользователь №: 13 164



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

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

Код
    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. А единичка отбрасывалась сама потом.
Go to the top of the page
 
+Quote Post
iiv
сообщение Aug 11 2018, 12:00
Сообщение #4


вопрошающий
*****

Группа: Свой
Сообщений: 1 726
Регистрация: 24-01-11
Пользователь №: 62 436



Цитата(zheka @ Aug 11 2018, 16:11) *
Въехал в вашу идею - так у меня практически то же самое

некоторая разница все-таки есть, в большинстве случаев не так чтоб существенна, но:
1. если у вас будет число 13.76, то в моем случае у вас на дисплее получится 13.8, а в вашем 13.7, что не верно с точки зрения округления,
2. в моем случае на одну операцию с плавающей точкой будет меньше, а если у вас плавающая точка в режиме эмулятора, то ваше вычисление будет выполняться существенно дольше sm.gif
Go to the top of the page
 
+Quote Post
HardEgor
сообщение Aug 11 2018, 12:19
Сообщение #5


Гуру
******

Группа: Свой
Сообщений: 2 223
Регистрация: 3-03-06
Из: Tomsk
Пользователь №: 14 925



Цитата(zheka @ Aug 11 2018, 15:59) *
Если нет, то как протыми средствами C разделить float на челую и дробную (до двух знаков) части?

Разобрать байты согласно IEEE754
Go to the top of the page
 
+Quote Post
Genadi Zawidowsk...
сообщение Aug 11 2018, 12:46
Сообщение #6


Профессионал
*****

Группа: Участник
Сообщений: 1 620
Регистрация: 22-06-07
Из: Санкт-Петербург, Россия
Пользователь №: 28 634



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;
}


зы: а может не переходить к плавающим там, где оно образуется? Чую, что это обработка датчика температуры... Так считайте в целых!
Go to the top of the page
 
+Quote Post
zheka
сообщение Aug 11 2018, 19:12
Сообщение #7


Гуру
******

Группа: Участник
Сообщений: 2 072
Регистрация: 14-01-06
Пользователь №: 13 164



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

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

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

Полез я кстати в это самое утройство (до этого работал с отладочной платой), в нем чип залочен. Оно и понятно. Но мне не читать прошивку надо, а стереть.
в ST Visual Programmer пункт меню Erase неактивен, даже с незалоченным чипом. Так как его стереть то?
Неуже ли STшники не могли унифицировать и сделать так, чтобы STM8 можно было работать в привычном Keil?

нашел... блин, как все сложно STM8....
Go to the top of the page
 
+Quote Post
jcxz
сообщение Aug 11 2018, 19:15
Сообщение #8


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(zheka @ Aug 11 2018, 22:12) *
Нет, это реверс инжиниринг вот такой штуки [url="http://electronix.ru/redirect.php?https://ru.aliexpress.com/item/10-100/32848423838.html?

И на кой там вообще float сдалось?
Go to the top of the page
 
+Quote Post
Harbinger
сообщение Aug 12 2018, 05:01
Сообщение #9


старший лаборант
******

Группа: Свой
Сообщений: 2 702
Регистрация: 30-09-05
Из: ЮЗЖД
Пользователь №: 9 097



Цитата(zheka @ Aug 11 2018, 22:12) *
Неуже ли STшники не могли унифицировать и сделать так, чтобы STM8 можно было работать в привычном Keil?
Не договорились с Keil, чтобы те компилятор написали. И сами не удосужились. "Из коробки" только ассемблер. sad.gif
Кстати, с STVD какой компилятор используете? Cosmic, Raisonance? Или кто-то прикрутил туда SDCC?


--------------------
Китайская комплектация - европейское качество! ;)
Go to the top of the page
 
+Quote Post
zheka
сообщение Aug 12 2018, 07:31
Сообщение #10


Гуру
******

Группа: Участник
Сообщений: 2 072
Регистрация: 14-01-06
Пользователь №: 13 164



Я использую COSMIC
Ну и нарисовалась очередная проблема...
Код
#error clnk Debug\ups_stm8_project.lkf:1 segment .text size overflow (1465)

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

Контроллер STM8S003F3P6 - флеша 8 кб, RAM - 1 кб
А программка то у меня небольшая, вряд ли, чтоб я успел вылезти за пределы памяти.

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


CODE

/* 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 - Aug 12 2018, 06:41
Go to the top of the page
 
+Quote Post
jcxz
сообщение Aug 12 2018, 08:08
Сообщение #11


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(zheka @ Aug 12 2018, 10:31) *
А программка то у меня небольшая, вряд ли, чтоб я успел вылезти за пределы памяти.

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

PS: А вообще - с таким уменьем программирования, как в "программке", на STM8 делать нечего. Разве что хелло ворд под виндой писать... crying.gif
Go to the top of the page
 
+Quote Post
zheka
сообщение Aug 12 2018, 09:46
Сообщение #12


Гуру
******

Группа: Участник
Сообщений: 2 072
Регистрация: 14-01-06
Пользователь №: 13 164



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


Вы про незнание мной назначения MAP файла? Так я не сразу и нашел его в проекте. После 2-х дней работы с STVD я этому даже не удивился уже. Компилятор меня отослал к lkf файлу (я решил, что это аналог map), а там белеберда какая-то.
Или вы в целом про стиль, который усматривается из текста (вы же упомянули "программку") Если второе, то, былбы благодарен услышать вашу критику моего уменья в более конкретных выражениях) Стиль? Конкретные ошибки? ИЛи еще что?
Я любитель) Консерваториев на заканчивал. Моим каждодневным занятием программирование не является.

Сообщение отредактировал zheka - Aug 12 2018, 09:56
Go to the top of the page
 
+Quote Post
Harbinger
сообщение Aug 12 2018, 11:53
Сообщение #13


старший лаборант
******

Группа: Свой
Сообщений: 2 702
Регистрация: 30-09-05
Из: ЮЗЖД
Пользователь №: 9 097



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


--------------------
Китайская комплектация - европейское качество! ;)
Go to the top of the page
 
+Quote Post
zheka
сообщение Aug 12 2018, 12:45
Сообщение #14


Гуру
******

Группа: Участник
Сообщений: 2 072
Регистрация: 14-01-06
Пользователь №: 13 164



Цитата(Harbinger @ Aug 12 2018, 14:53) *
Аппаратный SPI там никак не задействовать? Кривая китайская схема? wink.gif

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

А китайцы - они не могут без сюрпризов:
Я нарисовал схему этой платы. Про черную паяльную маску, чтобы бледнолицым было труднее, я вообще молчу. Так вот по схеме очевидно просматривался пин, на который поступает напряжение для измерения. Подключил я аккумулятор на 12 вольт. Ткнул щупом в пин входа АЦП - что-то на нем невнятное 0.2-0.3 вольт. А осцилограф у меня был переключен в диапазон до 20 вольт, так что я этот сигнал и не воспринял всерьез. Ну, думаю, ошибся наверное. Потыкал по другим пинам и не нашел входа, куда поступает напряжение для измерения.
Как оказалось, китайцы рассчитали эту схему на 100 вольт (!!!!) входного напряжения. 100 вольт!!! Понятно, что для этого потребовался делитель, который при 12 вольтах выдает 0.3 вольт на вход АЦП. А цена одного шага АЦП составляет около 0.1 вольт.
Go to the top of the page
 
+Quote Post
aaarrr
сообщение Aug 12 2018, 12:51
Сообщение #15


Гуру
******

Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448



Цитата(zheka @ Aug 12 2018, 15:45) *
Как оказалось, китайцы рассчитали эту схему на 100 вольт (!!!!) входного напряжения. 100 вольт!!!

Так в приведенной вами выше ссылке так прямо сразу и написано - 100В.
Go to the top of the page
 
+Quote Post

3 страниц V   1 2 3 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 15th June 2025 - 04:41
Рейтинг@Mail.ru


Страница сгенерированна за 0.01538 секунд с 7
ELECTRONIX ©2004-2016