|
STM32F207 - проблемы с bitband при доступе к EXTI->PR |
|
|
|
Jul 1 2015, 04:33
|
Знающий
   
Группа: Свой
Сообщений: 771
Регистрация: 16-07-07
Из: Волгодонск
Пользователь №: 29 153

|
Есть код, который использует внешние прерывания. Примерно такой CODE template<class T> constexpr T* bitbandAddr(T *addr, unsigned bit); // возвращает адрес в bitband-области для адреса addr и бита bit
struct ExternalInterrupt { unsigned no; // ... static bool checkIfInterrupt(unsigned no) { return *bitbandAddr(&EXTI->PR, no); }
bool checkIfInterruptAndClear() { bool r = checkIfInterrupt(no); if ® *bitbandAddr(&EXTI->PR, no) = 1; return r; } };
extern "C" void EXTI9_5_IRQHandler() { /* 1 */ for (unsigned i = 5; i < 10; ++i) if (ExternalInterrupt::checkIfInterrupt(i)) interrupts.exti[i].fire(); // вызов коллбэка, если он есть }
void onIntA() { // этот коллбэк связан с interrupts.exti[8] if (ei[0].checkIfInterruptAndClear()) { /* 2 */ } }
void onIntB() { // этот коллбэк связан с interrupts.exti[9] if (ei[1].checkIfInterruptAndClear()) { /* 3 */ } } Оба прерывания настроены на изменение фронта на входе. По факту иногда код пропускает изменение фронта. Сделал инвертирование контролек в некоторых местах, оказалось что иногда в /* 1 */ оно попадает, а в /* 2 */ или /* 3 */ - нет. Переписал функции checkIfInterruptXXX без использования bitband (и больше ничего не трогал) - все стало хорошо. Регистр EXTI->PR типа rc_w1 (чтение, сброс бита записью 1). bitband тут собственно не нужен, поставил в свое время по инерции. Но не ясно, почему с ним работает как-то избирательно. Никто с подобным не сталкивался?
Сообщение отредактировал IgorKossak - Jul 1 2015, 06:01
Причина редактирования: [codebox] для длинного кода, [code] - для короткого!!!
|
|
|
|
|
Jul 1 2015, 04:58
|
Знающий
   
Группа: Свой
Сообщений: 771
Регистрация: 16-07-07
Из: Волгодонск
Пользователь №: 29 153

|
Цитата(Golikov A. @ Jul 1 2015, 07:41)  возможно для битбанга нужно барьеров напихать? Вы меня подтолкнули к тому, чтобы перечитать cortex m3 prog manual Цитата Data accesses to this region are remapped to bit-band region. A write operation is performed as read-modify-write. Instruction accesses are not permitted. Собственно вопрос снят. Если два прерывания произвошли одновременно, то в PR будет установлено 2 бита, и инструкция *bitbandAddr(&EXTI->PR, no) = 1; приведет к чтению всего регистра, модификации бита (который и так 1) и записи всего этого обратно (в результате будут сброшены оба бита)
|
|
|
|
|
Jul 1 2015, 06:03
|
Гуру
     
Группа: Свой
Сообщений: 2 724
Регистрация: 14-05-07
Из: Ярославль, Россия
Пользователь №: 27 702

|
Цитата(Непомнящий Евгений @ Jul 1 2015, 07:58)  Собственно вопрос снят. Неа, это только начало... Мне всегда казалось, что запись в bit-band обеспечивает атомарность. Неужели это не так? Как понимать "read-modify-write"? Это по сколько бит? По 1/8/16/32? Как быть с регистрами, где чтение имеет побочное действие? А при чтении тоже считывается не один бит?
|
|
|
|
|
Jul 1 2015, 06:31
|
Знающий
   
Группа: Свой
Сообщений: 771
Регистрация: 16-07-07
Из: Волгодонск
Пользователь №: 29 153

|
Цитата(adnega @ Jul 1 2015, 09:03)  Мне всегда казалось, что запись в bit-band обеспечивает атомарность. Неужели это не так? Мне тоже. Почему то в programming manual об этом ничего не нашел, перечитал Yiu J., The Definitive Guide to the ARM Cortex-M3 One of the most important advantages or properties of a bit-band operation is that it is atomic. ... With the Cortex-M3 bit-band feature, this kind of race condition can be avoided because the READ- MODIFY-WRITE is carried out at the hardware level and is atomic (the two transfers cannot be pulled apart) and interrupts cannot take place between them. Цитата Как понимать "read-modify-write"? Это по сколько бит? По 1/8/16/32? Нашел такое: Bit band accesses can use byte, halfword, or word transfers. The bit band transfer size matches the transfer size of the instruction making the bit band access. и такое For write operations, the written bit data are shifted to the required bit position, and a READ-MODIFY-WRITE is performed. Цитата Как быть с регистрами, где чтение имеет побочное действие? А при чтении тоже считывается не один бит? For read operations, the word is read and the chosen bit location is shifted to the LSB of the read return data. Получается, что засада может быть с регистрами вида rc_w0 и rc_w1 (для которых собственно битбанд и не нужен), на что я и напоролся
|
|
|
|
|
Jul 1 2015, 06:54
|
Частый гость
 
Группа: Участник
Сообщений: 180
Регистрация: 5-04-09
Пользователь №: 47 205

|
QUOTE (adnega @ Jul 1 2015, 09:03)  Мне всегда казалось, что запись в bit-band обеспечивает атомарность. Неужели это не так? Атомарность обеспечивается на уровне процессора (операция не может быть прервана), для периферии никто ничего не обещает.
|
|
|
|
|
Jul 3 2015, 04:14
|
Гуру
     
Группа: Свой
Сообщений: 2 724
Регистрация: 14-05-07
Из: Ярославль, Россия
Пользователь №: 27 702

|
Цитата(Непомнящий Евгений @ Jul 1 2015, 09:31)  Получается, что засада может быть с регистрами вида rc_w0 и rc_w1 (для которых собственно битбанд и не нужен), на что я и напоролся Я в одно время очень сильно подсел на битовый доступ именно к периферии. Потом в новых ST из bit-band убрали GPIO - интерес к поутих. Сейчас использую M0, там нет битового доступа вообще. Вроде, msc-51 и AVR битовые операции делали без r-m-w?
|
|
|
|
|
Jul 3 2015, 08:29
|
Гуру
     
Группа: Свой
Сообщений: 2 724
Регистрация: 14-05-07
Из: Ярославль, Россия
Пользователь №: 27 702

|
Цитата(Golikov A. @ Jul 3 2015, 11:21)  Чем удобнее? Макросом через дефайн не решается? Я понимаю, что есть более одного правильного решения. Я в структурах хранил адрес бита, чтоб потом делать так: Код *pwm_pin = 1; Можно хранить номер порта и номер пина и делать так: Код void set_pin(const WORD pin, const BYTE value) { switch(pin & 0xF0) { case GPIOA_PORT: GPIOA->BSRR = (1 << (pin & 15)) << ((value)? 0 : 16); break; case GPIOB_PORT: GPIOB->BSRR = (1 << (pin & 15)) << ((value)? 0 : 16); break; case GPIOC_PORT: GPIOC->BSRR = (1 << (pin & 15)) << ((value)? 0 : 16); break; case GPIOD_PORT: GPIOD->BSRR = (1 << (pin & 15)) << ((value)? 0 : 16); break; case GPIOE_PORT: GPIOE->BSRR = (1 << (pin & 15)) << ((value)? 0 : 16); break; case GPIOF_PORT: GPIOF->BSRR = (1 << (pin & 15)) << ((value)? 0 : 16); break; } }
set_pin(PWM_PIN, 1); Или вы как-то по другому предлагаете?
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|