#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
а третьи будут опускать начальный ноль. Это просто вопрос стиля / вкуса.