Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: ST Visual Develop для STM8 и математические функции
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему
zheka
Господа, понадобилось мне разбить число 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....
Что за самодеятельность контроллера?
iiv
замените на
Код
    decimal_num=((int)(decimal_part*20)+1)>>2;

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

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

Код
    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. А единичка отбрасывалась сама потом.
iiv
Цитата(zheka @ Aug 11 2018, 16:11) *
Въехал в вашу идею - так у меня практически то же самое

некоторая разница все-таки есть, в большинстве случаев не так чтоб существенна, но:
1. если у вас будет число 13.76, то в моем случае у вас на дисплее получится 13.8, а в вашем 13.7, что не верно с точки зрения округления,
2. в моем случае на одну операцию с плавающей точкой будет меньше, а если у вас плавающая точка в режиме эмулятора, то ваше вычисление будет выполняться существенно дольше sm.gif
HardEgor
Цитата(zheka @ Aug 11 2018, 15:59) *
Если нет, то как протыми средствами C разделить float на челую и дробную (до двух знаков) части?

Разобрать байты согласно IEEE754
Genadi Zawidowski
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;
}


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

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

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

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

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

И на кой там вообще float сдалось?
Harbinger
Цитата(zheka @ Aug 11 2018, 22:12) *
Неуже ли STшники не могли унифицировать и сделать так, чтобы STM8 можно было работать в привычном Keil?
Не договорились с Keil, чтобы те компилятор написали. И сами не удосужились. "Из коробки" только ассемблер. sad.gif
Кстати, с STVD какой компилятор используете? Cosmic, Raisonance? Или кто-то прикрутил туда SDCC?
zheka
Я использую 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.
jcxz
Цитата(zheka @ Aug 12 2018, 10:31) *
А программка то у меня небольшая, вряд ли, чтоб я успел вылезти за пределы памяти.

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

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


Вы про незнание мной назначения MAP файла? Так я не сразу и нашел его в проекте. После 2-х дней работы с STVD я этому даже не удивился уже. Компилятор меня отослал к lkf файлу (я решил, что это аналог map), а там белеберда какая-то.
Или вы в целом про стиль, который усматривается из текста (вы же упомянули "программку") Если второе, то, былбы благодарен услышать вашу критику моего уменья в более конкретных выражениях) Стиль? Конкретные ошибки? ИЛи еще что?
Я любитель) Консерваториев на заканчивал. Моим каждодневным занятием программирование не является.
Harbinger
Аппаратный SPI там никак не задействовать? Кривая китайская схема? wink.gif
zheka
Цитата(Harbinger @ Aug 12 2018, 14:53) *
Аппаратный SPI там никак не задействовать? Кривая китайская схема? wink.gif

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

А китайцы - они не могут без сюрпризов:
Я нарисовал схему этой платы. Про черную паяльную маску, чтобы бледнолицым было труднее, я вообще молчу. Так вот по схеме очевидно просматривался пин, на который поступает напряжение для измерения. Подключил я аккумулятор на 12 вольт. Ткнул щупом в пин входа АЦП - что-то на нем невнятное 0.2-0.3 вольт. А осцилограф у меня был переключен в диапазон до 20 вольт, так что я этот сигнал и не воспринял всерьез. Ну, думаю, ошибся наверное. Потыкал по другим пинам и не нашел входа, куда поступает напряжение для измерения.
Как оказалось, китайцы рассчитали эту схему на 100 вольт (!!!!) входного напряжения. 100 вольт!!! Понятно, что для этого потребовался делитель, который при 12 вольтах выдает 0.3 вольт на вход АЦП. А цена одного шага АЦП составляет около 0.1 вольт.
aaarrr
Цитата(zheka @ Aug 12 2018, 15:45) *
Как оказалось, китайцы рассчитали эту схему на 100 вольт (!!!!) входного напряжения. 100 вольт!!!

Так в приведенной вами выше ссылке так прямо сразу и написано - 100В.
zheka
Цитата(aaarrr @ Aug 12 2018, 15:51) *
Так в приведенной вами выше ссылке так прямо сразу и написано - 100В.


Покупал я у другого продавца, с более бедным описанием. Приводя ссылку вчера, я в нее не вчитывался.
jcxz
Цитата(zheka @ Aug 12 2018, 12:46) *
Или вы в целом про стиль, который усматривается из текста (вы же упомянули "программку") Если второе, то, былбы благодарен услышать вашу критику моего уменья в более конкретных выражениях) Стиль? Конкретные ошибки? ИЛи еще что?

Я уже писал, что для того чтобы вывести напряжение/ток/температуру на LCD float в принципе не нужен. Это как вскапывать экскаватором грядки. Ваш экскаватор просто в огород не влезет. не удивлюсь что библиотека эмуляции float сожрёт половину из доступных 8 кБ флеша.
Ну и в остальном - это не стиль, а какое-то недоразумение. Зачем хотя-бы дублировать одни и те же операции многократно? Ещё раз - флеша в этом МК мало, от слова "совсем". И на такой быдлокодинг его размер явно не рассчитан.
Зачем использовать 32-битные переменные на 8-битном МК там где это совсем не нужно?
ну и остальное - не лучше...
zheka
Цитата
Зачем хотя-бы дублировать одни и те же операции многократно?

Это где это у меня дублирование?

Цитата
Зачем использовать 32-битные

Вообще не мое, в шаблоне было.
jcxz
Цитата(zheka @ Aug 12 2018, 17:33) *
Это где это у меня дублирование?

Посмотрите на свои LCD_Show_Voltage()/LCD_Show_Temperature()/LCD_Show_Current() - не видите схожести? Они почти идентичные за исключением нескольких мест. Зачем тогда столько раз продублировали одно и то же? Хотя сами говорите что не лезет во флешь....
Зачем BATTERY_LEVELS и DIGITS продублированы в ОЗУ и флешь, если используются как константы?
Зачем эти полотенца из send_command(0xC7);send_data(0x01); send_command(0x97); во многих местах? Что такое циклы - знаете?

PS: Удивительно - у меня в 8 кБ STM8 влезло управление PMSM-мотором с довольно сложным пользовательским интерфейсом, работой с EEPROM и UART и пр.. А у Вас простой вывод на семисегментник не лезет....
zheka
Цитата
Посмотрите на свои LCD_Show_Voltage()/LCD_Show_Temperature()/LCD_Show_Current() - не видите схожести?

Ну если вы об этом...
Они шлют разные команды, по разным адресам.
Более того, значок процента для цифр в левой части экрана дисплей считает точкой (запятой) старшего разряда цифр, находящихся в правой части экрана.
Управляя цифрами правой части экрана, нужно корректировать одну из цифр левой части, более того, нужно знать, что за параметр в данный момент в ней отображается (параметров 4, одновременно отображаемых - 2)
Можно конечно выделить общее и написать универсальную функцию, но для этого должно до такой степени не хватать места, что трата времени на подобные хитрости себя оправдывает. Я же зная сколько места в контроллере, и имея опыт написания более объемных прошивок, полагал, что места хватит при самом небрежном отношении к коду. ДА его собственно и хватило, я поставил галку, как указал выше - все нормально.
Эти 4 функции были написаны раздельно с целью упрощения рашифровки того, какой сегмент за что отвечает (даташита на дисплей нет).

Цитата
Зачем эти полотенца из send_command(0xC7);send_data(0x01); send_command(0x97); во многих местах? Что такое циклы - знаете?

Есть функция LCD_Clear - там цикл уместен и я его использовал. А в остальных функциях....
А ну-ка, хорошо, вот Вам кусок кода, покажите, как бы Вы сделали из него цикл? Просто интересно.
Код
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);

Но за критику в любом случае спасибо.
jcxz
Цитата(zheka @ Aug 12 2018, 21:18) *
А ну-ка, хорошо, вот Вам кусок кода, покажите, как бы Вы сделали из него цикл? Просто интересно.

Самое простое и очевидное что можно сделать: написать функцию отправляющую не побайтно, а строку байт.
Ну а в первую очередь - убрать float-ы.
zheka
Цитата(jcxz @ Aug 12 2018, 22:18) *
Самое простое и очевидное что можно сделать: написать функцию отправляющую не побайтно, а строку байт.

Не спорю. И это можно сделать. Но повторюсь, задача экономии передо мной не стояла, да и не стоит. Моя прошивка будет существенно меньше той, что была в оригинала (там было еще меню настроек). Мне нужно было побыстрее, пусть и топорно, сделать небольшой реверс-инжиниринг. Для себя, лишь бы работало.



Что-то не получается у меня с внешними прерываниями:
Цитата
.....
INTERRUPT_HANDLER(EXTI_PORTD_IRQHandler, 6)
{
GPIO_WriteHigh(BACKLIGHT_PORT, BACKLIGHT); // если прерывание сработает, то должна загореться подсветка (это не конечная цель, а просто способ отладки)
}

....
main()
{

GPIO_Init(GPIOD, GPIO_PIN_4, GPIO_MODE_IN_PU_IT); // кнопка, нога контроллера подтянута к питанию

ITC_SetSoftwarePriority(ITC_IRQ_PORTD, ITC_PRIORITYLEVEL_1);
EXTI_SetExtIntSensitivity(EXTI_PORT_GPIOD, EXTI_SENSITIVITY_FALL); // кнопка замыкает на минус


enableInterrupts();

//
while(1); // для чистоты эксперимента, чтобы программа не шла дальше.


Из комментариев все ясно. Брейкпоинты почему-то в STVD не ставятся...
Прерывание не срабатывает.
Вместе с тем есть еще одна странность - даже если отключить режим прерывания у пина, если вообще убрать текст обработчика (и сам обработчик), то как только вызвать enableInterrupts(); экран перестает работать. Как будто какое-то прерывание постоянно вызывается, что нарушает логику обмена информацией контроллером дисплея.

jcxz
Цитата(zheka @ Aug 12 2018, 22:44) *
Из комментариев все ясно. Брейкпоинты почему-то в STVD не ставятся...

Может не стоит мучаться и взять IAR? Там всё работает. Он бесплатный для Вашего МК.
Harbinger
Не видя всего проекта, сложно что-то сказать. Работал только со связкой STVD+Raisonance, проблем вроде не было... более того, отладчик STVD позволяет читать-писать переменные "на лету" ("read/write on the fly"). Но в свете последних веяний от резонанса придётся отказаться.

Цитата(jcxz @ Aug 13 2018, 17:05) *
Может не стоит мучаться и взять IAR? Там всё работает. Он бесплатный для Вашего МК.
А других альтернатив (с полноценной отладкой) в общем-то и нет.
zheka
Бесплатнее некуда
Цитата
The evaluation license is completely free of charge and allows you to try the integrated development environment and evaluate its efficiency and ease of use. When you start the product for the first time, you will be asked to register to get your evaluation license.

After installation, you have the following evaluation options to choose from:
•a 30-day time-limited but fully functional license
•a size-limited Kickstart license without any time limit

Restrictions to the 30-day time-limited evaluation
•A 30-day time limitation.
•Source code for runtime libraries is not included.
•No support for MISRA C.
•Limited technical support.
•Must not be used for product development or any other kind of commercial use.

Restrictions to the Kickstart, size-limited evaluation
•A 8 Kbyte code size limitation.
•Source code for runtime libraries is not included.
•No support for MISRA C.
•Limited technical support.


Информация с сайта iar

Но это не беда. Мне предложили на выбор 30 дневное ограничение либо 8кб ограничение по коду. Я выбрал второе.
Harbinger
Второе и предлагалось.
Cosmic на новом компе так и не получил лицензию - запрос проигнорировали, возможно, из-за того, что повторно на ту же почту. Raisonance за 32-килобайтную лицензию, бывшую когда-то бесплатной (аж на год!), дерёт 700 с чем-то евро, бесплатных вариантов не осталось вообще. Прикручивать sdcc к эклипсу или кодеблоксу - накладно по времени и результат под большим сомнением...
zheka
Н-да.. вы были правы. В IAR все работает.

Продолжаю мучить контроллер.
По нажатию одной из кнопок вхожу в режим halt.
Другой кнопкой хочу выводить из этого режима. Настроено прерывание по спаду на эту кнопку.
В принципе все работает - кнопку нажал, контроллер заснул. Нажал другую - контроллер пронулся и пошел дальше работать.

Добавил я в цикл основную задачу - измерение напряжения и вывод его на экран. И вот тут-то пошли проблемы - контроллер по нажатию на вторую кнопку просыпается, но зависает в функции работы с АЦП в виделенной жирным строке:
CODE
// ------------ ADC1 -------------------
uint16_t GetADCvalue(uint8_t ChanelNumb)
{
uint16_t tmphvalue;
uint16_t tmplvalue;

ADC1->CSR = ChanelNumb; // channel
delay(10);
ADC1->CR1 |= 0x61;
while(!(ADC1->CSR & ADC1_CSR_EOC)){;}
ADC1_ClearITPendingBit(ADC1_IT_EOC);
tmplvalue = ADC1->DRL;
tmphvalue = (uint16_t)ADC1->DRH << 8;
tmphvalue = tmphvalue + tmplvalue;
return (tmphvalue);
}


INTERRUPT_HANDLER(EXTI_PORTD_IRQHandler, 6)
{
/* In order to detect unexpected events during development,
it is recommended to set a breakpoint on the following instruction.
*/
w=0;
}

void main(void)
{

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

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

GPIO_Init(PWR_BTN_PORT, PWR_BTN, GPIO_MODE_IN_PU_NO_IT);
//EXTI_SetExtIntSensitivity(EXTI_PORT_GPIOA, EXTI_SENSITIVITY_FALL_ONLY);

GPIO_Init(MENU_BTN_PORT, MENU_BTN, GPIO_MODE_IN_PU_IT);
EXTI_SetExtIntSensitivity(EXTI_PORT_GPIOD, EXTI_SENSITIVITY_FALL_ONLY);

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


ADC1_Config();
enableInterrupts();

LCD_Clear(0);



while(1)
{
adc=GetADCvalue(4);
ADC_Voltage=adc*(5.0/1024)*20;
LCD_Show_Voltage(ADC_Voltage);

if (GPIO_ReadInputPin(GPIOA,GPIO_PIN_2))
{

LCD_Show_Spark(0);
}
else
{
LCD_Show_Spark(1);
halt();
}

}

Заметьте, я вхожу в halt аккуратно - не по прерыванию, а по чтению кнопки после того, как АЦП завершил работу.
Сам АЦП инициализирован так
Код
/ ------------ ADC1 -------------------
void ADC1_Config(void)
{
GPIO_Init(GPIOD,GPIO_PIN_3,GPIO_MODE_IN_FL_NO_IT);

ADC1->CR1  = 0x61; // 0b01100001;   // enable ADC
ADC1->CR2  = 0x08; // 0b00001000;   // right otygnment
ADC1->CR3  = 0x00; // 0;              // data bufer disable
ADC1->TDRL = 0xC0; // 0b11000000;  // disable Schmitt triggers for AIN7 & AIN6
}


Я решил эту проблему просто - перед halt() выключаю АЦП, после просыпания тут же включаю. Но что-то мне это кажется полумерой. Почему присходит сбой, так и должно быть?
jcxz
Цитата(zheka @ Aug 13 2018, 22:14) *
Почему присходит сбой, так и должно быть?

Вы бы, батенька, даташит хотя-б открыли. Да почитали как входить в halt, как выходить, что должно быть выключено во время halt-а, как включать после.....
zheka
Скачал полное руководство - RM0016
Описание halt занимает полстранички пункт 10.2.2, стр.103. Ценной информации там я не нашел, может покажете другой мануал?


Формально заход в halt и выход из него по нажатию одной и той же кнопки я осилил. Теперь борюсь за потребление.

Итак. Работающее устройство потребляет 55 мА (контроллер+подсветка, плюс драйвер LCD)
Последовал совету, который гласил, что нужно переводить все болтающиеся ноги на вывод. Получил 200 мА )))) Почему? Питание драйвера LCD китайская схема осуществляет через PC4. Нет, эта ножка не включает какой-нибудь ключ, она просто питает микросхему. И когда я микросхему отключил - потребление парадоксально возросо до 200 мА. Причиной тому была утечка через DIO и SCK в микросхему скорее всего. Потому как перевод пинов, подающих команды, в режим входа с подтяжкой снизил общее потребление до 40 мА.
Но, к сожалению, оно таким и остается.
Я попробовал поставить halt сразу после void main(void) {.... получил 35 мА.
Руки конечно потянулись, согласно советам в различных статьях, поиграться с тактовым генератором, но ведь у меня режим, в котором останавливается все....

Да... чтобы понять, к чему стремиться, прежде чем вытереть старую китайскую прошивку, я измерил, сколько потребляет устройство в спящем режиме - 12 мкА. Китайцы что-то знают, чего не знаю я.

Скажите мне, если я вызываю halt() сразу же, в начале void main(void) - каков должен быть ток потребления? И нужно ли что-то отключать, если по умолчанию все уже выключено, а если и включено, то при остановленном тактовом генераторе, по идее не должно жрать.
zheka
В общем я взял обычную китайскую отладочную плату, запитал ее не через имеющийся LDO, а непоредственно от разъема программирования. Сам LDO отпаял, потому как в этм случае ток куда-то в него уходит, потребляется то бишь.
Простым вызовом halt() сразу после void main (void) { у меня получилось 35 мкА. манипуляции переводом в нужное состояние портов, выключений всего что нужно, удалось снизить потребление до 11 мкА. Иными словами, все пляски с бубнами, описанные даташиты, нужны там, где речь идет о борьбе за единицы микроампер.

Возвращаемся к моей китайской плате - она в режиме Halt жрет 21 мА. Если же отключить ее от программатора - 50 мА. То есть проблема в схемотехнике. Но на плате ошибок нет, потому как, повторюсь, с исходной прошивкой она жрала те же 11 мкА. То есть китайская прошивка прежде чем перевести в halt, как то мотивирует обвязку контроллера на голодание.

Господа не обессудьте, выкладываю то что набросал на листе бумаги. Понятно, что шлак, я не требую подсказать решение по этой схеме. Если дойдет до того, что в схеме дейтвительно кто-то "ворует электричество", а из-за ее качества непонятно, тогда нарисую в КАДе и выложу.
А сейчас выкладываю этот шлак на случай, если косяк лежит на поверхности и его не оставит труда заметить даже на такой схеме

Трехногая микруха слева - преоразователь питания на 3.3 вольт. Транзисторный ключ справа - управляет подсветкой. Неизвестный эемент вверху. левая нога которого идет к двум коненсаторам и земле - похожа на чип индуктивность. Микруха справа - драйвер дисплея. Питается с ноги PC4 14 пина (он отключен, если что).
Нижняя кнопка - она-то и включает и выключает устройство.

Может мне кто объяснит назначение МОСФЕТа внизу? Если вы обратите внимание, то с его истока проводник идет к LDO, питающему контроллер. То есть МОСФЕТ должен быть открыт. И открывается он внешним питанием, через резистор. Но видите ли, в этом случае эту функцию транзистора с успехом заменил бы обычный проводник. Но там еще два диода, развязывающие его от остальных частей схемы. Кстати, я дергал PD3 и в ноль и в плюс - управляя его затвором - эффекта не возымело.

Будут мысли у кого-нибудь?
jcxz
Цитата(zheka @ Aug 18 2018, 21:54) *
Возвращаемся к моей китайской плате - она в режиме Halt жрет 21 мА. Если же отключить ее от программатора - 50 мА. То есть проблема в схемотехнике. Но на плате ошибок нет,

А почему Вы вообще именно на STM8S экономию питания затеяли? Для малопотребляющих решений ведь естьSTM8L. Почему не его взяли???
И зачем брать отладку, на которой куча всего непонятного и потом кувыркаться с ней? Почему не взять пустую отладку и не гадать кто лишнее жрёт?
На STM8L, если верить даташиту, и в run-mode можно всего ~5.5 мкА получить. Без всяких halt-ов.
zheka
Цитата
А почему Вы вообще именно на STM8S экономию питания затеяли?

я же писал, что это своего рода реверс-инжиниринг. Есть китайский индикатор, в нем устраивало, кроме ряда моментов, на которые в рамках этой темы отвлекаться нецелесообразно. Поэтому я не разрабатываю новое, а перепрошиваю старое.

Цитата
Для малопотребляющих решений ведь естьSTM8L. Почему не его взяли???

Из предыдущего абзаца думаю понятно.

Цитата
И зачем брать отладку, на которой куча всего непонятного и потом кувыркаться с ней?

А на ней нет ничего непонятного. На ней из лишнего только светодиод питания и LDO на 3.3 вольт. Я их отпаял и получил то, что вы назвали пустой отладкой.

Цитата
Почему не взять пустую отладку и не гадать кто лишнее жрёт?

ПОвторюсь, я взял пустую отладку, но по ней не скажешь, кто жрет лишнее на другом устройстве, на индикаторе, который я перепрошиваю. А эксперименты пустой отладкой лишь позволили понять, что проблема не в коде. Что мой код на голой отладке способен обеспечить 11 мкА - тот самый ток, который потреблял индикатор со старой китайской прошивкой. А значит остальные миллиамперы жрутся исключительно обвязкой контроллера на индикаторе.



Я на Ваши вопросы ответил. Буду рад если Вы ответите на мои. В свете вышесказанного хочу на всякий случай обратить Ваше внимание, что тонкости режимов энергосбережения контроллера меня больше не интересуют - я добился того тока, который меня устраивает. Пусть бездумно, не изучив досконально предмет, но результат меня устраивает. И, как я уже сказал, от плясок с управлением режимами контроллера резуьтат не зависит. Пробема из цифровой плоскости переходит в аналоговую
Моя просьба, если она не ясна из предыдущих постов заключается в том, чтобы кто-то выказал мнение, что на приведенной мной выше схеме может жрать? И предположить, как китайцы это побороли, ведь устройство с первоначальной прошивкой жрало как и контроллер - 11 мкА.
Я уже отпаял транзисторный ключ подсветки и микросхему справа, к которой идут линии SPI от порта C контроллера. Не помогло.
jcxz
Цитата(zheka @ Aug 18 2018, 22:42) *
Я на Ваши вопросы ответил. Буду рад если Вы ответите на мои.

на Вашей "схеме" трудно что-то понять.
И если проблема в схеме, то почему-бы тогда просто не потыкаться амперметром в разные цепи и не выяснить - в каких цепях протекает потерянный ток?
zheka
Проблема решена. Всем спасибо.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.