|
|
|
давайте делится удобными дефайнами для stm32f10x |
|
|
|
Feb 6 2013, 03:09
|
Местный
Группа: Участник
Сообщений: 222
Регистрация: 14-12-12
Из: новосибирск
Пользователь №: 74 845
|
например я вот такие сделал : CODE #define enable_clock(port) RCC->APB2ENR|=RCC_APB2ENR_IOP##port##EN // включение тактирования порта
#define set_pin(port,bit) GPIO##port## -> ODR |= GPIO_ODR_ODR##bit // установить на порте 1 #define clear_pin(port,bit) GPIO##port## -> ODR &= ~ GPIO_ODR_ODR##bit // установить на порте 0 #define test_pin(port,bit) GPIO##port## -> ODR &= GPIO_ODR_ODR##bit // возвращает истинное состояние на выводе ножки порта
//vvvvvvvvvvv Задаём направление и максимальную частоту работы портов MODE[bit1,bit0] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv #define set_in(port,half,bit) GPIO##port##->CR##half##&=~(GPIO_CR##half##_MODE##bit##_0); GPIO##port##->CR##half##|=(GPIO_CR##half##_MODE##bit##_0) //00: Input mode (reset state) - порт работает на вход, устанавливается по умолчанию после ресета #define set_out_10MHz(port,half,bit) GPIO##port##->CR##half##&=~(GPIO_CR##half##_MODE##bit##_0); GPIO##port##->CR##half##|=(GPIO_CR##half##_MODE##bit##_1) //01: Output mode, max speed 10 MHz. #define set_out_2MHz(port,half,bit) GPIO##port##->CR##half##&=~(GPIO_CR##half##_MODE##bit##_1); GPIO##port##->CR##half##|=(GPIO_CR##half##_MODE##bit##_0) //10: Output mode, max speed 2 MHz. #define set_out_50MHz(port,half,bit) GPIO##port##->CR##half##&=~(GPIO_CR##half##_MODE##bit##_1); GPIO##port##->CR##half##|=(GPIO_CR##half##_MODE##bit##_1) //11: Output mode, max speed 50 MHz. //если биты портов 0-7 то half=L //если биты портов 8-15 = то half=H // в регистре GPIOX_CR(L/H) меняет биты MODEx (режимы работы x ножки порта X) // Пример использования: // set_in(D,L,7); // устанавливает порт D.7 как вход // set_out_10MHz(D,L,7); //устанавливает порт D.7 как выход с максимальной частотой 10 Мгц. // set_out_2MHz(D,L,7); // set_out_50MHz(D,L,7); //^^^^^^^^^^^^ Задаём направление и максимальную частоту работы портов ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Сообщение отредактировал IgorKossak - Feb 7 2013, 18:53
Причина редактирования: форматирование
|
|
|
|
|
Feb 6 2013, 04:12
|
Гуру
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713
|
хм.... И что-ж тут удобного??? Во-первых - неудобно, во-вторых - баги в каждом макросе. Вот вам для примера мои аналогичные макросы, для управления пинами GPIO (NXP). Найдите отличия и поймите где баги: Код #define Pval2(port, pin) (GPIO[port].PIN >> (pin) & 1) #define Pclr2(port, pin) (GPIO[port].CLR = 1U << (pin)) #define Pset2(port, pin) (GPIO[port].SET = 1U << (pin)) #define Pval(port_pin) Pval2(port_pin) #define Pclr(port_pin) Pclr2(port_pin) #define Pset(port_pin) Pset2(port_pin) #define PORT2(port, pin) port #define PIN2(port, pin) pin #define PORT(port_pin) PORT2(port_pin) #define PIN(port_pin) PIN2(port_pin)
#define PIN_RF_RST 2, 3 //порт, пин #define PIN_KEY1 1, 0 #define PIN_WDI 1, 9
//использование: Pset(PIN_RF_RST); //установить в '1' Pclr(PIN_WDI); //установить в '0' if (Pval(PIN_KEY1)) { ... }
struct { u8 port, pin; } static const t[] = {{PORT(PIN_WDI), PIN(PIN_WDI)}, ...};
Pset2(t[0].port, t[0].pin);
|
|
|
|
|
Feb 6 2013, 05:19
|
Местный
Группа: Участник
Сообщений: 205
Регистрация: 21-09-10
Из: г.Зеленоград
Пользователь №: 59 631
|
Ну, раз для STM32F1, т.е. для контроллера с ядром Cortex M3, то полезны макросы для побитовой работы с регистрами: CODE /*Побитовая адресация:*/ #define PERIPH_BB_ADDRESS(PeriphAddress,RegisterOffset,NumBit) (((u32)((u32)PeriphAddress+RegisterOffset)- 0x40000000)*32+(NumBit)*4+0x42000000)
#define RELAY5 (Pin)(*((Pin*) PERIPH_BB_ADDRESS(GPIOA,0x0C,2))) /*output PA2*/ #define RELAY6 (Pin)(*((Pin*) PERIPH_BB_ADDRESS(GPIOB,0x0C,2))) /*output PB2*/ #define LEDS_LED1 (Pin)(*((Pin*) PERIPH_BB_ADDRESS(GPIOE,0x0C,5))) /*output PE5*/ #define LEDS_LED2 (Pin)(*((Pin*) PERIPH_BB_ADDRESS(GPIOE,0x0C,4))) /*output PE4*/
/*И по месту просто использовать так:*/ LEDS_LED1 = 1; RELAY6 =0; Но при использовании оптимизации лучше использовать следующее: CODE #define RS485_ADDRESS_FE PERIPH_BB_ADDRESS(RS485_USART,USART_SR,1) /*адрес флага FE*/ #define RS485_ADDRESS_NE PERIPH_BB_ADDRESS(RS485_USART,USART_SR,2) /*адрес флага NE*/
extern vu32 RS485_FE __attribute__((at(RS485_ADDRESS_FE))); extern vu32 RS485_NE __attribute__((at(RS485_ADDRESS_NE)));
/*И по месту:*/ if( RS485_FE ) abc = bcd+cda;
Сообщение отредактировал IgorKossak - Feb 7 2013, 18:50
Причина редактирования: форматирование
|
|
|
|
|
Feb 7 2013, 14:11
|
;
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509
|
Цитата(jcxz @ Feb 7 2013, 16:34) У меня вызовы макросов типа установить/обнулить/проинвертировать/... пин GPIO одинаковы для разных процессоров, что часто позволяет переносить исходники с одного CPU на другой другого производителя с минимальными модификациями (или вообще без оных). Вы коснулись хорошей темы, а именно наилучшего API для контроллеров. Его, ессно, не существует, но есть вещи, которые хорошо себя зарекомендовали и на разных платформах. Например вызов инициализации UART в виде Код uint_fast8_t async_config( const uint_fast8_t number, //номер порта const uint_fast32_t baud, // скорость const uint_fast8_t bits, // число бит const char parity, //'E'-even, 'O'-odd, 'N'-none const uint_fast8_t stops // 1, 2 для полутора - нет ); Но uart - это просто. Потом, с SPI более менее получается - там где нет аппаратной поддержки, например, DMA, - включается эмуляция оного. А вот с таймерами - уже засада. Или с АЦП. А дергать пинами - у меня лично такого уровня просто нет. У меня в board.h есть например "включить реле", а set-reset какой-то сферический пин - нету.
|
|
|
|
|
Feb 8 2013, 02:27
|
Местный
Группа: Участник
Сообщений: 222
Регистрация: 14-12-12
Из: новосибирск
Пользователь №: 74 845
|
CODE #define uchar unsigned char // VVVVVVVVVVVVVVVVVVVVV Работа с портами VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV #define enable_clock(port) RCC->APB2ENR|=RCC_APB2ENR_IOP##port##EN // включение тактирования порта #define pin_on(port,bit) GPIO##port## -> ODR |= GPIO_ODR_ODR##bit // установить на порте 1 #define pin_off(port,bit) GPIO##port## -> ODR &= ~ GPIO_ODR_ODR##bit // установить на порте 0 #define pin_test(port,bit) GPIO##port## -> ODR &= GPIO_ODR_ODR##bit // возвращает истинное состояние на выводе ножки порта
//vvvvvvvvvvv Конфигурируем порты: направление и максимальная частота работы портов vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv #define set_in(port,half,bit) GPIO##port##->CR##half##&=~(GPIO_CR##half##_MODE##bit##_0); GPIO##port##->CR##half##|=(GPIO_CR##half##_MODE##bit##_0) //00: Input mode (reset state) - порт работает на вход, устанавливается по умолчанию после ресета #define set_out_10MHz(port,half,bit) GPIO##port##->CR##half##&=~(GPIO_CR##half##_MODE##bit##_0); GPIO##port##->CR##half##|=(GPIO_CR##half##_MODE##bit##_1) //01: Output mode, max speed 10 MHz. #define set_out_2MHz(port,half,bit) GPIO##port##->CR##half##&=~(GPIO_CR##half##_MODE##bit##_1); GPIO##port##->CR##half##|=(GPIO_CR##half##_MODE##bit##_0) //10: Output mode, max speed 2 MHz. #define set_out_50MHz(port,half,bit) GPIO##port##->CR##half##&=~(GPIO_CR##half##_MODE##bit##_1); GPIO##port##->CR##half##|=(GPIO_CR##half##_MODE##bit##_1) //11: Output mode, max speed 50 MHz. //если пин порта 0-7 то half=L //если пин порта 8-15 = то half=H // в регистре GPIOX_CR(L/H) меняет биты MODEx (режимы работы x ножки порта X) // Пример использования: // set_in(D,L,7); // устанавливает порт D.7 как вход // set_out_10MHz(D,L,7); //устанавливает порт D.x как выход с максимальной частотой 10 Мгц. // set_out_2MHz(D,L,7); //устанавливает порт D.x как выход с максимальной частотой 2 Мгц. // set_out_50MHz(D,L,7); //устанавливает порт D.x как выход с максимальной частотой 50 Мгц. //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #define pin_config(port,half,pin,b1,b0) GPIO##port##->CR##half##&=~(GPIO_CR##half##_CNF##pin##_0);GPIO##port##->CR##half##&=~(GPIO_CR##half##_CNF##pin##_1) ; // конфигурация регистра CNF
//vvvvvvvvvvv меняем настройки портов настроенных на выход, general/alternativ и push-pull/open-drain vvvvvvvvvvvvvvv #define general_push_pull(port,half,pin) pin_config(##port##,##half##,##pin##,0,0)//00: set port as General purpose output push-pull #define general_open_drain(port,half,pin) pin_config(##port##,##half##,##pin##,0,1)//01: set port as General purpose output Open-drain #define alternate_push_pull(port,half,pin) pin_config(##port##,##half##,##pin##,1,0)//10: set port as Alternate function output Push-pull #define alternate_open_drain(port,half,pin) pin_config(##port##,##half##,##pin##,1,1)//11: set port as Alternate function output Open-drain // пример использования: //out_general_push_pull(D,L,7); //set port D, pin7 as General purpose output push-pull //out_general_open_drain(D,L,7); //set port as General purpose output push-pull //out_alternate_push_pull(D,L,7); //set port as alternate output push-pull //out_alternate_open_drain(D,L,7);//set port as alternate output push-pull //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
//vvvvvvvvvvv меняем настройки портов настроенных на вход vvvvvvvvvvvvvvv #define input_analog(port,half,pin) pin_config(##port##,##half##,##pin##,0,0)//00: set port as Analog mode #define input_float(port,half,pin) pin_config(##port##,##half##,##pin##,0,1)//01: set port as Floating input (reset state) #define input_pull_up_down(port,half,pin) pin_config(##port##,##half##,##pin##,1,0)//10: set port as Input with pull-up / pull-down // пример использования: //input_analog(port,half,pin);// set port as Analog mode //input_float(port,half,pin);// set port as Floating input (reset state) //input_pull_up_down(port,half,pin); //set port as Input with pull-up / pull-down //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ CODE enable_clock(D);//включение тактирования порта set_out_2MHz(D,L,7); //устанавливает порт D.x как выход с максимальной частотой 2 Мгц. general_push_pull(D,L,7); //set port as General purpose output push-pull
while (1) { if (pin_test(D,7)) { pin_off(D,7); } else { pin_on(D,7); } for (d=0; d<1000000; ++d){e ++;}; теперь програмка моргания светодиодом выглядет высокоуровневее
Сообщение отредактировал super_puper - Feb 8 2013, 03:47
|
|
|
|
|
Feb 8 2013, 05:29
|
Местный
Группа: Участник
Сообщений: 226
Регистрация: 10-07-09
Пользователь №: 51 126
|
Цитата(super_puper @ Feb 8 2013, 06:48) никто не написал ещё? Бросьте фигнёй заниматься... займитесь лучше чем-то полезным...
|
|
|
|
|
Feb 8 2013, 06:23
|
Профессионал
Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831
|
Цитата(super_puper @ Feb 8 2013, 06:27) теперь програмка моргания светодиодом выглядет высокоуровневее Ну, ну... Почитайте этот же код через этак пару месяцев, занявшись в это время другим проектом. Уверен, что хрен поймете, что это за абстрактные "D", 7, что они делают в проге. Нормальный высокоуровневый код НЕ должен выглядеть, как куча дефайнов поверх других дефайнов. Не это - его цель. Если уже делаете обертки, то они должны отражать суть программы, ее действия, а вовсе не суть самой этой обертки - избавиться от чужого говнокода. Я тоже пытался что-то вразумительное сделать поверх либ от ST, но в итоге из всего осталось только - пинодрыгание. Остальное сделать удобным под все линейки процев, увы, нереально. По сути, ST-либы реально ускоряют разработку, а в последствии переписать "быстрые" куски кода в обход библиотеки - это уже оптимизация, которая, зачастую, не так уж и обязательна. Например, мне приходилось "разворачивать" и оптимизировать код этих либ в обработчиках прерываний CAN, т.к. это реально ускорило работу системы... Чтобы не быть голословным, поделюсь своим вариантом обертки для пинов (STM32L). Базовый класс пина: Код class PinBase { public: PinBase(GPIO_TypeDef * port, UNSIGNED8 pinIndex) { this->port = port; this->pinIndex = pinIndex; switch ((UNSIGNED32)(this->port)) { case ((UNSIGNED32)GPIOA): RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); break; case ((UNSIGNED32)GPIOB): RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); break; case ((UNSIGNED32)GPIOC): RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE); break; case ((UNSIGNED32)GPIOD): RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOD, ENABLE); break; case ((UNSIGNED32)GPIOE): RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOE, ENABLE); break; case ((UNSIGNED32)GPIOH): RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOH, ENABLE); break; default : break; } disablePullUpAndPullDown(); }
void setAsInput(void) // MODER = 0 { port->MODER &= ~(((UNSIGNED32)0x03) << (pinIndex * 2)); port->MODER |= (((UNSIGNED32)0x00) << (pinIndex * 2)); } void setAsOutput(void) // MODER = 1 { port->MODER &= ~(((UNSIGNED32)0x03) << (pinIndex * 2)); port->MODER |= (((UNSIGNED32)0x01) << (pinIndex * 2)); }
typedef UNSIGNED8 AlternativeFunction; void setAsAlternative(AlternativeFunction function) // MODER = 2 { port->MODER &= ~(((UNSIGNED32)0x03) << (pinIndex * 2)); port->MODER |= (((UNSIGNED32)0x02) << (pinIndex * 2)); if (pinIndex < 8) { port->AFR[0] &= ~(((UNSIGNED32)0x0F) << (pinIndex * 4)); port->AFR[0] |= (((UNSIGNED32)function & 0x0F) << (pinIndex * 4)); } else { port->AFR[1] &= ~(((UNSIGNED32)0x0F) << ((pinIndex - 8) * 4)); port->AFR[1] |= (((UNSIGNED32)function & 0x0F) << ((pinIndex - 8) * 4)); } } void setAsAnalogInput(void) // MODER = 3 { disablePullUpAndPullDown(); port->MODER &= ~(((UNSIGNED32)0x03) << (pinIndex * 2)); port->MODER |= (((UNSIGNED32)0x03) << (pinIndex * 2)); } void setAsPushPull(void) { port->OTYPER &= ~(1 << pinIndex); } // OTYPER = 0 void setAsOpenDrain(void) { port->OTYPER |= (1 << pinIndex); } // OTYPER = 1 void setOutputSpeed400kHz(void) { port->OSPEEDR &= ~(((UNSIGNED32)0x03) << (pinIndex * 2)); port->OSPEEDR |= (((UNSIGNED32)0x00) << (pinIndex * 2)); } // OSPEEDR = 0 void setOutputSpeed2MHz(void) { port->OSPEEDR &= ~(((UNSIGNED32)0x03) << (pinIndex * 2)); port->OSPEEDR |= (((UNSIGNED32)0x01) << (pinIndex * 2)); } // OSPEEDR = 1 void setOutputSpeed10MHz(void) { port->OSPEEDR &= ~(((UNSIGNED32)0x03) << (pinIndex * 2)); port->OSPEEDR |= (((UNSIGNED32)0x02) << (pinIndex * 2)); } // OSPEEDR = 2 void setOutputSpeed40MHz(void) { port->OSPEEDR &= ~(((UNSIGNED32)0x03) << (pinIndex * 2)); port->OSPEEDR |= (((UNSIGNED32)0x03) << (pinIndex * 2)); } // OSPEEDR = 3 void disablePullUpAndPullDown(void) { port->PUPDR &= ~(((UNSIGNED32)0x03) << (pinIndex * 2)); port->PUPDR |= (((UNSIGNED32)0x00) << (pinIndex * 2)); } // PUPDR = 0 void enablePullUp(void) { port->PUPDR &= ~(((UNSIGNED32)0x03) << (pinIndex * 2)); port->PUPDR |= (((UNSIGNED32)0x01) << (pinIndex * 2)); } // PUPDR = 1 void enablePullDown(void) { port->PUPDR &= ~(((UNSIGNED32)0x03) << (pinIndex * 2)); port->PUPDR |= (((UNSIGNED32)0x02) << (pinIndex * 2)); } // PUPDR = 2 void lock(void) { volatile UNSIGNED32 tempRegister = ((UNSIGNED32)1 << pinIndex); port->LCKR = tempRegister | (1 << 16); // Set LCKK bit port->LCKR = tempRegister; // Reset LCKK bit port->LCKR = tempRegister | (1 << 16); // Set LCKK bit tempRegister = port->LCKR; // Read LCKK bit tempRegister = port->LCKR; // Read LCKK bit }
private: volatile GPIO_TypeDef * port; volatile UNSIGNED8 pinIndex; }; Шаблон класса пина: Код template <UNSIGNED32 PORT_ADDR, UNSIGNED8 PIN_INDEX> class Pin : public PinBase { public: Pin(void) : PinBase(((GPIO_TypeDef*)PORT_ADDR), PIN_INDEX) { setToLow(); }
inline void setToHigh(void) __attribute__((always_inline)) { ((GPIO_TypeDef*)PORT_ADDR)->BSRRL = (((UNSIGNED32)1) << PIN_INDEX); } inline void setToLow(void) __attribute__((always_inline)) { ((GPIO_TypeDef*)PORT_ADDR)->BSRRH = (((UNSIGNED32)1) << PIN_INDEX); } inline bool isHigh(void) const __attribute__((always_inline)) { return (((((GPIO_TypeDef*)PORT_ADDR)->IDR) & (((UNSIGNED32)1) << PIN_INDEX)) != 0); } inline bool isLow(void) const __attribute__((always_inline)) { return (((((GPIO_TypeDef*)PORT_ADDR)->IDR) & (((UNSIGNED32)1) << PIN_INDEX)) == 0); } }; Увы, в компиляторе KEIL нельзя в качестве параметров шаблонов использовать указатели, хотя стандарт C++ это вполне допускает. Поэтому приходится его обманывать: Код #define PORTA (((UNSIGNED32)GPIOA)) #define PORTB (((UNSIGNED32)GPIOB)) #define PORTC (((UNSIGNED32)GPIOC)) ..... Ну, и пример использования (внешний акселерометр с программным SPI). H-файл: Код // ACCELEROMETER #define PORT_ACCEL_CS (PORTB) #define PIN_ACCEL_CS (15)
#define PORT_ACCEL_SCK (PORTB) #define PIN_ACCEL_SCK (14)
#define PORT_ACCEL_MOSI (PORTB) #define PIN_ACCEL_MOSI (13)
#define PORT_ACCEL_MISO (PORTB) #define PIN_ACCEL_MISO (12)
#define PORT_ACCEL_INT (PORTB) #define PIN_ACCEL_INT (11)
..... ..... ....
class Accelerometer : public Singleton<Accelerometer> { .....
private: Pin<PORT_ACCEL_CS, PIN_ACCEL_CS> pinCS; Pin<PORT_ACCEL_SCK, PIN_ACCEL_SCK> pinSCK; Pin<PORT_ACCEL_MOSI, PIN_ACCEL_MOSI> pinMOSI; Pin<PORT_ACCEL_MISO, PIN_ACCEL_MISO> pinMISO; Pin<PORT_ACCEL_INT, PIN_ACCEL_INT> pinINT; ..... }; CPP-файл: Код void Accelerometer::initialize(void) { .... pinCS.setAsOutput(); pinCS.setAsPushPull(); pinCS.setOutputSpeed10MHz(); pinCS.setToLow();
.... pinMISO.setAsInput(); pinMISO.enablePullDown();
pinINT.setAsInput(); pinINT.enablePullDown(); .... }
void Accelerometer::writeRegister(UNSIGNED8 address, UNSIGNED8 value) { pinCS.setToLow(); pinSCK.setToLow();
for (UNSIGNED8 bit = 8; bit > 0; --bit) { if ((address & 0x80) != 0) pinMOSI.setToHigh(); else pinMOSI.setToLow(); pinSCK.setToHigh(); address = address << 1; pinSCK.setToLow(); }
for (UNSIGNED8 bit = 8; bit > 0; --bit) { if ((value & 0x80) != 0) pinMOSI.setToHigh(); else pinMOSI.setToLow(); pinSCK.setToHigh(); value = value << 1; pinSCK.setToLow(); }
pinSCK.setToHigh(); pinCS.setToHigh(); } p.s. Буду рад, если мои идеи кому-то еще пригодятся
--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
|
|
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|