Рекомендуемый способ выполнения приведения к меньшему типу на микроконтроллере в C

#c #casting #microcontroller

#c #Кастинг #микроконтроллер

Вопрос:

Я программирую для Arduino Due, который основан на 32-битном микроконтроллере. Я хочу прочитать регистр результатов АЦП (если я прав, он имеет ширину 32 бита, но его истинная максимальная ширина составляет 12 бит, что соответствует разрешению АЦП), и записать его в заданном месте в массив целых чисел шириной 16 бит.

Это работает:

 volatile uint16_t Buf[nchannels];

[... some code...]

void ADC_Handler() {
  for (int i = 0; i < nchannels; i  )
  {
      Buf[i] = (volatile uint16_t) * (ADC->ADC_CDR   channels[i]); // my modification
  }
   FlagConversion = true;
}
  

Но использование вместо этого более «явного» приведения не работает:

 Buf[i] = dynamic_cast<volatile uint16_t *>(ADC->ADC_CDR   channels[i]);
  

Производит:

«ошибка: не удается выполнить dynamic_cast ‘(((RoReg *)1074528336) ((sizetype)(((unsigned int)каналы [i]) * 4)))’ (типа ‘RoReg * {он же volatile long unsigned int *}’) для ввода ‘volatile uint16_t * {он жеvolatile short unsigned int*}’ (цель не является указателем или ссылкой на класс)»

и аналогичные неясные ошибки со статическими и переинтерпретированными приведениями:

«ошибка: не удается выполнить dynamic_cast ‘(((RoReg *)1074528336) ((sizetype)(((unsigned int)каналы [i]) * 4)))’ (типа ‘RoReg * {он же volatile long unsigned int *}’) для ввода ‘volatile uint16_t * {он жеvolatile short unsigned int*}’ (цель не является указателем или ссылкой на класс)»

и

«ошибка: недопустимое преобразование из’volatile uint16_t * {он же volatile short unsigned int *}’ в’uint16_t {он же short unsigned int}»

  • Есть идеи, почему эти более явные приведения завершаются неудачей?
  • Что было бы наилучшей практикой здесь?

Комментарии:

1. Первый код разыменовывает что-то, а затем преобразует результат в целое число. Второй код — это приведение к указателю без разыменования. Вы хотите Buf[i] = * static_cast<volatile uint16_t *>(ADC->ADC_CDR channels[i]); добавить разыменование и использовать static_cast вместо этого?

2. Было бы хорошо, если бы вы вернулись к своей книге по C и снова прочитали о приведении. dynamic_cast применяется только для типов, которые имеют RTTI, поэтому для классов, которые имеют хотя бы виртуальный метод on. Для целочисленных типов у него нет сеанса.

3. Хорошо, спасибо :). Так Buf[i] = * reinterpret_cast<volatile uint16_t *>(ADC->ADC_CDR channels[i]); что работает нормально, но статическое и динамическое приведение не выполняется (вероятно, из-за того, что говорит @MarekR). Как вы думаете, что лучше всего использовать в таких случаях?

Ответ №1:

«Безопасный» способ выполнить приведение — сначала разыменовать указатель на 32-разрядные данные, затем замаскировать верхние 16 бит этого элемента данных, а затем static_cast преобразовать результат в ваши 16-разрядные данные назначения:

     Buf[i] = static_cast<volatile uint16_t>( *(ADC->ADC_CDR   channels[i]) amp; 0x0FFFF );
  

Всегда лучше использовать «простейшую» форму приведения и избегать приведений в стиле C reinterpret_cast и, когда это возможно. В этом случае нет смысла приводить указатель, и гораздо безопаснее и проще преобразовать / привести сами данные.

На самом деле, маскирование с 0x0FFFF помощью, строго говоря, совершенно не нужно и излишне. Однако любому будущему читателю вашего кода становится ясно, что вы действительно знаете, что делаете!

Комментарии:

1. Небольшой вопрос @AdrianMole: зачем писать 0x0FFFF вместо 0xFFFF ? Это для того, чтобы убедиться, что мы помним, что мы вырезали?

2. @Zorglub29 Я просто хотел бы добавить хотя бы один начальный ноль, чтобы было ясно, что я знаю, что делаю. Другие люди будут писать полные 32 бита: 0x0000FFFF а третьи будут опускать начальный ноль. Это просто вопрос стиля / вкуса.