Форум программистов, компьютерный форум, киберфорум
Наши страницы
Микроконтроллеры ARM, Cortex, STM32
Войти
Регистрация
Восстановить пароль
 
 
Рейтинг 4.67/3: Рейтинг темы: голосов - 3, средняя оценка - 4.67
_SayHello
765 / 460 / 148
Регистрация: 30.07.2015
Сообщений: 1,535
1

Прерывания в стиле С++

06.12.2018, 15:08. Просмотров 559. Ответов 31
Метки нет (Все метки)

Решил побаловаться и попробовать написать чего-нибудь на С++ под stm32. Чисто ради любопытства, накорябал класс Led (хотя наверное можно было накидать класс Output, а от него наследоваться =)).
типа того
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#ifndef LEDS_H_INCLUDED
#define LEDS_H_INCLUDED
 
#include "stm32f30x_gpio.h"
#include "stm32f30x_rcc.h"
 
#define LED_RESET   0
#define LED_SET     1
 
 
class Led
{
private:
    GPIO_TypeDef * LED_PORT;
    uint32_t LED_PIN;
    uint8_t State;
public:
    explicit Led(GPIO_TypeDef * port, uint32_t pin, uint8_t state = LED_RESET);
 
    void Set();
    void Reset();
    void Toogle();
    uint8_t GetState();
};
 
#endif /* LEDS_H_INCLUDED */


Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
Led::Led(GPIO_TypeDef * port, uint32_t pin, uint8_t state)
{
    LED_PORT = port;
    LED_PIN = pin;
 
    if(port == GPIOA)
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
    else if(LED_PORT == GPIOB)
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
    else if(LED_PORT == GPIOC)
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);
    else if(LED_PORT == GPIOD)
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOD, ENABLE);
    else if(LED_PORT == GPIOE)
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOE, ENABLE);
    else if(LED_PORT == GPIOF)
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOF, ENABLE);
 
    GPIO_InitTypeDef gpio;
 
    gpio.GPIO_Pin = LED_PIN;
    gpio.GPIO_Speed = GPIO_Speed_2MHz;
    gpio.GPIO_Mode = GPIO_Mode_OUT;
    gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(LED_PORT, &gpio);
 
    if(state == LED_SET)
    {
        GPIO_SetBits(LED_PORT, LED_PIN);
        State = LED_SET;
    }
    else
    {
        GPIO_ResetBits(LED_PORT, LED_PIN);
        State = LED_RESET;
    }
}
 
void Led::Set()
{
    GPIO_SetBits(LED_PORT, LED_PIN);
    State = LED_SET;
}
 
void Led::Reset()
{
    GPIO_ResetBits(LED_PORT, LED_PIN);
    State = LED_RESET;
}
 
void Led::Toogle()
{
    LED_PORT->ODR ^= LED_PIN;
    if(State)
        State = LED_RESET;
    else
        State = LED_SET;
}
 
uint8_t Led::GetState()
{
    return State;
}


Так вот, прикольно, и мне понравилось. Все работает. Но возник вопрос, как например поморгать светодиодом в обработчике прерываний? Если мы объявим объекты в main их будет не видно в обработчиках.
Будучи явно уверенным, что глобально объект объявить нельзя, сделал так:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include "Leds.h"
 
Led LED1(GPIOE, GPIO_Pin_12);
Led LED2(GPIOE, GPIO_Pin_13);
 
int main(void)
{
    SystemInit();
 
    while(1)
    {
        for(uint32_t i = 0; i < 0xFFFFF; i++){};
        LED1.Toogle();
        LED2.Toogle();
    }
}
И оно заработало. Хотя я почему то был уверен, что функции до main вызывать нельзя и компилятор ругаться будет. Проверил в Qt действительно, и там на вызов перед main не ругается.
Может кто уже с этим разбирался, подскажите как правильно, и чем черевата такая писанина?
0
QA
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
06.12.2018, 15:08
Ответы с готовыми решениями:

Прерывания
Здравствуйте. С горем пополам с немалой помощью умов этого форума удалось мне собрать свой проект....

Прерывания
Добрый день подскажите вот я включаю прервания и настраиваю приоритет (у всех один): /*...

Прерывания
Подскажите пожалуйста. Использую дискавери F4 Настроил внешние прерывания. В обработчик...

Прерывания
Ситуация следующая. Есть двигатель, к нему прилеплена оптопара(светодиод-фототранзистор). Нужно...

Возможно ли преобразование в стиле БПФ для нелинейных чирпов
Пусть имеется источник сигналов с нелинейно меняющейся со временем частотой, например упрощенно...

31
ValeryS
Модератор
7905 / 5882 / 773
Регистрация: 14.02.2011
Сообщений: 20,223
Завершенные тесты: 1
06.12.2018, 18:17 2
Цитата Сообщение от _SayHello Посмотреть сообщение
И оно заработало.
а почему не должна?
вопросы по Си, плюсам в соответствующих ветках
Цитата Сообщение от _SayHello Посмотреть сообщение
Но возник вопрос, как например поморгать светодиодом в обработчике прерываний?
так сделал или нет?
здесь другая засада, искажения имен
просто объявив функцию, обработчик её не найдет
нужен extern"C"
1
Void1509
4 / 4 / 0
Регистрация: 17.09.2018
Сообщений: 41
06.12.2018, 18:22 3
Простите, а в каком месте здесь прерывания ?
0
Humanoid
Почетный модератор
10096 / 3959 / 370
Регистрация: 12.06.2008
Сообщений: 11,575
06.12.2018, 23:32 4
Цитата Сообщение от _SayHello Посмотреть сообщение
и чем черевата такая писанина?
Вы сами уже сделали вывод, что чревата путаницей с последовательностью вызовов. Например, объекты Led1 и Led2 будут созданы ещё до вызова SystemInit(), в котором настраиваются PLL. Если бы классы управляли не светодиодами, а настраивали какое-то оборудование, зависящее от частоты, то поведение оказалось бы неожиданным. Так же, например, если бы класс ожидал, что клоки уже включены, а клоки включаются в main(), который ещё не начал выполняться, то при обращении к выключенному блоку было бы непредсказуемое поведение. Хотя, это легко обойти если использовать указатели на классы, а создавать их уже в main().
На сколько я знаю, плюсовое приложение использует чуть больше ресурсов (например, памяти), из-за чего плюсы редко используют для микроконтроллеров.
0
06.12.2018, 23:32
locm
2280 / 996 / 126
Регистрация: 28.10.2011
Сообщений: 3,367
Записей в блоге: 6
06.12.2018, 23:43 5
Цитата Сообщение от Humanoid Посмотреть сообщение
Так же, например, если бы класс ожидал, что клоки уже включены, а клоки включаются в main().
В стартупе.
Если посмотреть стандартный стартуп куба, то видно что SystemInit вызывается до функции __libc_init_array в которой если не ошибаюсь вызываются конструкторы классов.
0
_SayHello
765 / 460 / 148
Регистрация: 30.07.2015
Сообщений: 1,535
07.12.2018, 09:57  [ТС] 6
Цитата Сообщение от ValeryS Посмотреть сообщение
а почему не должна?
Почему то никогда об этом не задумывался, но было стойкое "убеждение", что за пределами main нет жизни, только разве что объявить переменные. Освежил немного знания.
Void1509, прерываний здесь пока нет, вопрос был больше на упреждение
Цитата Сообщение от ValeryS Посмотреть сообщение
так сделал или нет?
здесь другая засада, искажения имен
просто объявив функцию, обработчик её не найдет
нужен extern"C"
Вчера вечером поленился воткнуть прерывания. А вот сегодня за инфу спасибо, IDE в упор отказывалась видеть обработчик прерывания. Сам бы не подумал про extern "С". В своих проектах никогда не использовал, видел только у других, но когда спрашивал на кой этот extern, говорили "хз, все так делают" или "взял откуда-то уже с этим".
Так работает нормально
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include "Leds.h"
#include "stm32f30x_conf.h"
 
Led LED1(GPIOE, GPIO_Pin_12);
Led LED2(GPIOE, GPIO_Pin_13);
 
int main(void)
{
    SystemInit();
 
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);
 
    TIM_TimeBaseInitTypeDef timer;
    TIM_TimeBaseStructInit(&timer);
    timer.TIM_Prescaler =   64000 - 1;
    timer.TIM_Period    =   1000 - 1;
    TIM_TimeBaseInit(TIM7, &timer);
    TIM_ITConfig(TIM7, TIM_IT_Update, ENABLE);
 
    TIM_Cmd(TIM7, ENABLE);
 
    NVIC_InitTypeDef nvic;
    nvic.NVIC_IRQChannel    =   TIM7_IRQn;
    nvic.NVIC_IRQChannelCmd =   ENABLE;
    NVIC_Init(&nvic);
 
    while(1)
    {
 
    }
}
 
extern "C"
{
    void TIM7_IRQHandler()
    {
        if(TIM_GetITStatus(TIM7, TIM_IT_Update))
        {
            TIM_ClearITPendingBit(TIM7, TIM_IT_Update);
            LED1.Toogle();
            LED2.Toogle();
        }
    }
}


Humanoid,
Цитата Сообщение от Humanoid Посмотреть сообщение
а сколько я знаю, плюсовое приложение использует чуть больше ресурсов (например, памяти), из-за чего плюсы редко используют для микроконтроллеров.
Как раз этот вопрос был одним из интересующих. Наверное уже миллион раз все проверяли, но вот мои замеры:
Вызов двух конструкторов из класса Leds инициализирующие два пина на выход и устанавливающие их в логическую 1
Прерывания в стиле С++

Вызов двух функций в стиле Си делающих тоже самое
Прерывания в стиле С++

Тут понятно конечно, что чем больше приватных данных тем больше объект с собой таскать будет всегда, но разница не такая уж и большая. Другое дело, что плюсы С++ не использовать в явном виде, разве что только оформление в виде классов и объектов ибо контейнеры использовать на МК это уже перебор.
Цитата Сообщение от Humanoid Посмотреть сообщение
Например, объекты Led1 и Led2 будут созданы ещё до вызова SystemInit(), в котором настраиваются PLL.
Меня это тоже смутило, на светодиодах это не особо критично, на периферии сложнее уже неприятно. Можно конечно решить, таким образом. Оставить конструктор таким:

C
1
2
3
4
5
Led::Led(GPIO_TypeDef * port, uint32_t pin)
{
    LED_PORT = port;
    LED_PIN = pin;
}
И вызывать его до main. И добавить метод:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void Led::Init(uint8_t State)
{
    if(port == GPIOA)
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
    else if(LED_PORT == GPIOB)
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
    else if(LED_PORT == GPIOC)
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);
    else if(LED_PORT == GPIOD)
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOD, ENABLE);
    else if(LED_PORT == GPIOE)
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOE, ENABLE);
    else if(LED_PORT == GPIOF)
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOF, ENABLE);
 
    GPIO_InitTypeDef gpio;
 
    gpio.GPIO_Pin = LED_PIN;
    gpio.GPIO_Speed = GPIO_Speed_2MHz;
    gpio.GPIO_Mode = GPIO_Mode_OUT;
    gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(LED_PORT, &gpio);
 
    if(state == LED_SET)
    {
        GPIO_SetBits(LED_PORT, LED_PIN);
        State = LED_SET;
    }
    else
    {
        GPIO_ResetBits(LED_PORT, LED_PIN);
        State = LED_RESET;
    }    
}
И вызывать его в main
0
ValeryS
Модератор
7905 / 5882 / 773
Регистрация: 14.02.2011
Сообщений: 20,223
Завершенные тесты: 1
07.12.2018, 10:50 7
Цитата Сообщение от _SayHello Посмотреть сообщение
Почему то никогда об этом не задумывался, но было стойкое "убеждение", что за пределами main нет жизни,
"Есть , но не про вашу честь"
Цитата Сообщение от _SayHello Посмотреть сообщение
только разве что объявить переменные.
Я ведь не зря посылал в специализированные ветки
объявить переменные, с точки зрения плюсов, это вызвать конструктор( даже пустой), а это та же функция
Цитата Сообщение от _SayHello Посмотреть сообщение
на кой этот extern,
В плюсах можно объявить функции с одним именем
C++
1
2
int MyFunc(int a,int b);
int MyFunc(double a,double b);
и все скомпилируется
а как?
просто компилятор преобразует их в вид
MyFunc_int_int
MyFunc_double_double
( имена условные, каждый компилятор изменяет их по своему, в зависимости от внутреннего стандарта)
и линкер понимает что это разные функции
это и есть искажение имен
но, для обработчика прерываний нужны четкие имена
для этого и добавляют extern"C", т.е в стиле С
тогда обе функции будут выглядеть так
MyFunc
MyFunc

Цитата Сообщение от _SayHello Посмотреть сообщение
Как раз этот вопрос был одним из интересующих.
я тоже как то это делал, класс Ножка, класс Порт, но дальше экспериментов дело не пошло,
заплюхивался я этими в стиле Си в стиле С++
Классы при програмировании STM32
1
_SayHello
765 / 460 / 148
Регистрация: 30.07.2015
Сообщений: 1,535
07.12.2018, 11:03  [ТС] 8
ValeryS,
Цитата Сообщение от ValeryS Посмотреть сообщение
я тоже как то это делал, класс Ножка, класс Порт, но дальше экспериментов дело не пошло,
заплюхивался я этими в стиле Си в стиле С++
Да я читал данную тему, тогда я правда был еще только на начальном этапе изучения Си )))

Просто начинаю замечать за собой, что начинаю всякие общие вещи в проектах пытаться затолкать в структуры, чтобы один раз проинициализировал ее со всеми параметрами в main, а дальше во все функции связанные с ней только указатель на нее передавать. Чтобы при переносе в другой проект, только инициализацию структуры поменять и все.
Типа того :
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
LED_TypeDef LEDS[17] = {{"Fan ON",    GPIOE, GPIO_Pin_0},
                        {"Sound OFF", GPIOE, GPIO_Pin_1},
                        {"Light ON",  GPIOE, GPIO_Pin_2},
                        {"Tmax",      GPIOE, GPIO_Pin_3},
                        {"Socket ON", GPIOE, GPIO_Pin_4},
                        {"Screen",    GPIOE, GPIO_Pin_5},
                        {"Ten ON",    GPIOE, GPIO_Pin_6},
                        {"Flow 1",    GPIOE, GPIO_Pin_7},
                        {"Flow 2",    GPIOE, GPIO_Pin_8},
                        {"Flow 3",    GPIOE, GPIO_Pin_9},
                        {"Flow 4",    GPIOE, GPIO_Pin_10},
                        {"Flow 5",    GPIOE, GPIO_Pin_11},
                        {"Flow 6",    GPIOE, GPIO_Pin_12},
                        {"Flow 7",    GPIOE, GPIO_Pin_13},
                        {"Flow 8",    GPIOC, GPIO_Pin_12},
                        {"Flow 9",    GPIOC, GPIO_Pin_11},
                        {"Auto",      GPIOC, GPIO_Pin_10}};
И дальше работать в таком стиле
C
1
  Init_LED(17, LEDS);
А по ходу дела въехал еще в С++ для верхнего уровня и видимо начались естественные сравнения с классами и объектами.
0
Rius
Эксперт .NET
6122 / 3960 / 952
Регистрация: 25.05.2015
Сообщений: 12,027
Записей в блоге: 12
Завершенные тесты: 4
08.12.2018, 01:13 9
Цитата Сообщение от _SayHello Посмотреть сообщение
Цитата Сообщение от Humanoid Посмотреть сообщение
Например, объекты Led1 и Led2 будут созданы ещё до вызова SystemInit(), в котором настраиваются PLL.
Меня это тоже смутило, на светодиодах это не особо критично, на периферии сложнее уже неприятно. Можно конечно решить, таким образом. Оставить конструктор таким:

C
1
2
3
4
5
Led::Led(GPIO_TypeDef * port, uint32_t pin)
{
    LED_PORT = port;
    LED_PIN = pin;
}
И вызывать его до main.
Можно собрать все нужные классы в один общий класс (в качестве полей), скажем, Application, который инициализировать в main() через placement new. Тогда последовательность инициализации будет заведомо известна и без копания в области до main().

Цитата Сообщение от _SayHello Посмотреть сообщение
а дальше во все функции связанные с ней только указатель на нее передавать
Лучше ссылку. Она не может вдруг оказаться равной null.
0
Void1509
4 / 4 / 0
Регистрация: 17.09.2018
Сообщений: 41
09.12.2018, 02:35 10
Через объекты весело, но без них никакую память выделять не надо
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
uint32_t const ports[]={GPIOA_BASE, GPIOB_BASE, GPIOC_BASE, GPIOA_BASE, GPIOA_BASE, GPIOA_BASE, GPIOA_BASE};
 
void port_setup(uint8_t pp, uint8_t mod, uint8_t cnf) {
    GPIO_TypeDef *prt = (GPIO_TypeDef*)ports[((pp >> 4) & 7)];
    if (pp & 8) {
        prt->CRH &= ~ (0xf << ((pp & 0x7) << 2));
        prt->CRH |= ((mod | (cnf << 2)) << ((pp & 0x7) << 2));
    } else {
        prt->CRL &= ~ (0xf << ((pp & 0x7) << 2));
        prt->CRL |= ((mod | (cnf << 2)) << ((pp & 0x7) << 2));
    }
}
void port_set(uint8_t pp, uint8_t en) {
    GPIO_TypeDef *prt = (GPIO_TypeDef*)ports[((pp >> 4) & 7)];
    prt->BSRR = (en)? (1 << (pp & 0xf)):(1 << ((pp & 0xf) + 0x10));
}
где pp - старшие 4-ре бита номер порта, младшие номер пина.
ЗЫ: Так сказать меняю ОЗУ на флэш.

Добавлено через 10 минут
или можно лаконичней
C
1
2
3
4
5
void port_setup(uint8_t pp, uint8_t mod, uint8_t cnf) {
    uint32_t *prt = (uint32_t*)(ports[((pp >> 4) & 7)] + ((pp & 8)?4:0));
    *prt &= ~ (0xf << ((pp & 0x7) << 2));
    *prt |= ((mod | (cnf << 2)) << ((pp & 0x7) << 2));
}
0
Voland_
1724 / 1063 / 104
Регистрация: 04.01.2010
Сообщений: 3,637
15.12.2018, 13:02 11
...подобные структуры в общем даже очень применимы в применении со `static inline` функциями. Компилятор дожмет ее до состояния присваивания или RMW в крайнем случае... В принципе последние компилеры и так ее "дожмут", но вообще - структуры динамического сдвига занимают много места и времени в коде. Типа таких:

*prt &= ~ (0xf << ((pp & 0x7) << 2));
0
Void1509
4 / 4 / 0
Регистрация: 17.09.2018
Сообщений: 41
15.12.2018, 14:27 12
Доброго времени суток !

Цитата Сообщение от Voland_ Посмотреть сообщение
но вообще - структуры динамического сдвига занимают много места и времени в коде
Вы ошибаетесь, сдвиг(циклический, арифметический), на всех (по крайней мере что мне попадались Intel,M68k,MIPS,LSI-11) платформах, были самыми легковесными(в смысле тактов) командами выполняющимися за 1 такт. В случае же с ARM дело обстоит еще лучше. В ARM есть специальная матрица со сдвигом от 0..31, при загрузке операнда он проходит через эту матрицу и в регистр процессора заносится из соответствующего элемента матрицы. Таким образом, в ARM, грузите вы операнд без сдвига или со сдвигом - это вопрос всего лишь из какого элемента матрицы (0-го - не 0-го) будут данные перенесены в регистр и отдельных тактов процессора (да же одного) сдвиг не требует. Место в коде в ARM занимать не возможно - команды выровнены по длине и занимают 32-ва бита (в thumb режиме 16 бит), и занимать больше или меньше они не могут.
0
Voland_
1724 / 1063 / 104
Регистрация: 04.01.2010
Сообщений: 3,637
15.12.2018, 18:32 13
Цитата Сообщение от Void1509 Посмотреть сообщение
Вы ошибаетесь, сдвиг(циклический, арифметический), на всех (по крайней мере что мне попадались Intel,M68k,MIPS,LSI-11) платформах, были самыми легковесными(в смысле тактов) командами выполняющимися за 1 такт.
Вы ж сами себе противоречите. Чтобы сдвинуть значение на N бит, нужно сделать цикл на N итераций. И никакой pipeline не сделает вам итерацию за "1" такт. Даже в оптимальном AVR переход будет стоить 3 такта, ну, еще один сдвиг, то есть 4 такта на 1 бит сдвига. А за Intel и т.д. я вообще молчу. Либо вы что-то не договариваете. То есть да, можно конечно логарифмами это сделать, то есть сопроцессором, но это совсем другая история.

Цитата Сообщение от Void1509 Посмотреть сообщение
В случае же с ARM дело обстоит еще лучше. В ARM есть специальная матрица со сдвигом от 0..31, при загрузке операнда он проходит через эту матрицу и в регистр процессора заносится из соответствующего элемента матрицы. Таким образом, в ARM, грузите вы операнд без сдвига или со сдвигом - это вопрос всего лишь из какого элемента матрицы (0-го - не 0-го) будут данные перенесены в регистр и отдельных тактов процессора (да же одного) сдвиг не требует.
Интересно - закиньте ссылку на команду ассемблера, почитаю.
0
Void1509
4 / 4 / 0
Регистрация: 17.09.2018
Сообщений: 41
15.12.2018, 18:45 14
в общих чертах - где-то так https://www.youtube.com/watch?v=rXW-CLByNDs На скорую руку ничего подробней не нашел, но устройство сдвига отображено.
По поводу ассемблера выглядит это примерно так
ADDEQ R2,R4,R5 ; Если Z = 1, то R2:=R4+R5
TEQS R4,#3 ; Проверить R4 на равенство 3
; (ассемблер автоматически установит в единицу бит S)
SUB R4,R5,R7,LSR R2 ; Логический сдвиг вправо R7 на такое число бит,
; которое указано в старшем байте регистра R2,
; затем вычесть результат из R5 и разместить его в R4.
MOV PC,R14 ; Возврат из подпрограммы.
MOVS PC,R14 ; Возврат из исключения и восстановление CPSR
; из SPSR_mode.
а вот ссылка на статью целиком
http://www.gaw.ru/html.cgi/txt/doc/m...ssing_data.htm
0
Voland_
1724 / 1063 / 104
Регистрация: 04.01.2010
Сообщений: 3,637
15.12.2018, 19:19 15
...чуть поэкспериментировал, под рукой был AVR:

C
1
2
3
4
5
6
7
unsigned int a;
volatile unsigned char offset;
volatile unsigned int b;
 
a=5;
offset=3;
b=a<<offset;
Вот кусок ассемблера, который делает битовый сдвиг:

Assembler
1
2
3
4
5
6
7
8
9
10
11
12
__LSLW12:
    TST  R30
    MOV  R0,R30
    MOVW R30,R26
    BREQ __LSLW12R
__LSLW12L:
    LSL  R30
    ROL  R31
    DEC  R0
    BRNE __LSLW12L
__LSLW12R:
    RET
То есть с прямой настройкой GPIO это может работать в одну инструкцию, командами sbi/cbi, или всем портом вообще. А вот с динамическим смещением все это будет бесполезной фичей.

Добавлено через 5 минут
Цитата Сообщение от Void1509 Посмотреть сообщение
а вот ссылка на статью целиком
http://www.gaw.ru/html.cgi/txt/doc/m...ssing_data.htm
ну в общем да, в ARM'ах это есть, это я знал. просто Вы озвучили
Цитата Сообщение от Void1509 Посмотреть сообщение
Intel,M68k,MIPS,LSI-11
и я не очень понимаю, чем эти названия с ним связаны - просто не со всеми сталкивался.

Добавлено через 11 минут
...немного по-ковырял вопрос - в общем да, решение сильно зависит от платформы. Скажем, на Z80, STM8, AVR (и еще нескольких, которые называть здесь просто не могу) шифт на динамическое количество бит не существует. но заглянут в кортексы и интел - у них есть такая команда.
0
Void1509
4 / 4 / 0
Регистрация: 17.09.2018
Сообщений: 41
15.12.2018, 20:34 16
ТС указал, что речь об stm32 - то есть об ARM. Сравнивать AVR и ARM - это как сотовый начала 90-х и современный телефон. В смысле и то, и то электроника. Пример - листинг сгенерированный gcc из приведенного выше Си кода
8:../src/gpio.c **** *prt |= ((mod | (cnf << 2)) << ((pp & 0x7) << 2));
57 .loc 1 8 0
58 0020 41EA8202 orr r2, r1, r2, lsl #2
59 .LVL3:
7:../src/gpio.c **** *prt &= ~ (0xf << ((pp & 0x7) << 2));
60 .loc 1 7 0
61 0024 23EA0503 bic r3, r3, r5
62 .loc 1 8 0
63 0028 02FA00F0 lsl r0, r2, r0
64 002c 0343 orrs r3, r3, r0
65 002e A351 str r3, [r4, r6]
9:../src/gpio.c ****
в строке 58 обратите внимание на операцию ИЛИ со сдвигом второго операнда на 2.

Intel,M68k,MIPS,LSI-11
это платформы на которых операции сдвига являются самыми легковесными (в смысле тактов) относительно остальных команд. На некоторых команда смещения может быть сразу на несколько разрядов, на некоторых (intel) есть предикат REP/REPE/REPZ/REPNE/REPNZ который позволяет несколько раз повторить команду не загружая ее из памяти(короче ускорено). Это я написал к тому, что вы утверждали, что операции сдвига - тяжеловесны.

Добавлено через 1 час 10 минут
Вернемся к ТСу. Для атомарного доступа к битам в stm32 как и в многих ARM системах, присутствует механизм Bit Banding. Который позволяет менять нужный бит вообще в одно касание, без использования сдвигов.
0
_SayHello
765 / 460 / 148
Регистрация: 30.07.2015
Сообщений: 1,535
15.12.2018, 20:44  [ТС] 17
Да вы вроде как уже не в ту степь пошли) Тут не стоял вопрос про экономию ресурсов и счет тактов и даже работы с пинами, тут скорее вопрос был про удобство применимости С++ инструментов для написания кода, благо ресурсы современных stm32 позволяют себе таким заниматься.
Когда встают вопросы к минимальному ресурсу и максимальному быстродействию, проще использовать асм напрямую.
0
Void1509
4 / 4 / 0
Регистрация: 17.09.2018
Сообщений: 41
15.12.2018, 21:06 18
Применение С++ в безОСом программировании контроллеров затруднено отсутствием менеджера памяти. Так как используется куча (стек наоборот), программисту приходится следить за очередностью создания и удаления объектов. Ну и Ваш пример выбран не удачно, так как периферия вам дана производителем контроллера и оборачивать ее в класс просто бессмысленно. Так как вы не можете создать экземпляр(объект) вашей периферии и тем более удалить. У вас просто есть периферия, предусмотренная производителем, такая как есть. При использовании операционных систем (FreeRTOS) С++, вполне применимы, а при построении каких-то интерактивных приложений с большим ассортиментом событий, даже полезно.
0
_SayHello
765 / 460 / 148
Регистрация: 30.07.2015
Сообщений: 1,535
15.12.2018, 21:26  [ТС] 19
Void1509, да никто и не спорит с менеджером памяти, я это выше уже писал. По той же причине закрыто и использование контейнерный классов. Создать то я как раз могу объект. Не динамически, но все таки. Удалить - да не могу. Бессмысленно с точки зрения использования ресурса. С точки зрения работы с кодом вполне есть смысл.
Либо объектами:
C
1
2
Led LED1(GPIOE, GPIO_Pin_12);
LED1.Set();
Либо структурами:
C
1
2
Led LED1 = {GPIOE, GPIO_Pin_12};
LED1.Port->BSSR = LED1.Pin;
Либо по простому
C
1
GPIOE->BSSR = GPIO_Pin_12;
Либо как у вас, тут уже вопрос вкуса.
0
Void1509
4 / 4 / 0
Регистрация: 17.09.2018
Сообщений: 41
15.12.2018, 22:04 20
Простите, не могу поймать нить Вашей мысли. Вы писали -
Цитата Сообщение от _SayHello Посмотреть сообщение
С точки зрения работы с кодом вполне есть смысл.
, но в приведенных Вами 3-х примерах - самый простой и понятный третий. Давайте их разберем подробней, с точки зрения программирования контроллеров. И так - у Вас есть некий девайс, где к пину 1 порта А и пину 2 порта С припаяны светодиоды, которыми Вы собираетесь управлять. Вы создаете класс Led, который позволяет вам инициализировать и управлять произвольным битом произвольного порта. Но этих бита у Вас ровно 2, ни больше ни меньше. И в процессе эксплуатации девайса, эти биты не изменятся(их не станет 1 или 3). Поясните - в чем смысл описания класса и создания 2-ух объектов.
В то же время если использовать препроцессор, допустим
C
1
2
3
4
#define       LD1ON         GPIOA->BSSR |= GPIO_Pin_1
#define       LD1OFF        GPIOA->BSSR |= (GPIO_Pin_1 << 16)
#define       LD2ON         GPIOC->BSSR |= GPIO_Pin_2
#define       LD2OFF        GPIOC->BSSR |= (GPIO_Pin_2 << 16)
Никаких функций сгенерировано не будет и память для объектов выделяться не будет и читаемость нормальная - диод включил - выключил. Вставляй в теле программы где нравится и тому кто читает код - сразу понятно что происходит.

ЗЫ: Константные смещения в процессе компиляции будут заменены цифрами, фактически, каждая строка будет заменена 1-й командой процессора.
0
15.12.2018, 22:04
Answers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
15.12.2018, 22:04

Прерывания MSP430
Здравствуйте! Написал программу для обмена данными с температурного датчика и компьютера через...

Прерывания в STM32F107VBT6
Доброго времени суток. Я навичек в ARMах, не могу запустить прерывания на кристале STM32F107VBT6....

Внешние прерывания
Кто-нить вкурсе есть ли у СТМов флаги внешних прерываний портов, а если есть то где их найти? А то...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2019, vBulletin Solutions, Inc.