Преобразование операций побитового сдвига в определенный формат двоичных чисел (для микроконтроллера PIC)

Я работаю над несколькими проектами домашней автоматизации с PIC12F675. Чтобы максимизировать переносимость, я использую операции побитового сдвига для сопоставления GPIO, например:

// Hardware Mapping
#define pressure_pin  (1<<0)  // Pressue sensor output (analog input) 
#define button_pin    (1<<1)  // Multi-function button (digital input)
#define buzzer_pin    (1<<2)  // NPN transitor to activate buzzer (digital output)
#define pause_pin     (1<<4)  // NPN transistor to pause button (digital output)
#define led_pin       (1<<6)  // Led (digital output)
#define PORT          GPIO    // Port 

Чтобы установить входные контакты («pressure_pin» и «button_pin»), необходимо изменить регистры TRISIO, ANSEL и ADCON0. Изменения TRISIO и ANSEL легко выполнить следующим образом:

TRISIO |= (button_pin | pressure_pin);  // Output pins 
ANSEL = (0x50 | pressure_pin);          // Fosc/16 and 'pressure_pin' as analog input`

У ADCON0 должны быть установлены биты 7 и 0, а биты 3 и 2 должны получать номер GPIO, используемый в качестве аналогового входа, как показано на рисунке ниже (извлечено из таблицы данных PIC12F675).

введите сюда описание изображения

Итак, мне нужно, чтобы ADCON0 получил одно из следующих значений (согласно определению «pressure_pin»):

Pressure_pin ADCON0 (1<<0) 0x81 (1<<1) 0x85 (1<<2) 0x89 (1<<3) 0x8D

Мое решение было:

ADCON0 = 0x81;  // 0b10000001. ADC value right justified ; ADC turned on
ADCON0 |= (((0x82>>(pressure_pin-1))&0x1)<<2) | (((0x88>>(pressure_pin-1))&0x1)<<3);

Этот фрагмент кода воспроизводит мое решение на языке C:

int main(int, char**)
{
    unsigned char pressure_pin = (1<<3);
    unsigned char ADCON0 = 0x81;
    ADCON0 |= (((0x82>>(pressure_pin-1))&0x1)<<2) | (((0x88>>(pressure_pin-1))&0x1)<<3);   
    return 0;
}

Учитывая ограничения PIC12F675, существует ли более элегантное и/или более эффективное решение для этого преобразования?

Заранее спасибо.

Любой разумный компилятор должен выполнить это преобразование автоматически.

Barmar 21.06.2024 18:59

@Бармар, я думаю, он хочет чего-то такого тривиального. Компилятор не будет знать значение как постоянное выражение

0___________ 21.06.2024 19:26
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
63
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Возможно, таблица поиска. Никаких расчетов не требуется.

const unsigned char data[] = {[1 << 0] = 0x81, [1 << 1] = 0x85, [1 << 2] = 0x89, [1 << 3] = 0x8D};
unsigned char foo(unsigned pressurepin)
{
    return data[pressurepin];
}

У меня нет компилятора изображений, но аналогичный 8-битный код, сгенерированный для AVR, показывает, что он будет намного быстрее (и, скорее всего, меньше).

https://godbolt.org/z/j9aWP3Tqx

0___________, спасибо за точный и краткий ответ! Компиляция вашего кода в MPLAB X (XC8) привела к получению ассемблерного кода немного большего размера, чем тот, который был получен с помощью моего решения (мое: 37% использования программной памяти; ваше: 38,3%). Ваше решение работает, более читабельно и занимает почти ту же программную память, что и мое. Код, сгенерированный компилятором AVR, невероятно больше, чем код, сгенерированный XC8!

Nelson 22.06.2024 00:01

Насколько я понимаю, ваша цель — иметь возможность устанавливать в регистр только некоторые определенные биты. Я правильно понял?

Если это так, вы можете использовать базовые операции маскировки и сдвига в функции для установки канала АЦП. Я рекомендую вам сделать это в функции, поскольку в вашем приложении может потребоваться изменить канал АЦП более одного раза. Это уменьшит использование ПЗУ. Итак, следующий фрагмент дает вам представление о том, как это будет реализовано: Давайте определим некоторые константы для маскировки и смещения:

#define ADC_CHANNEL_MASK    0xC // or 0b00001100
#define ADC_CHANNEL_OFFSET  2   // bit offset to be used in shift operations

Теперь давайте реализуем функцию ADC_setChannel:

void ADC_setChannel(char channel) {
    // clear the channel bits first
    ADCON0 &= ~ADC_CHANNEL_MASK;
    // mask the channel parameter for 2 bits first, then left shift the value 2 times
    // then or it with the current register value. This way only tha channel bits
    // will be modified
    ADCON0 |= (channel & 3) << ADC_CHANNEL_OFFSET;
    // Delay for 20 micros to comply the acquisition time after changing the channel
    // See the ADC section of datasheet for the calculation of acquisition time
    __delay_us(20);
}

Однако если ваше приложение в течение всего срока службы использует только один канал АЦП, вам не нужно реализовывать его как функцию. Вместо этого вы должны настроить его один раз в основной функции перед входом в основной цикл. Те же операции, но не в функции. См. следующий фрагмент:

// The definitions made above + your pin definitions
...

void main(void) {
    // Other init codes go here...
    ...
    ADCON0 &= ~ADC_CHANNEL_MASK;
    ADCON0 |= 2 << ADC_CHANNEL_OFFSET; // Let's say you wanna select 2nd AN channel
    __delay_us(20);
    ...

    while(1) {
        // Application code
    }
}

Но если вы также хотите сопоставить контакты порта с номерами каналов, вам следует использовать простой массив, содержащий номера каналов, соответствующие номеру порта. В этом случае имеется 4 канала AN, входы которых — GP0, GP1, GP2 и GP4 соответственно. И эти цифры сильно различаются в зависимости от модели MCU. Вот почему я не знаю какой-либо формулы для решения этого вопроса о сопоставлении каналов для всех устройств с использованием битовых сдвигов, поскольку размещение битов не является последовательным. Использование массивов более практично для создания такого типа отображения, поскольку числа невелики. Массив будет выглядеть так:

// We add new definitions for ADC
#define GPIO_PIN_COUNT 7

// The indexes represent the GPIO pin numbers. So if the corresponding pin does not have
// analog feature, this array will give us -1.
char getChannelForPin[GPIO_PIN_COUNT] = { 0, 1, 2, -1, 3, -1, -1 };

// Then you would use that array like this in the application
char channel = getChannelForPin[pressure_pin];
if (channel >= 0) {
    ADC_setChannel(channel);
}
else {
    // Handle; that pin does not have analog feature
}

Козьмотроник, большое спасибо за полный и поучительный ответ. Он, конечно, более читабелен и элегантен, чем мой. Однако использование компиляции (с помощью XC8) упрощенной версии вашего кода привело к использованию 43,2% памяти программы и 67,2% памяти данных. Мое решение использует 37% и 35,9% соответственно. Если хотите, я могу отредактировать ответ, включая проведенные мной тесты. Еще раз спасибо!

Nelson 21.06.2024 23:54

Другие вопросы по теме