Задать вопрос
@djEban

Возможно ли как-то использовать обычную функцию заместо макро-функции?

#define PORTB 0x04
#define PORTB0 0

void bit_set(uint8_t port, uint8_t bit)
{
    port |= (1 << bit);
}

int main(void)
{
    bit_set(PORTB, PORTB0);
}

Если задавать значение регистра через функцию, то, насколько я понял, компилятор оптимизирует это дело, и выходной результат такой, будто я вовсе не вызывал bit_set
#define BIT_SET(port, bit) (port |= (1 << bit))

int main(void)
{
    BIT_SET(PORTB, PORTB0);
}

Хотя вот такой вариант, с макро-функцией, работает, так как это просто текстовая замена
Конечныый результат в ассемблере должен быть такой
_main:
    sbi 0, 0x04
  • Вопрос задан
  • 374 просмотра
Подписаться 2 Простой Комментировать
Решения вопроса 2
jcmvbkbc
@jcmvbkbc
"I'm here to consult you" © Dogbert
#define BIT_SET(port, bit) (port |= (1 << bit))

int main(void)
{
    BIT_SET(PORTB, PORTB0);
}


вот такой вариант, с макро-функцией, работает, так как это просто текстовая замена

Он работает, потому что с таким определением BIT_SET PORTB не может быть определён просто как 0x04. Потому что просто текстовая замена 0x04 |= 1 << 0 не имеет смысла. Он определён как volatile ссылка на память с адресом 0x04. Когда ты научишься передавать ссылку на такую память в функцию, функция тоже начнёт работать.
Ответ написан
Vindicar
@Vindicar
RTFM!
bit_set() принимает аргумент по значению, т.е. принимает его копию. От того, что эта копия изменена внутри функции, снаружи ничего не поменяется. А раз других побочных эффектов у функции нет, компилятор удаляет её вызов.
Макрос же после всех подстановок сделает просто присваивание указанной переменной. Тут побочный эффект есть.
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
includedlibrary
@includedlibrary
Да, возможно, но вы это делаете не правильно.
#define PORTB 0x04
#define PORTB0 0

void bit_set(uint8_t port, uint8_t bit)
{
    port |= (1 << bit);
}

int main(void)
{
    bit_set(PORTB, PORTB0);
}


Здесь вообще ничего не делается, это же void функция. Если поменять её тип на uint8_t и добавить return, то компилятор всё равно оставит и функцию и её вызов, потому что она не объявлена статической, а значит может вызываться откуда-то ещё.
#include <stdint.h>
#include <stdio.h>

#define PORTB 0x04
#define PORTB0 0

// вызов функции останется
uint8_t bit_set(uint8_t port, uint8_t bit)
{
    return port |= (1 << bit);
}

int main(void)
{
    uint8_t res = bit_set(PORTB, PORTB0);
    printf("%hhd\n", res);
}


Если же сделать функцию статической, то её вызов будет заинлайнен, но лучше тогда сразу static inline использовать:
#include <stdint.h>
#include <stdio.h>

#define PORTB 0x04
#define PORTB0 0

// А вот так функция будет заинлайнена
static inline uint8_t bit_set(uint8_t port, uint8_t bit)
{
    return port |= (1 << bit);
}

int main(void)
{
    uint8_t res = bit_set(PORTB, PORTB0);
    printf("%hhd\n", res);
}

И да, чтобы переиспользовать static inline функции, их надо объявлять в заголовочных файлах, объявить прототип в заголовочном файле, а тело в .c файле не выйдет.
Рекомендую проверить самостоятельно с помощью https://godbolt.org. Не забудьте, что без указания флагов оптимизации, ничего заинлайнено не будет, даже если объявить функцию, как static inline
Ответ написан
@vanyamba-electronics
inline void bit_set(uint8_t port, uint8_t bit)
{
    port |= (1 << bit);
}
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы