[uquote="dosikus",url="/forum/viewtopic.php?p=3435232#p3435232"]
Код: Выделить всё
#define GreenLED_OFF GPIOC->BSRR = GPIO_BSRR_BR9
#define GreenLED_ON GPIOC->BSRR = GPIO_BSRR_BS9
#define BlueLED_OFF GPIOC->BSRR = GPIO_BSRR_BR8
#define BlueLED_ON GPIOC->BSRR = GPIO_BSRR_BS8
[/uquote]
Не так давно занимался похожим ногодрыжеством и придумалось небольшое "улучшательство". Если посмотреть на эти макросы, то все они повторяют друг друга, за исключением последних двух символов, которые и кодируют действие. В общем виде тело макроса для любого действия над любым светодиодом можно записать, как
где x -- действие(вкл/выкл), а y -- конкретный светодиод. Тут сразу на ум приходит, что неплохо бы "нарисовать" макрос, который по виду имитировал функцию и выглядел примерно так:
где COLOR -- это цвет светодиода, а STATE -- состояние (вкл/выкл). Кодируем цвета и действия:
Код: Выделить всё
#define GREEN 9
#define BLUE 8
#define ON S
#define OFF R
Дальше получается непонятка, т.к. если записать макрос в виде
Код: Выделить всё
LED(COLOR, STATE) GPIOC->BSRR = GPIO_BSRR_BSTATECOLOR
то препроцессор такого авангардизма не поймет и справедливо пошлет нафиг. На помощь можно призвать директиву "склейки" -- ##:
Код: Выделить всё
LED(COLOR, STATE) GPIOC->BSRR = GPIO_BSRR_B##STATE##COLOR
Такой вариант уже ближе к работоспособному состоянию, но при использовании макроса возникает неприятный эффект, когда запись вида:
разворачивается в совершенно негодное для данных целей представление:
компилятор в данном случае негодует, хотя препроцессор сделал все так, как его просили. Чтобы исправить и это, обычно прибегают к небольшому трюку, который называется "экранирование". Суть его в том, чтобы операцию склейки выполнять отдельным макросом, завернутым в экран. Выглядеть это может как-то так:
Код: Выделить всё
#define GLUE2(A, B) A##B
#define GLUE(A, B) GLUE2(A, B)
GLUE2 -- сама склейка, GLUE -- экранирующая обертка.
В конечном счете макрос должен приобрести нижеследующий вид:
Код: Выделить всё
#define LED(COLOR, STATE) GPIOC->BSRR = GLUE(GPIO_BSRR_B, GLUE(STATE, COLOR))
Теперь, чтобы иметь все вместе перед глазами, старая и новая формы одновременно, плюс проверочный код:
Код: Выделить всё
#include "stm32f1xx.h"
#define GreenLED_OFF GPIOC->BSRR = GPIO_BSRR_BR9
#define GreenLED_ON GPIOC->BSRR = GPIO_BSRR_BS9
#define BlueLED_OFF GPIOC->BSRR = GPIO_BSRR_BR8
#define BlueLED_ON GPIOC->BSRR = GPIO_BSRR_BS8
#define ON S
#define OFF R
#define GREEN 9
#define BLUE 8
#define GLUE2(A, B) A##B
#define GLUE(A, B) GLUE2(A, B)
#define LED(COLOR, STATE) GPIOC->BSRR = GLUE(GPIO_BSRR_B, GLUE(STATE, COLOR))
int main(void) {
while (1) {
GreenLED_ON;
LED(GREEN, ON);
for(int i = 0; i< 0x000FFFFF; i++);
GreenLED_OFF;
LED(GREEN, OFF);
BlueLED_ON;
LED(BLUE, ON);
for(int i = 0; i< 0x000FFFFF; i++);
BlueLED_OFF;
LED(BLUE, OFF);
}
}
Для наглядности макросы в оригинальном и доработанном виде вызываются друг за другом. После обработки препроцессором вышеприведенный код приобретает уже такой вид:
Код: Выделить всё
int main(void) {
while (1) {
((GPIO_TypeDef *)((0x40000000U + 0x00010000U) + 0x00001000U))->BSRR = (0x1U << (9U));
((GPIO_TypeDef *)((0x40000000U + 0x00010000U) + 0x00001000U))->BSRR = (0x1U << (9U));
for(int i = 0; i< 0x000FFFFF; i++);
((GPIO_TypeDef *)((0x40000000U + 0x00010000U) + 0x00001000U))->BSRR = (0x1U << (25U));
((GPIO_TypeDef *)((0x40000000U + 0x00010000U) + 0x00001000U))->BSRR = (0x1U << (25U));
((GPIO_TypeDef *)((0x40000000U + 0x00010000U) + 0x00001000U))->BSRR = (0x1U << (8U));
((GPIO_TypeDef *)((0x40000000U + 0x00010000U) + 0x00001000U))->BSRR = (0x1U << (8U));
for(int i = 0; i< 0x000FFFFF; i++);
((GPIO_TypeDef *)((0x40000000U + 0x00010000U) + 0x00001000U))->BSRR = (0x1U << (24U));
((GPIO_TypeDef *)((0x40000000U + 0x00010000U) + 0x00001000U))->BSRR = (0x1U << (24U));
}
}
Что полностью доказывает идентичность макросов в плане производимых ими действий.
Вышеприведенный код не годится для заливки в МК, т.к. нет никакой инициализации железа и единственное, что от него требовалось -- это посмотреть, что будет на выходе препроцессора.