Урок 10. Типы данных языка Си для STM32.

Уроки STM32

Урок короткий, но очень важный. Разберемся в базовых типах данных STM32. Понимание этого вопроса абсолютно необходимо для разработки программ.

Предыдущий урок     Список уроков     Следующий урок

Все действия в программе мы производим над переменными. Поэтому необходимо точно знать форматы, размеры переменных, диапазоны чисел, которые они способны содержать. Переменные должны быть объявлены с указанием типа данных до использования в программе. Тип данных и задает параметры переменных.

 

Все это мы знаем из уроков Ардуино. Но базовые типы данных системы Ардуино и компилятора C для STM32 отличаются. Формальное применение знаний, полученных из курса Ардуино, может привести к фатальным последствиям для программ STM32. Давайте переучиваться.

Некоторые базовые типы данных стандартов языка программирования Си зависят от используемого микроконтроллера. В значительной мере от разрядности данных, с которыми он оперирует. Например, для 8ми разрядного микроконтроллера Ардуино тип int это 16 разрядов. Тот же тип для STM32 составляет 32 разряда.

Я не буду рассказывать о принципах, по которым стандарт языка Си определяет форматы базовых типов данных. Я буду освещать этот вопрос применительно к нашему микроконтроллеру STM32, нашей среде программирования.

Выделим из стандарта C99 языка программирования Си следующие базовые типы данных.

Тип данных Пояснение Разрядность
бит (байт)
Диапазон чисел
char
int8_t
Целочисленный знаковый тип. 8 (1) - 128 … 127
unsigned char
uint8_t
Целочисленный беззнаковый тип. 8 (1) 0 … 255
short
int16_t
Целочисленный знаковый тип. 16 (2) - 32768 … 32768
unsigned short
uint16_t
Целочисленный беззнаковый тип. 16 (2) 0 … 65535
int
int32_t
Целочисленный знаковый тип. 32 (4) - 2147483648 …  2147483647
unsigned int
uint32_t
Целочисленный
беззнаковый тип.
32 (4) 0 … 4294967295
long
int32_t
Целочисленный знаковый тип. 32 (4) - 2147483648 …  2147483647
unsigned long
uint32_t
Целочисленный беззнаковый тип. 32 (4) 0 … 4294967295
long long
int64_t
Целочисленный знаковый тип. 64 (8) - 9223372036854775808

9223372036854775807
unsigned long long
uint64_t
Целочисленный беззнаковый тип. 64 (8) 0 … 18446744073709551615
float Формат с плавающей запятой одинарной точности. 32 (4) ± (3,4 * 10 -38

3,4 * 10 +38)
double Формат с плавающей запятой двойной точности. 64 (8) ± (1,7 * 10 -308

1,7 * 10 +308)
long double Формат с плавающей запятой повышенной точности. 64 (8) ± (1,7 * 10 -308

1,7 * 10 +308)
_Bool Логический тип. 8 (1) 0 (ложь)  или 1 (истина)

Я подкорректировал параметры в соответствии с нашей средой программирования, поверил все эти типы данных.

 

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

int n = sizeof(long); // считать размер типа long

Я написал программу, которая определяет размеры разных типов данных и выводит их через последовательный порт. Это основной блок программы.

sprintf((char *)str,"Тип char - %u байт\r\n", sizeof(char));
outputRes();
sprintf((char *)str,"Тип short - %u байт\r\n", sizeof(short));
outputRes();
sprintf((char *)str,"Тип int - %u байт\r\n", sizeof(int));
outputRes();
sprintf((char *)str,"Тип long - %u байт\r\n", sizeof(long));
outputRes();
sprintf((char *)str,"Тип long long - %u байт\r\n", sizeof(longlong));
outputRes();
sprintf((char *)str,"Тип float - %u байт\r\n", sizeof(float));
outputRes();
sprintf((char *)str,"Тип double - %u байт\r\n", sizeof(double));
outputRes();
sprintf((char *)str,"Тип long double - %u байт\r\n", sizeof(longdouble));
outputRes();
sprintf((char *)str,"Тип _Bool - %u байт\r\n\r\n", sizeof(_Bool));
outputRes();

Вот, что показал монитор последовательного порта CoolTerm.

Окно монитора CoolTerm

Кто захочет проверить сам, полностью проект программы можно загрузить по ссылке:

 

Зарегистрируйтесь и оплатитеВсего 60 руб. в месяц за доступ ко всем ресурсам сайта!
 

В программах для STM32 можно пользоваться всеми типами данных из таблицы. Но я предлагаю ввести ограничения.

 

В таблице есть целочисленные типы данных с явно заданной разрядностью.

Их имена образуются из символов:

  • int –целое знаковое;
  • uint –целое беззнаковое (к int добавили u);
  • число разрядов;
  • _t – тип.

Формат таких типов данных не зависит от разрядности микроконтроллера.

  • int8_t – целое знаковое 8 разрядов;
  • uint8_t – целое беззнаковое 8 разрядов;
  • int16_t – целое знаковое 16 разрядов;
  • uint16_t – целое беззнаковое 16 разрядов;
  • int32_t – целое знаковое 32 разрядов;
  • uint32_t – целое беззнаковое 32 разрядов;
  • int64_t – целое знаковое 64 разрядов;
  • uint64_t – целое беззнаковое 64 разрядов;

Хороший стиль использовать при разработке программ для STM32 именно такие описания для целочисленных переменных. Давайте так и будем поступать в дальнейшем.

Я бы еще допустил использование типа char. Он явно указывает на назначение переменной – хранение кода символа и, в какой-то степени, улучшает читаемость программы.

Типы с плавающей запятой будем использовать из таблицы.

Думаю, вы заметили, что отсутствуют привычные логические типы данных bool и boolean.В языке Си современной редакции они заменены на тип _Bool.

Но дело не в формальном изменении имени. Переменные для него не могут принимать значения true и false. Теперь это 0 – ложно и 1 – истинно. Часто в качестве логического типа используют uint8_t.

Можно вернуть привычный логический тип boolean и значения для него, если подключить заголовочный файл stdbool.h. Переназначение имен будет происходить через макросы.

Я предлагаю отказаться от такого способа и оставить принцип описания логических переменных, заданный разработчиками стандарта языка Си.

Но, в проектах C++ тип _Bool не поддерживается. Давайте использовать для логических переменных тип uint8_t.

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

Еще два слова по поводу констант. Они тоже могут иметь знак. К беззнаковой константе добавляется буквы U или u.

int x = 234U; // беззнаковое число
int x = 0x001Eu; // беззнаковое число

В противном случае (без буквы U) константа считается знаковой.

int x = 234; // знаковое число
int x = 0x001E; // знаковое число

В случае использования знаковой константы старший разряд считается знаком, и отрицательные числа воспринимаются в дополнительном коде.

 

В следующем уроке будем займемся обработкой сигналов кнопок.

Предыдущий урок     Список уроков     Следующий урок

3

Автор публикации

не в сети 11 часов

Эдуард

285
Комментарии: 1946Публикации: 199Регистрация: 13-12-2015

10 комментариев на «Урок 10. Типы данных языка Си для STM32.»

  1. uint1_t – целое беззнаковое 1 разряд; бит мне захотелось, Кейл не вкусил моего порыва…

    0
  2. почему не писать так:

    int32_t x = 234; // знаковое число
    uint32_ x = 234; // беззнаковое число

    вместо:

    int x = 234; // знаковое число
    int x = 234u; // беззнаковое число

    и сколько бит выделит компилятор для этого «int» ?

    0
  3. Странно, у меня тип _Bool вызывает ошибку. А где они все определены кстати и как?

    0
    • Здравствуйте!
      Попробуйте включить , который определяет макросы true, false и bool. Тогда можно работать с bool, как обычно.

      0

  4. В статье говорится, что char — Целочисленный знаковый тип. Это в корне неверно. Стандарт языка определяет, что знаковость char зависит от реализации:

    The three types char, signed char, and unsigned char are collectively called the character types.
    The implementation shall define char to have the same range, representation, and behavior as either signed char or unsigned char.

    Один из моментов, на которых массово сыпятся у меня на собеседованиях «знатоки» языка Сю

    0
    • Приветствую всех! Язык Си изучаю недавно. Однако словосочетание «Целочисленный знаковый тип» сразу бросается в глаза, режет слух. Насколько мне известно, char в разных языках — это символьный тип данных, переменная такого типа может принимать значения из таблицы ASCII. Соответственно целое число и символ (знак) не должны сочетаться в одном типе, они должны быть разными типами, для них должно использоваться свое обозначение. Видимо у разработчиков стандарта языка своя особенная логика. Сравниваю разные языки, многое повторяется, но вот зачем так усложнять, если уже это было придумано в другом языке, например, менять название типов данных и т.п.

      0
  5. Автор забыл указать, что GCC для ядер Cortex M может запросто заоптимизировать в выходном коде тип unsigned long long (64-битное беззнаковое) на float (причём мать его даже не double!) со всеми вытекающими типа потери точности и т п.

    0
    • реально? Это в каких случаях произойти может? Даже не представляю, как такой ужас отлаживать

      0
  6. Приветствую всех! Чем отличаются типы данных double и long double, int int32_t и long int int32_t, unsigned int
    uint32_t и unsigned long int uint32_t? Судя по таблице ничем.

    0

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Нажимая кнопку "Отправить" Вы даёте свое согласие на обработку введенной персональной информации в соответствии с Федеральным Законом №152-ФЗ от 27.07.2006 "О персональных данных".