STM32 новичку в ARM что к чему

Кто любит RISC в жизни, заходим, не стесняемся.
Ответить
Опытный кот
Аватара пользователя
Сообщения: 725
Зарегистрирован: Вт апр 26, 2011 13:58:36
Откуда: Ростовская область

Сообщение amv2000 »

HHIMERA писал(а):Юзать прерывания - моветон.
Т.е. лучше заводить таймер и опрашивать через определенный период, тем самым и дребезг устранить и в главном цикле не тормозить ?
Реклама
Вымогатель припоя
Аватара пользователя
Сообщения: 535
Зарегистрирован: Вт авг 28, 2012 22:21:33

Сообщение menzoda »

Да, опечатка.

Скажу свое видение плюсов/минусов программного и аппаратного антидребезга. То что касается цены - упасите, какое нафиг дорого? Резистор и конденсатор будут стоить в буквальном смысле копейки. Да пол-часа работы программиста, который будет программный антидребезг делать обойдется дороже!

Гибкая настройка? Да что там гибко настраивать то? Лучше подумайте какой ценой вы получаете программный антидребезг. Таймер и прерывания? На жалкую обработку кнопок целый таймер и прерывание? Да в МК может быть всего один-два таймера, и вы ими так просто швыряетесь? А может в фоновом цикле ждать 10-100-200 миллисекунд? В фоновом цикле обычно крутятся всякие приемо-передачи по UART, разные мигания, не критичные ко времени алгоритмы... Вы хотите, чтобы они все подождали, пока у вас кнопочка обработается? Это же бесполезная трата процессорного времени. Нет уж, я всегда буду настаивать на аппаратном антидребезге и только на нем.

Это все не ради спора, просто мое мнение. Вопросы риторические.
Реклама
Друг Кота
Аватара пользователя
Сообщения: 3604
Зарегистрирован: Пн июл 28, 2008 22:12:01

Сообщение dosikus »

menzoda писал(а):Нет уж, я всегда буду настаивать на аппаратном антидребезге и только на нем.
То есть вы хотите сказать , что кнопки вешаете на EXTI + конденсаторы ???
Ну, ну , большое вам счастье скакать по, пройденным уже многими, граблям . Возможно эти грабли вам мОзги вправят... :)))
Вымогатель припоя
Аватара пользователя
Сообщения: 535
Зарегистрирован: Вт авг 28, 2012 22:21:33

Сообщение menzoda »

Если нужно - повешу на внешнее прерывание, если нужно - просто на ножку, если нужно - буду читать через сдвиговый регистр по SPI, но во всех случаях антидребезг буду использовать аппаратный.
Реклама
Эиком - электронные компоненты и радиодетали
Поставщик валерьянки для Кота
Сообщения: 2108
Зарегистрирован: Пт авг 29, 2008 16:28:19
Откуда: Киев

Сообщение Foks »

Ну тогда и я отвечу, не ради спора. :)

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

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

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

P.S. Вообще, соглашусь, что решение может зависеть от конкретных условий. Не стоит ограничивать себя ни аппаратным, ни программным способом, лучше держать в голове все возможные способы, и выбирать оптимальный для каждой задачи.
Giggity giggity goo!
Реклама
Друг Кота
Сообщения: 4583
Зарегистрирован: Вс дек 05, 2010 06:10:34
Откуда: ЮВ

Сообщение HHIMERA »

Ну тогда и я... совсем не для спора... :))
menzoda писал(а):Если нужно - повешу на внешнее прерывание, если нужно - просто на ножку, если нужно - буду читать через сдвиговый регистр по SPI, но во всех случаях антидребезг буду использовать аппаратный.
Вы, похоже, никогда кнопки не подключали... бывает...
Подключите плёнку... сколько она протянет???
Foks писал(а): Про "дорого" зависит от объемов. Если вы инженер компании, которая производит модули миллионными партиями, то экономическая часть вопроса будет стоять у вас по сути на первом месте. Если же нет - что же, можно и добавить пару элементов, но будет ли от них реальная польза?
Загляните в схемы мобильников... ведь не зря там всё это навешано...
Вообще, соглашусь, что решение может зависеть от конкретных условий. Не стоит ограничивать себя ни аппаратным, ни программным способом, лучше держать в голове все возможные способы, и выбирать оптимальный для каждой задачи.
100% !!!
"Я не даю готовых решений, я заставляю думать!"(С)
Реклама
Друг Кота
Сообщения: 4583
Зарегистрирован: Вс дек 05, 2010 06:10:34
Откуда: ЮВ

Сообщение HHIMERA »

amv2000 писал(а):
HHIMERA писал(а):Юзать прерывания - моветон.
Т.е. лучше заводить таймер и опрашивать через определенный период, тем самым и дребезг устранить и в главном цикле не тормозить ?
Не только дребезг... Ещё можно отработать короткое нажатие, двойное, тройное, длинное (автоповтор, переход куда-либо) или залипание (механическое повреждение) и т. д. ...
"Я не даю готовых решений, я заставляю думать!"(С)
Опытный кот
Аватара пользователя
Сообщения: 725
Зарегистрирован: Вт апр 26, 2011 13:58:36
Откуда: Ростовская область

Сообщение amv2000 »

HHIMERA писал(а):[Не только дребезг...
Попробовал с таймером SysTick заметно лучше ситуация :))
Спойлер#include "stm32f10x.h" /* "stm32f10x.h" - Файл описания периферии */
#define Green 0 /* Зеленый светодиод */
#define Blue 1 /* Голубой светодиод */
uint8_t flag; /* флаг состояния */

RCC->APB2ENR |= RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPAEN;// включаем тактирование порта C и А
GPIOC->CRH |= (GPIO_CRH_MODE8_0 | GPIO_CRH_MODE9_0);// Режим выхода порта С, вывода PC8,9 с максимальной скоростью – 10МГц (MODEy[1:0] = 01)
GPIOC->CRH &= ~(GPIO_CRH_CNF8 | GPIO_CRH_CNF9);// Режим вывода PC8,9 порта C, Двухтактный выход общего назначения Push-Pull CNFy[1:0] = 00:
GPIOA->CRL &= ~ GPIO_CRL_MODE0; // Кнопка "USER" PA0 - на вход.
/**-----------------------------------------------------------------------------
* @описание Функция управления состояния светодиодов.
* @параметр uint8_t stat
* @возвращаемое
* значение Нет
*---------------------------------------------------------------------------*/
void Led_on_off(uint8_t stat)
{
GPIOC->ODR &= 0xFCFF; /* Выкл синий и зеленый светодиод */
switch(stat)
{
case Green: _PORTC(9)= 1; /* вкл синий GPIOC->ODR |= 1 << 8; */
break;
case Blue: _PORTC(8)= 1; /* вкл зеленый GPIOC->ODR |= 1 << 9; */
}
}

/*---------- Обработчик прерывания по переполнению таймера SysTick -----------*/
void SysTick_Handler()
{
/* Обработка кнопки */
static uint8_t btn_old_state = 0; /* первичное состояние кнопки */
uint8_t btn_state = (GPIOA->IDR & 1); /* текущее состояние кнопки */

if (btn_old_state == 0 && btn_state == 1)
flag=!flag;
btn_old_state = btn_state;
}

int main() /*------------- Основная программа --------------------------------*/
{
/* Конфигурируем таймер SysTick на срабатывание 100 раз в секунду */
SysTick_Config(SystemCoreClock / 100);

while(1) /* -------------- Основной цикл ------------------------------------*/
{
Led_on_off(flag);
}
}
Код выше вдруг кому интересно.
Поставщик валерьянки для Кота
Сообщения: 2108
Зарегистрирован: Пт авг 29, 2008 16:28:19
Откуда: Киев

Сообщение Foks »

Антидребезга в коде так и не увидел.
Giggity giggity goo!
Опытный кот
Аватара пользователя
Сообщения: 725
Зарегистрирован: Вт апр 26, 2011 13:58:36
Откуда: Ростовская область

Сообщение amv2000 »

Здесь кнопка опрашивается 100 раз в секунду, сколько пробовал дребезга не увидел(если что, думаю можно реже опрашивать будет еще лучше).
Еще надо попробовать в цикле антидребезг организовать по таймеру.
Грызет канифоль
Сообщения: 296
Зарегистрирован: Ср дек 30, 2009 09:55:39

Сообщение kolobok0 »

amv2000 писал(а):...кнопка опрашивается 100 раз в секунду...организовать по таймеру.
собственно можно по разному мутить. смысл один - отслеживать человека :) человек не быстр. посему есть некий "дооолгий" промежуток времени который однозначно нам скажет - нет это не помеха. в кавычках - потому как это несколько миллисекунд.
дальше:
нам надо асинхронно воспринимать кнопку от человека. Тут два способа - прерывание либо опрос. опрос проезжаем - это понятно. остаётся прерывание. собственно если мы отжимаемся и время прошло мало - то его в пень. если много - то флаг нажатия.
из основной программы, если у нас нет РТОС - то нам кнопка подтребуется когда потребуется (если РТОС - то мы можем тут задействовать всевозможные осевые выкрутасы - типа ожидание со временем и прочее). Т.е. если что то там нажалось, то до запроса этого события из основной логики - нам пофигу. а вот если запросили, то мы можем посмотреть две вещи 1) сколько времени прошло от нажатия (если такое было) 2) стоит ли флаг нажатия кнопочки (если он был выставлен).

надеюсь понятно описал логику, без всякого кода на таймерах (необходимо только всё же чем то считать время - т.е. запустить таймер какой нить :) можно в условных попугаях - т.е. не обязательно реальные секунды, миллисекунды и т.п.)???
Вымогатель припоя
Сообщения: 581
Зарегистрирован: Ср янв 05, 2011 10:03:18

Сообщение ut1wpr »

kolobok0 писал(а):собственно можно по разному мутить.
Однозначно! :)
Тут два способа - прерывание либо опрос. опрос проезжаем - это понятно. остаётся прерывание. собственно если мы отжимаемся и время прошло мало - то его в пень. если много - то флаг нажатия.
Ну вот. Так сразу. Только два? Почему?
В большинстве случаев случаев применяю третий, совмещенный - опрос в прерывании. :)
К тому же в этом методе есть и защита от дребезга. Без накладных расходов. Довольно удобно как для отдельных кнопок, так и в матрицах из них.
Чаще всего использую системный таймер на 1 мсек.
Если кому-то будет интересно - спрашивайте. Тратить время "в воздух" нет желания.
С уважением,
Виктор.
Опытный кот
Аватара пользователя
Сообщения: 725
Зарегистрирован: Вт апр 26, 2011 13:58:36
Откуда: Ростовская область

Сообщение amv2000 »

ut1wpr писал(а):Если кому-то будет интересно - спрашивайте. Тратить время "в воздух" нет желания.
Да ладно Вам, очень даже интересно. Я перекопал интернет и везде только разговоры на эту тему или примеры с применением библиотеки SPL, а она хоть и говорят для удобного восприятия у меня в голове вызывает только кашу. Впрочем про таймер SysTick читал именно с применение SPL, так же попадалось очень похожее на пример выше, где учитывается время нажатия. Только здесь я думаю опечатка надо if (++button_cnt == некая_константа) Action();, тогда условие выполняется если нажата кнопка, запускаем счет и ждем когда значение будет равно константе. Если равно выполняем какое то действие, если нет обнуляем счетчик. Надеюсь правильно понимаю код, тем более он прост

Код: Выделить всё

void SysTick_Handler()
 {
 static unsigned button_cnt = 0;
 if (GPIOA->IDR & 1) {
 if (++button == некая_константа) Action();
 } else button_cnt = 0;
 }
И еще отложил для себя

Код: Выделить всё

static uint16_t KeyAcc = 0; /* Key Accumulator */
static uint16_t Key = 0;    /* Key State */
 
/*
* Read Key
*/
void ReadKey() {
   KeyAcc = KeyAcc << 1;
   KeyAcc |= !(KEYPORT & KEYPIN);
   if(KeyAcc == 0xFFFF) Key = 1;
   if(KeyAcc == 0) Key = 0;
}


Функция ReadKey() вызывается либо из главного цикла программы, либо по прерыванию таймера. 
В главном цикле обрабатывается переменная Key.
if(Key) /* кнопка нажата */  
{      
}
Еще сильно помагает исходник от Galizin, за что ему отдельное спасибо.
Вымогатель припоя
Сообщения: 581
Зарегистрирован: Ср янв 05, 2011 10:03:18

Сообщение ut1wpr »

amv2000 писал(а):Да ладно Вам, очень даже интересно. Я перекопал интернет и везде только разговоры на эту тему или примеры с применением библиотеки SPL, а она хоть и говорят для удобного восприятия у меня в голове вызывает только кашу. Впрочем про таймер SysTick читал именно с применение SPL, так же попадалось очень похожее на пример выше, где учитывается время нажатия. Только здесь я думаю опечатка надо if (++button_cnt == некая_константа) Action();, тогда условие выполняется если нажата кнопка, запускаем счет и ждем когда значение будет равно константе. Если равно выполняем какое то действие, если нет обнуляем счетчик. Надеюсь правильно понимаю код, тем более он прост

Код: Выделить всё

void SysTick_Handler()
 {
 static unsigned button_cnt = 0;
 if (GPIOA->IDR & 1) {
 if (++button == некая_константа) Action();
 } else button_cnt = 0;
 }
Практически то, что я применяю.
Немного переиначил условие. Считается, что кнопка в отжатом состоянии в "1", нажатие в "0".
Организованы N счетчиков по количеству кнопок. Все счетчики volatile.

Код: Выделить всё

volatile int button_cnt_1, button_cnt_2;
void AnyTimer_Handler( void){
 if( GPIOA->IDR and 0x01 ) button_cnt_1 = 0x00; else if( button_cnt_1 < TRESHOLD ) button_cnt_1++;
 if( GPIOA->IDR and 0x02 ) button_cnt_2 = 0x00; else if( button_cnt_2 < TRESHOLD ) button_cnt_2++;
return;
И так по количеству кнопок. Линейное сканирование в один проход обработчика.
С матрицей немного иначе, но суть та же.
Если период прерываний таймера, выбранного в качестве системного равен 1 мсек, то TRESHOLD должен иметь значение порога антидребезга в мсек.
Если таймер считает быстрее, соответственно пересчитывается TRESHOLD.
И никаких Action() из обработчика!
Для получения состояния кнопки ( только результат "кнопка Кн была нажата", длительности и моментальная "нажата/отжата" не отрабатываются, легко додумываются по необходимости ) надо обращаться к функции

Код: Выделить всё

int GetPressedButton( void ){
 if( button_cnt_1 == TRESHOLD ) button_cnt_1 = TRESHOLD+1; else return 1;
 if( button_cnt_2 == TRESHOLD ) button_cnt_2 = TRESHOLD+1; else return 2;
// ... по количеству кнопок
return ( 0 );
}
Функция возвращает либо код нажатой кнопки асинхронно к моменту обращения, либо ноль, если ни одна их кнопок не была нажата.
Подобный подход позволяет не загромождать обработчик ничем, кроме работы со счетчиками, "защелкивая" событие "была нажата" по истечении TRESHOLD-а.
Фиксация времени нажатия кнопок или получение состояния "нажата/отжата" возможна с применением того же механизма, но слегка поменяв логику работы функции получения данных по кнопкам.
Как видите, все очень просто. Метод не мой, просто уже не помню, из какой литературы я его извлек.
Встречал еще несколько модификаций этого алгоритма. Но они, на мой взгляд, были менее оптимальны. Этот же прост и прозрачен. Соотвественно надежен. Об ограничениях применения я уже сказал.

Про "кашу в голове" не комментирую, это ваши проблемы. Никто не заставляет применять SPL.
С уважением,
Виктор.
Опытный кот
Аватара пользователя
Сообщения: 725
Зарегистрирован: Вт апр 26, 2011 13:58:36
Откуда: Ростовская область

Сообщение amv2000 »

ut1wpr писал(а):Практически то, что я применяю.
Опробовал и его через таймер 3, разницы по сравнению с SysTick не заметил, но для себя полезно.
Спойлер#include "stm32f10x.h" /* "stm32f10x.h" - Файл описания периферии */

#define Green 0 /* Зеленый светодиод */
#define Blue 1 /* Голубой светодиод */
uint8_t flag; /* флаг состояния */

/*------------------------------------------------------------------------------
Побитовый доступ к регистрам:
--------------------------------------------------------------------------------
Адрес в области bit band = начальный адрес области bit band + смещение байта в «родной» области ? 20 + номер бита ? 4.*/

#define _PORTC(i) (*((__O unsigned long *) \
(PERIPH_BB_BASE + ((GPIOC_BASE+0x0C-PERIPH_BASE) * 0x20) + (i*4))))

void Initall ()
{
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPAEN;// включаем тактирование порта C и А
GPIOC->CRH |= (GPIO_CRH_MODE8_0 | GPIO_CRH_MODE9_0);// Режим выхода порта С, вывода PC8,9 с максимальной скоростью – 10МГц (MODEy[1:0] = 01)
GPIOC->CRH &= ~(GPIO_CRH_CNF8 | GPIO_CRH_CNF9);// Режим вывода PC8,9 порта C, Двухтактный выход общего назначения Push-Pull CNFy[1:0] = 00:
GPIOA->CRL &= ~ GPIO_CRL_MODE0; // Кнопка "USER" PA0 - на вход.

/*------------------------------- Таймер 3 -----------------------------------*/
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; /* Тактирование таймера TIM3 */
TIM3->ARR = 10000; /* перезагружать каждые (2MHz / 10000 = 200Hz ) - 5mSec (1000/200) */
/* Когда таймер достигнет максимального значения необходимо уведомить об этом контроллер прерываний, */
TIM3->DIER |= TIM_DIER_UIE; /* для этого разрешим генерацию событий от таймера 3 */
TIM3->CR1 |= TIM_CR1_CEN; /* старт таймера TIM3 */
NVIC_EnableIRQ(TIM3_IRQn);/* Задействуем обработчик прерывания от 3-го таймера */
}

/**-----------------------------------------------------------------------------
* @описание : Обработчик прерывания по переполнению таймера 3.
* @заметка :
* @параметр : Нет
* @возвр значение: Нет
*---------------------------------------------------------------------------*/
void TIM3_IRQHandler(void)
{
TIM3->SR &= ~TIM_SR_UIF; // не забываем сбросить флаг, //TIM3->SR = 0; /* Очистить флаг ожидания*/
// иначе прерывание будет генериться постоянно
static unsigned button_cnt = 0; /* счетчик периода нажатия */
if (GPIOA->IDR & 1) /* Если нажата кнопка */
{
if (++button_cnt == 100) flag=!flag;/* запускаем счетчик, если в этот период кн еще нажата*/
} else button_cnt = 0;/* выполняем какое то действие, если нет обнуляем счетчик */
}

/**-----------------------------------------------------------------------------
* @описание : Функция управления состояния светодиодов.
* @параметр : uint8_t stat (переменная типа unsigned char )
* @возвращаемое значение: Нет
*---------------------------------------------------------------------------*/
void Led_on_off(uint8_t stat)
{
GPIOC->ODR &= 0xFCFF; /* Выкл синий и зеленый светодиод */
switch(stat)
{
case Green: _PORTC(9)= 1; /* вкл синий GPIOC->ODR |= 1 << 8; */
break;
case Blue: _PORTC(8)= 1; /* вкл зеленый GPIOC->ODR |= 1 << 9; */
}
}

int main() /*------------- Основная программа --------------------------------*/
{
Initall (); /* инициализируем порты и таймеры */

while(1) /* -------------- Основной цикл ------------------------------------*/
{
Led_on_off(flag); /* запускаем переключение LED */
}
}
Здесь один раз нажали горит зеленый светодиод, второй раз синий и так по кругу, опрашиваем с помощью таймера 3.
Родился
Сообщения: 6
Зарегистрирован: Чт июн 20, 2013 03:11:09

Сообщение Citizen Kane »

Всем добрый день!

Вопрос по IAR (6.5): как разместить переменную в сегменте eeprom?
(для контроллера STM32L152 EEPROM начинается с 0x08080000)
в конфиге для линкера это описано как

define symbol __region_EEPROM_start__ = 0x08080000;
define symbol __region_EEPROM_end__ = 0x08080FFF;
define region EEPROM_region = mem:[from __region_EEPROM_start__ to __region_EEPROM_end__];

place in EEPROM_region { section .eeprom };

Для AVR это выглядело как что-то вроде
__attribute__((section(".eeprom")))
в IAR соотв-но не работает.

Спасибо.
Друг Кота
Аватара пользователя
Сообщения: 7439
Зарегистрирован: Чт ноя 04, 2010 01:56:36
Откуда: г. Москва

Сообщение Satyr »

Вопрос типа "Как под виндами разместить переменную на жестком диске".
Никак. EEPROM - это периферия. Инициализируй, пиши, читай функциями. Как оно само в еепром телепортируется ?
Это не игрушечный атмел авр студио, где фрейморк незримо за тебя подшаманивет.
Поставщик валерьянки для Кота
Аватара пользователя
Сообщения: 1900
Зарегистрирован: Сб фев 21, 2009 13:11:40
Откуда: Москва

Сообщение ibiza11 »

Satyr, добрый день!
Вы уважаемый мной кот, ни в коем разе не хочу Вас обидеть.
Но мне кажется в данном случае Вы заблуждаетесь. в STM32L EEPROM отображается в общее адресное пространство на адреса начиная с 0x08080000.
Следовательно переменную можно разместить там явно, указав ее адрес. Или неявно, используя указатель, записать просто в ячейку.
И то и другое делается без всяких функций, таких как например в AVR.

Код: Выделить всё

*((uint32_t *)(0x08080000)) = 0xFEDCBA; 
или

Код: Выделить всё

uint32_t * pMyVal = (uint32_t *)0x08080000;
*pMyVal = 0xABCDEF; 
Ставим плюсы: )
Друг Кота
Сообщения: 4583
Зарегистрирован: Вс дек 05, 2010 06:10:34
Откуда: ЮВ

Сообщение HHIMERA »

Да не всё так просто...
А Unlock кто будет делать? :))
"Я не даю готовых решений, я заставляю думать!"(С)
Вымогатель припоя
Сообщения: 581
Зарегистрирован: Ср янв 05, 2011 10:03:18

Сообщение ut1wpr »

ibiza11 писал(а):Satyr, добрый день!
Вы уважаемый мной кот, ни в коем разе не хочу Вас обидеть.
Но мне кажется в данном случае Вы заблуждаетесь. в STM32L EEPROM отображается в общее адресное пространство на адреса начиная с 0x08080000.
Следовательно переменную можно разместить там явно, указав ее адрес. Или неявно, используя указатель, записать просто в ячейку.
И то и другое делается без всяких функций, таких как например в AVR.

Код: Выделить всё

*((uint32_t *)(0x08080000)) = 0xFEDCBA; 
или

Код: Выделить всё

uint32_t * pMyVal = (uint32_t *)0x08080000;
*pMyVal = 0xABCDEF; 
Читать можно свободно. А вот с записью не получится. Области EEPROM и FLASH имеют защиту. При обращении к памяти в этой области ее необходимо предварительно снять. Учитывая это, механизм программной инициализации не может быть выражен путем VAR = VALUE. Запись во флеш и еепром имеет механизм, отличный от ОЗУ. Причем далеко не все STM32 имеют этот самый eeprom.
С уважением,
Виктор.
Ответить

Вернуться в «ARM»