Не удается изменить указатель на порт в главном цикле

Я тестирую на Arduino UNO. Я хочу использовать timer1 и прерывания для создания различных светодиодных шаблонов. Светодиоды могут охватывать несколько портов, т. е. PORTB и PORTD. Я хочу использовать указатель "volatile uint8_t*" для сохранения того, следует ли переключать вывод на PORTB или PORTD, т. е. хранить указатель &PORTB или &POINTD. А затем используйте его по мере необходимости. Однако после установки я, похоже, не смогу изменить указатель в цикле.

Еще несколько деталей: Я использую timer1 в режиме CTC (очистить при сопоставлении) и использую прерывания OCR1A и OCR1B. Когда выходное сравнение прерывания совпадает, индикатор включается, и для основного контура устанавливается флаг для изменения светодиодов. Когда выходные данные сравнивают совпадения прерываний B, светодиод выключается. Оператор switch работает, и контакты светодиода могут быть изменены, т. е. next_led и current_led, однако операторы next_port и current_port игнорируются в основном цикле. Изменение вывода светодиода работает при переходе с варианта 3 на вариант 1 (когда они оба являются PORTB), но при изменении переменной next_port на PORTD это полностью игнорируется.

В самом начале кода, если я инициализирую current_port и next_port с помощью &PORTD, то загорится только индикатор PORTD, и основной цикл не изменит переменные current_port и next_port на &PORTB.

Я пробовал использовать volatile uint16_t* для следующего порта и текущего порта, и это тоже не работает.

Что я делаю не так?

int red_led = 9;    //PORTB 1
int green_led = 10; //PORTB 2

int led3 = 5;       //PORTD 5

volatile uint8_t current_led = 1; //port number of led
volatile uint8_t next_led = 1; 

volatile uint8_t *current_port = &PORTB; 
volatile uint8_t *next_port = &PORTB;    

int counter = 0; 
volatile int change_led_flag = 0;
volatile uint8_t led_state = 1;

uint16_t ocr1a_val = 10000-1; //determines period. 16MHz clock, Prescaler=8, 10000-1 ticks = 5ms period (200Hz)
uint16_t ocr1b_val = 9900-1; //determines duty cycle (99% for 10000 ticks)


void setup() {
  pinMode(red_led, OUTPUT);
  pinMode(green_led, OUTPUT);
  pinMode(led3, OUTPUT);

  TCCR1A = 0;
  TCCR1B = 0;

  //set CTC mode
  TCCR1B |= (1<<WGM12);

  //set OCR1A value
  OCR1A = ocr1a_val;
  //set OCR1B value
  OCR1B = ocr1b_val;

  //set pre-scaler to 8
  TCCR1B |= (1 << CS11);

  //enable output compare interrupts
  TIMSK1 |= (1 << OCIE1A);
  TIMSK1 |= (1 << OCIE1B);

  sei();
}

void loop() {
  if(change_led_flag == 1){
  change_led_flag = 0;
  current_led = next_led; //set the current led to what was just turned on
  current_port = next_port; //this statement doesn't do anything.
  switch (led_state) {
    case 1:
      led_state = 2;
      next_led = 2;
      next_port = &PORTB; //this statement doesn't do anything
      break;
    case 2:
      led_state = 3;
      next_led = 5;
      next_port = &PORTD; //this statement doesn't do anything
      break;
    case 3:
      led_state = 1;
      next_led = 1;
      next_port = &PORTB; //this statement doesn't do anything
      break;
    } //end switch statement
  } //end if
  
} //end loop()


//turn on next_led and set change_led_flag
ISR(TIMER1_COMPA_vect){
  *next_port |= (1 << next_led); //turn on led
  change_led_flag = 1; //set flag to update next led
}

//turn off current_led
ISR(TIMER1_COMPB_vect){
  *current_port &= ~(1 << current_led); //turn off led
}

, 👍-1

Обсуждение

Я получаю ошибку: не удается преобразовать "volatile uint8_t* {aka volatile без знака*}" в "volatile uint16_t* {aka volatile без знака int*}" при инициализации volatile uint16_t *next_port = &PORTB;, @Tommy Ray

Добро пожаловать в SE/Arduino! Пожалуйста, пройдите [экскурсию], чтобы узнать, как работает этот сайт. Если у вас есть какая-либо дополнительная информация, пожалуйста, [отредактируйте] свой вопрос, не оставляйте комментариев. Это не будет прочитано, это не форум. -- Однако, пожалуйста, сведите свою программу к самому простому примеру, где вы меняете только указатель порта и используете его для доступа к портам. -- Если это в целом не работает, вы можете вернуться к использованию функций Arduino, таких как "digitalWrite ()" или, например, использовать "перечисление" и "переключатель"., @the busybee


1 ответ


Лучший ответ:

0

Я не знаю, в чем ваша проблема, но в любом случае это проблема. Сам указатель, а не только то, на что он указывает, также должен быть квалифицирован как изменчивый. То есть:

volatile uint8_t *volatile current_port = &PORTB; 
volatile uint8_t *volatile next_port = &PORTB;    

Изменчивость после декларатора указателя ( *) определяет изменчивое качество самого указателя. Изменчивость, которая у вас уже была до декларатора, просто определяет то, на что указано, в данном случае порт, который необходим по аналогичным причинам.

Без того, чтобы сам указатель был изменчивым, вы не можете ожидать, что изменения в нем будут правильно отражены между контекстами ISR и обычной основной линией выполнения, которая запускает настройку, цикл и почти все остальное. Компилятор/оптимизатор может предположить, что, поскольку он не видит никакого способа добраться до кода, который может изменить значение, значение, следовательно, не меняется. Сообщения ISR находятся вне его поля зрения.

Также в случае, когда вы обновляете next_port за пределами ISR, вам, вероятно, следует убедиться, что прерывания отключены во время его обновления:

noInterrupts();
next_port = &PORTB;
interrupts();

Поскольку значение указателя является двухбайтовым адресом, если вы не отключите прерывания при его назначении, ваш ISR может быть отправлен в середине назначения, когда объект указателя еще не действителен. Я говорю "должен" выше, потому что в вашей ситуации адреса, которые используются для портов, могут иметь одинаковое значение верхнего байта. Это хорошая привычка, несмотря ни на что.

,

Большое вам спасибо! Добавление дополнительной " изменчивости` исправило код. (Это первый раз, когда я использовал изменчивый указатель, который сам по себе является изменчивым!) Вы делаете некоторые хорошие замечания по неатомным заданиям. Вероятно, было бы хорошей идеей поместить назначение "next_led" также в этот блок без прерываний, чтобы ISR не отправлялся с несоответствующим светодиодом и портом. Еще раз спасибо!, @Tommy Ray