|
|
  |
Библиотеки для STM32 |
|
|
|
Apr 20 2017, 07:37
|

Профессионал
    
Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831

|
Цитата(juvf @ Apr 20 2017, 10:32)  тогел потоко не защищённый. Осторожно, тут мины!! )) Я же говорил, что к одному и тому же пину у меня в коде НИКОГДА нет обращения их РАЗНЫХ потоков. Это исключено еще на этапе построения проекта. Поэтому не будет ничего страшного, если кто-то прервет эту строчку с ODR. В крайнем случае могу использовать CriticalSection, если нужно сохранить строгую последовательность некого пинодрыга, но пока еще с таким я не сталкивался.
--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
|
|
|
|
|
Apr 20 2017, 07:42
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(Forger @ Apr 20 2017, 12:28)  Цитата(juvf @ Apr 20 2017, 12:21)  вы в одном потоке дергаете cs? который есть PA13, а в другом потомке маргаете диодом который сидит на PA5.
Ага, я понял к чему вы - я имел ввиду не порт, а пин. Но и в данном случае проблем тоже не будет - в MODER пишется маска, меняющая всего лишь ОДИН соотв. бит, а не группу битов. Ничего вы не поняли. В вашем коде две проблемы: 1. Проблема с двухэтапной записью в MODER. Выше уже очень хорошо объяснили, что это нехорошо. Потому что ножка переводится в промежуточное состояние, которое может быть нежелательным. И при возникновении прерывания между двумя записями ножка может остаться в этом состоянии достаточно продолжительное время. Этого может быть достаточно, чтобы что-нибудь сжечь. Это реальная мина, потому что на столе может всё работать, а в продакшене может рвануть. 2. Проблема с неатомарностью операции GPIOA->ODR ^= bit. (Об этом вам пишет juvf). Дело в том, что если такие операции выполняются одновременно из разных потоков (к разным битам одного порта), то возможны сбои. Разжёвывать не буду, гуглите. ЗЫ. Рано вы взялись за "Чистый код". Надо было сначала основы подтянуть.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Apr 20 2017, 07:49
|

Профессионал
    
Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831

|
Цитата(AHTOXA @ Apr 20 2017, 10:42)  1. Проблема с двухэтапной записью в MODER. Выше уже очень хорошо объяснили, что это нехорошо. Потому что ножка переводится в промежуточное состояние, которое может быть нежелательным. И при возникновении прерывания между двумя записями ножка может остаться в этом состоянии достаточно продолжительное время. Этого может быть достаточно, чтобы что-нибудь сжечь. Это реальная мина, потому что на столе может всё работать, а в продакшене может рвануть. В моих схемах ни одна ножка не висит в воздухе, всегда есть внешняя подтяжка, это связано с непредсказуемым поведение схемы, пока проц в состоянии сброса или прошивается. Поэтому в моих проектах это не создает никаких проблем )) Внутренние пуллапы может быть и полезны разве что для входных пинов, т.е. те, которые всегда настроены на вход. Цитата 2. Проблема с неатомарностью операции GPIOA->ODR ^= bit. (Об этом вам пишет juvf). Дело в том, что если такие операции выполняются одновременно из разных потоков (к разным битам одного порта), то возможны сбои. Разжёвывать не буду, гуглите. Вы хотите сказать, что операция обращения к ODR неатомарнае, например, копирование из регистра R2 -> [R3], где R3 хранит адрес ODR-регистра? Вы точно ничего не путаете?
--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
|
|
|
|
|
Apr 20 2017, 07:51
|

Профессионал
    
Группа: Свой
Сообщений: 1 261
Регистрация: 14-05-09
Из: Челябинск
Пользователь №: 49 045

|
Цитата(Forger @ Apr 20 2017, 12:37)  Я же говорил, что к одному и тому же пину у меня в коде НИКОГДА нет обращения их РАЗНЫХ потоков. Это исключено еще на этапе построения проекта. Поэтому не будет ничего страшного, если кто-то прервет эту строчку с ODR. В крайнем случае могу использовать CriticalSection, если нужно сохранить строгую последовательность некого пинодрыга, но пока еще с таким я не сталкивался. просто ужос!!!! такие элементарные вещи... вроде разживал все... выделил жирным шрифтом, что при изменении РАЗНЫХ ПИНОВ РАЗНЫХ!!! ну не видите вы сами... я же вам привел пример... РА15 и РА5 пошагам.... пусть cs будет РА1, а led будет PA4. воспользуемся литералом b. для кратности пусть порт будет 8 бит. изночально в ODR 0b0000'0000 вы в одном поторке выитали ODR, он равен нулю. второй поток прервал первый. второй поток вычитал ODR и добавил туда cs, стало 0b0000'0010. теперь записал это в ODR. Сs стал "1". Вернулись в первый поток... 0b0000'0000 добавили бит светодиода, получили 0b0001'0000 - записали в ODR - всё!!! CS сбросился! Упс!!! раз вы пишете на RTOS, должны знать про неатомарные операции Цитата Вы хотите сказать, что операция обращения к ODR неатомарная, например, копирование из регистра R2 -> [R3], где R3 хранит адрес ODR-регистра? БИНГО!!!!
|
|
|
|
|
Apr 20 2017, 07:56
|
Участник

Группа: Участник
Сообщений: 48
Регистрация: 15-07-06
Пользователь №: 18 836

|
Цитата(juvf @ Apr 20 2017, 06:50)  естественно.... так же как и в шаблонах/классах Для класса ты пишешь всякие on/off один раз и они точно будут рабочие. На макросах ты пишешь намного больше и можешь случайно ошибиться: Код #define csOn() (GPIOA->BSRR = GPIOA_BSRR_BR4) #define csOff() (GPIOA->BSRR = GPIOA_BSRR_BS4)
#define led1On() (GPIOA->BSRR = GPIOA_BSRR_BR5) #define led1Off() (GPIOA->BSRR = GPIOA_BSRR_BS5) ... Цитата чего? На радиокоте недавно спрашивали как записать 7 бит заданного байта в PD9, 5 бит в РВ4 и т.д.... Дали парочку ответов, лучший из которых выглядит так: Код GPIOD->BSRR = b & 0x80 ? GPIO_BSRR_BS9 : GPIO_BSRR_BR9; GPIOB->BSRR = b & 0x20 ? GPIO_BSRR_BS4 : GPIO_BSRR_BR4; Шаблонный код мог бы быть таким: Код GpioD<9>::write(b & 0x80); GpioB<4>::write(b & 0x20); Он понятнее и эффективнее, т.к. никаких проверок в нем нет. Цитата я затронул только переключение бита. инит нужен. его можно и функцией, и макросом, можно классом...можно сразу хором весь порт GPIOA проинитить.... это как вам угодно. Речь про инит, который уже написан, отлажен и привязан к конкретному пину. Возьмем, для примера, USART. У некоторых популярных мк их по 6 штук, каждому нужно инитить ноги, большинству нужно 3 параметра для каждого пина, у STM32F1 их 2, но там инициализация немного другая... Какая сущность должна всем этим заниматься? Должен ли я для каждого USARTа закопать инициализацию пинов в какой-то общей функции инициализации? Считаю, что не должен, потому делаю так: Код Usart1<> usart1; Usart6<> usart6;
usart1.init<PinA<9, 7>, PinA<10, 7>>(2457600); usart6.init<PinC<6, 8>, PinC<7, 8>>(115200); Не нравится прописывать пины прямо тут, выносим их куда угодно Код using USART1_TX = PinA<9, 7>; using USART1_RX = PinA<10, 7>;
usart1.init<USART1_TX, USART1_RX>>(2457600); Цитата Я просто хочу сказать, что не нужно думать бинарно.... у вас либо 0, либо 1. Либо С++ с ООП в полный рост безоговорочно.... с оверинженерингом, либо если один макрос или литерал воткнут - то это непрофессионализм. Использование стандартного литерала х и b - это фу фу фу, магическая цифра.... зато SIGNED32 (при чем с миной) - это круть!!!! Здорово, особенно учитывая, что про бинарные литералы в С++ тебе рассказал именно я
|
|
|
|
|
Apr 20 2017, 08:21
|

Профессионал
    
Группа: Свой
Сообщений: 1 261
Регистрация: 14-05-09
Из: Челябинск
Пользователь №: 49 045

|
Цитата(Reflector @ Apr 20 2017, 12:56)  .... Даже коментировать тут нечего.... Полемика. Ну я же говорю, бинарно мыслите... при чем тут уарт? я вам сказал что макросы ВЕЗДЕ лучше классов/шаблонов? Цитата(Reflector @ Apr 20 2017, 12:56)  На радиокоте недавно спрашивали как записать 7 бит заданного байта в PD9, 5 бит в РВ4 и т.д.... Дали парочку ответов, лучший из которых выглядит так: Код GPIOD->BSRR = b & 0x80 ? GPIO_BSRR_BS9 : GPIO_BSRR_BR9; GPIOB->BSRR = b & 0x20 ? GPIO_BSRR_BS4 : GPIO_BSRR_BR4; Шаблонный код мог бы быть таким: Код GpioD<9>::write(b & 0x80); GpioB<4>::write(b & 0x20); Он понятнее и эффективнее, т.к. никаких проверок в нем нет. я бы не так сделал. была подобная задача.
|
|
|
|
|
Apr 20 2017, 08:28
|

Профессионал
    
Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831

|
Цитата(juvf @ Apr 20 2017, 10:51)  ... Так глубоко в компилятор я не влезал, щас влез в asm код, который нагенерил компилятор для toggle() ... Действительно, возможны глюки, надо переписывать! Вот и баги нашлись Хотя до сих пор, проблем не было, но это - вопрос времени, нужно обязательно переделать! Есть идеи, как переделать void toggle() { port->ODR ^= pinMask; }, избежав критических секций? зы. Пока писал, нашел простое на мой взгляд решение - некий более старший в иерархии класс Port, который уже будет предоставлять функционал Pin более потоко-безопасно. К тому же этот Port сможет предоставить возможность массовой настройки пинов (например, подключена внешняя память на параллельной шине).
--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
|
|
|
|
|
Apr 20 2017, 08:36
|
Участник

Группа: Участник
Сообщений: 48
Регистрация: 15-07-06
Пользователь №: 18 836

|
Цитата(juvf @ Apr 20 2017, 11:11)  Даже коментировать тут нечего.... Полемика. Ну я же говорю, бинарно мыслите... при чем тут уарт? я вам сказал что макросы ВЕЗДЕ лучше классов/шаблонов? Был тут уже один товарищ с критикой, я ему предложил написать аналог инициализации портов FSMC двумя строками, ты видел ответ? Так и тут... Начинается с того, что задефайнить csOn()/csOff() и прочие максросы проще простого, как и проинитить, а ты покажи полную инициализацию пинов хотя бы пары усартов, тогда будет с чем сравнивать. Имеется в виду количество кода который придется написать для каждого нового усарта в проекте. Цитата я бы не так сделал. была подобная задача. Не знаю какая там была задача в целом, потому исходил из того, что нужно записывать по одном биту. В любом случае тебе придется делать новый макрос, или несколько.
Сообщение отредактировал Reflector - Apr 20 2017, 08:37
|
|
|
|
|
Apr 20 2017, 08:55
|

Профессионал
    
Группа: Свой
Сообщений: 1 261
Регистрация: 14-05-09
Из: Челябинск
Пользователь №: 49 045

|
Цитата(Forger @ Apr 20 2017, 13:28)  Есть идеи, как переделать void toggle() { port->ODR ^= pinMask; }, избежав критических секций? Навскидку... Код void toggle() { port->BSRR = (port->ODR & pinMask) == 0 ? pinMask : pinMask << 16; } Цитата зы. Пока писал, нашел простое на мой взгляд решение - некий более старший в иерархии класс Port, который уже будет предоставлять функционал Pin более потоко-безопасно. К тому же этот Port сможет предоставить возможность массовой настройки пинов (например, подключена внешняя память на параллельной шине). !!! Извини.... но блин.... это всего лишь ногой дёрнуть!!! ))))))))))))
|
|
|
|
|
Apr 20 2017, 08:56
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(Forger @ Apr 20 2017, 13:28)  Есть идеи, как переделать void toggle() { port->ODR ^= pinMask; }, избежав критических секций? Можно использовать bit-band для реализации cpl(). У меня так. (К сожалению, для stm32L0x - не сработает, там нет bin-band для GPIO). Да и ерунда всё это, на самом деле. Я лично не помню, чтобы где-то использовал cpl(), кроме тестов. Всегда нужно переводить ножку в какое-то определённое состояние.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Apr 20 2017, 09:09
|
Участник

Группа: Участник
Сообщений: 48
Регистрация: 15-07-06
Пользователь №: 18 836

|
Цитата(juvf @ Apr 20 2017, 11:55)  Навскидку... Код void toggle() { port->BSRR = (port->ODR & pinMask) == 0 ? pinMask : pinMask << 16; } Ага, но только что я приводил пример практически такого-же кода, как не самого эффективного, т.к. там будет проверка. Код redLed.write(!redLed.read()); То же самое, но без лишней инструкции ITE. ps. Упс, сорри, это совсем не тоже самое, т.к. читает с IDR  Но сути это не меняет, меняем IDR на ODR, но код прячем внутри класса. Код gpio->BSRR = (0x10000 << pin) | (!(gpio->ODR & (1 << pin)) << pin); У F0 ITE нет, там проверки еще более тяжелые.
Сообщение отредактировал Reflector - Apr 20 2017, 09:34
|
|
|
|
|
Apr 20 2017, 09:15
|

Профессионал
    
Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831

|
Цитата(juvf @ Apr 20 2017, 11:55)  Код void toggle() { port->BSRR = (port->ODR & pinMask) == 0 ? pinMask : pinMask << 16; } Как вариант )) Но я предпочитаю более читаемо: Код void toggle() { if (isLow()) setToHigh(); else setToLow(); } Впрочем, этот toggle (добавил совсем недавно) у меня пока что использовался лишь для моргания лампочкой, поэтому багов не было. Но исправить все же нужно, дабы больше не возвращаться к этому вопросу.
--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
|
|
|
|
|
Apr 20 2017, 09:18
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(juvf @ Apr 20 2017, 14:08)  есть проект, где светодиод мигает, показывая живучесть системы. не было cpl, использовал с дополнительным флагом сет/ресет. А так бы было бы удобно cpl.
ps ещё програмный меандр выдавать.... можно найти применение cpl-ю Ну тогда bit-band. Есть везде, кроме L0. ЗЫ. Я там вам ответил про варианты pin.h, возможно вы проглядели (тема растёт очень быстро).
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|