Форум программистов, компьютерный форум, киберфорум
Наши страницы
Микроконтроллеры ARM, Cortex, STM32
Войти
Регистрация
Восстановить пароль
 
_SayHello
575 / 329 / 108
Регистрация: 30.07.2015
Сообщений: 1,202
1

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

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

Решил побаловаться и попробовать написать чего-нибудь на С++ под 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
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
06.12.2018, 15:08
Ответы с готовыми решениями:

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

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

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

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

Прерывания по USART
Никак не могу разобраться. Вот очень простая программа: присылаем что-нибудь через терминал на...

9
ValeryS
Модератор
7372 / 5573 / 707
Регистрация: 14.02.2011
Сообщений: 18,942
06.12.2018, 18:17 2
Цитата Сообщение от _SayHello Посмотреть сообщение
И оно заработало.
а почему не должна?
вопросы по Си, плюсам в соответствующих ветках
Цитата Сообщение от _SayHello Посмотреть сообщение
Но возник вопрос, как например поморгать светодиодом в обработчике прерываний?
так сделал или нет?
здесь другая засада, искажения имен
просто объявив функцию, обработчик её не найдет
нужен extern"C"
1
Void1509
2 / 2 / 0
Регистрация: 17.09.2018
Сообщений: 15
06.12.2018, 18:22 3
Простите, а в каком месте здесь прерывания ?
0
Humanoid
Почетный модератор
9982 / 3868 / 348
Регистрация: 12.06.2008
Сообщений: 11,401
06.12.2018, 23:32 4
Цитата Сообщение от _SayHello Посмотреть сообщение
и чем черевата такая писанина?
Вы сами уже сделали вывод, что чревата путаницей с последовательностью вызовов. Например, объекты Led1 и Led2 будут созданы ещё до вызова SystemInit(), в котором настраиваются PLL. Если бы классы управляли не светодиодами, а настраивали какое-то оборудование, зависящее от частоты, то поведение оказалось бы неожиданным. Так же, например, если бы класс ожидал, что клоки уже включены, а клоки включаются в main(), который ещё не начал выполняться, то при обращении к выключенному блоку было бы непредсказуемое поведение. Хотя, это легко обойти если использовать указатели на классы, а создавать их уже в main().
На сколько я знаю, плюсовое приложение использует чуть больше ресурсов (например, памяти), из-за чего плюсы редко используют для микроконтроллеров.
0
locm
2168 / 895 / 116
Регистрация: 28.10.2011
Сообщений: 2,859
Записей в блоге: 6
06.12.2018, 23:43 5
Цитата Сообщение от Humanoid Посмотреть сообщение
Так же, например, если бы класс ожидал, что клоки уже включены, а клоки включаются в main().
В стартупе.
Если посмотреть стандартный стартуп куба, то видно что SystemInit вызывается до функции __libc_init_array в которой если не ошибаюсь вызываются конструкторы классов.
0
_SayHello
575 / 329 / 108
Регистрация: 30.07.2015
Сообщений: 1,202
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
Модератор
7372 / 5573 / 707
Регистрация: 14.02.2011
Сообщений: 18,942
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
575 / 329 / 108
Регистрация: 30.07.2015
Сообщений: 1,202
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
5131 / 3351 / 820
Регистрация: 25.05.2015
Сообщений: 10,294
Записей в блоге: 11
Завершенные тесты: 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
2 / 2 / 0
Регистрация: 17.09.2018
Сообщений: 15
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
09.12.2018, 02:35
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
09.12.2018, 02:35

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

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

Внешние прерывания
Доброе время суток! Подскажите пожалуйста как настроить внешнее прерывание на еще одном порте. Я...


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

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

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