Немного очевидных вещей, которые не так часто используются в AVR микроконтроллерах, зато достаточно популярны при написании кода для STM32. В частности для настройки периферии посредством библиотеки standart peripheral library (SPL)
Рассмотрим следующий кусок кода:
typedef enum{low=9600, mid=19200, high=57600}speed; speed SPEED; SPEED = low; printf("%d\n", SPEED); |
Пофантазируем. Представьте себе ситуацию: вы сделали некое устройство, пускай это будет абстрактный датчик температуры, который передает данные по uart. Теперь задача написать библиотеку, которая бы предоставляла дальнейшим пользователям простой интерфейс, т.е. ее можно было бы прицепить к любому проекту. Допустим датчик может корректно работать только на трех скоростях uart 9600, 19200 и 57600.
Можно пытаться объяснить этот момент в документации, но кто будет ее читать? 🙂 Поэтому гораздо правильнее написать такой код, чтобы пользователь не мог ошибиться. В этом очень здорово могут помочь перечисления. Вернемся к нашему куску кода. Есть некое перечисление, о чем нам говорит ключевое слово enum, название которого speed. Назвать перечисление мы вольны как угодно.
typedef enum{low=9600, mid=19200, high=57600}speed;
Далее по коду мы выделяем память
speed SPEED;
Теперь у нас есть переменная SPEED, которая может принимать только 3 значения low, mid, high. Соответственно когда мы присваиваем SPEED = low; то переменная SPEED становится равной 9600. Если мы попытаемся записать в SPEED все что угодно, кроме low, mid, high, то проект не скомпилится и вылезет ошибка. Таким образом, у того кто будет использовать вашу библиотеку шанс ошибиться намного меньше.
Пример из реальной жизни. Все кто использовал SPL для создания проектов для STM32 совершенно точно видел подобные куски кода
GPIO_InitTypeDef PA10_setup; PA10_setup.GPIO_Mode = GPIO_Mode_Out_PP; PA10_setup.GPIO_Pin = GPIO_Pin_10; PA10_setup.GPIO_Speed = GPIO_Speed_2MHz; GPIO_Init(GPIOA, &PA10_setup); |
Выколите мне глаза, кричали они 😀 Но на деле, если взглянуть в инклюды, то все просто например PA10_setup.GPIO_Speed это всего навсего перечисление, выглядит оно так:
typedef enum { GPIO_Speed_10MHz, GPIO_Speed_2MHz, GPIO_Speed_50MHz }GPIOSpeed_TypeDef; |
Периодически народ спрашивает, откуда ты берешь все эти значения для инициализации? Да вот же они! Все возможные варианты. Достаточно понимать что ищешь. Тоже самое с остальными настройками.
Теперь перейдем к структурам. Знаете ли вы что такое массив? Это объединение нескольких одинаковых переменных под одним именем. Например, вы измерили 3 раза температуру за окном. Это удобно и понятно, все 3 температуры относятся к некому одному объекту и имеют одинаковый тип данных char.
char temp[3];
temp[0] = 25, temp[1] = 15, temp[2] = 10.
Но иногда может быть так, что нужно привязать к одному объекту, переменные разного типа данных char, int и т.п. Типичный пример, у человека есть рост, вес и цвет волос. Рост и вес типа char, а цвет волос кодируется 3 байтами (RGB). В этом случае удобно использовать структуры.
Лучше один раз показать, чем 10 раз объяснять. Пример есть есть две собаки шарик и бобик, описание которых выполнено с помощью всего одной структуры:
#include "stdafx.h" int _tmain(int argc, _TCHAR* argv[]) { struct DOG //структура DOG, с помощью которой можно составить описание собаки { char gender; //возраст int weight; //вес char *color; //цвет (указатель) }; struct DOG sharik; //выделяем память под шарика struct DOG bobik; //выделяем память под бобика sharik.gender = 2; //возраст шарика 2 года sharik.weight = 17; //вес 17 кг sharik.color = "black"; //цвет черный bobik.gender = 5; //возраст шарика 5 лет bobik.weight = 15; //вес 15 кг bobik.color = "white"; //цвет белый printf("Sharik:\n weight: %d, gender: %d, color: %s\n\r", sharik.weight, sharik.gender, sharik.color); printf("==============================================\n\r"); printf("Bobik:\n weight: %d, gender: %d, color: %s\n\r", bobik.weight, bobik.gender, bobik.color); printf("==============================================\n\r"); return 0; } |
Программа полна коментов, но все же поясним. Вначале создается структура с именем DOG, т.е. собака. Все собаки имеют общие признаки возраст, вес и цвет. Соответственно 3 переменных. Чтобы можно было отличить по программе шарика от бобика, создается переменная типа структуры, например sharik, т.е. выделяется память под переменные возраста, цвета и веса. Доступ к переменным осуществляется через точку.
Очевидно, что если делать подобную программу без структуры, она скорее всего содержала бы кучу переменных типа char sharik_gender, sharik_weight. Поэтому программа со структурами выглядит намного нагляднее и понятнее. Кроме того, существует несколько хитростей, благодаря которым можно сильно упростить разбор протоколов, но об этом в другой раз, когда сам хорошо овладею 🙂
Ну и еще один момент, про который промолчали выше. Для тех кого напрягает каждый раз писать struct DOG sharik, можно сделать синоним, для этого используется ключевое слово typedef
typedef struct //создаем синоним структуры под именем DOG { char gender; //возраст int weight; //вес char *color; //цвет } DOG; //теперь можно обращаться без ключевого имени struct DOG sharik; //выделяем память под шарика DOG bobik; //выделяем память под бобика |
Собственно вернемся к изначальной цели, откуда мне взять все эти настройки для периферии STM. Возьмем наш пример и GPIO, заходим в stm32f10x_gpio.h и находим ту самую структуру.
typedef struct { uint16_t GPIO_Pin; /*!< Specifies the GPIO pins to be configured. This parameter can be any value of @ref GPIO_pins_define */ GPIOSpeed_TypeDef GPIO_Speed; /*!< Specifies the speed for the selected pins. This parameter can be a value of @ref GPIOSpeed_TypeDef */ GPIOMode_TypeDef GPIO_Mode; /*!< Specifies the operating mode for the selected pins. This parameter can be a value of @ref GPIOMode_TypeDef */ }GPIO_InitTypeDef; |
Из структуры GPIO_InitTypeDef можно сразу догадаться, что GPIO_Pin — номер ножки, GPIO_Speed — скорость ножки, GPIO_Mode — тип режима ножки. В итоге GPIO_InitTypeDef позволяет полностью описать настройку ножки. Обратите внимание, что для разных типов мк она может немного отличаться.
Дальнейшие наши действия — прошерстить файл и найти описание элементов структуры.
Например для GPIO_Pin, можно найти кучу дефайнов с комментами, думаю тут даже нечего пояснять
#define GPIO_Pin_0 ((uint16_t)0x0001) /*!< Pin 0 selected */ #define GPIO_Pin_1 ((uint16_t)0x0002) /*!< Pin 1 selected */ #define GPIO_Pin_2 ((uint16_t)0x0004) /*!< Pin 2 selected */ #define GPIO_Pin_3 ((uint16_t)0x0008) /*!< Pin 3 selected */ #define GPIO_Pin_4 ((uint16_t)0x0010) /*!< Pin 4 selected */ #define GPIO_Pin_5 ((uint16_t)0x0020) /*!< Pin 5 selected */ #define GPIO_Pin_6 ((uint16_t)0x0040) /*!< Pin 6 selected */ #define GPIO_Pin_7 ((uint16_t)0x0080) /*!< Pin 7 selected */ #define GPIO_Pin_8 ((uint16_t)0x0100) /*!< Pin 8 selected */ #define GPIO_Pin_9 ((uint16_t)0x0200) /*!< Pin 9 selected */ #define GPIO_Pin_10 ((uint16_t)0x0400) /*!< Pin 10 selected */ #define GPIO_Pin_11 ((uint16_t)0x0800) /*!< Pin 11 selected */ #define GPIO_Pin_12 ((uint16_t)0x1000) /*!< Pin 12 selected */ #define GPIO_Pin_13 ((uint16_t)0x2000) /*!< Pin 13 selected */ #define GPIO_Pin_14 ((uint16_t)0x4000) /*!< Pin 14 selected */ #define GPIO_Pin_15 ((uint16_t)0x8000) /*!< Pin 15 selected */ #define GPIO_Pin_All ((uint16_t)0xFFFF) /*!< All pins selected */ |
GPIO_Mode, это уже перечисление, т.е. список того, каким может быть GPIO_Mode. Каким типом может быть
ножка полюбому лучше один раз прочитать в даташите, но по смыслу можно догадаться, что AIN это аналоговый вход, OUT_PP это выход пуш-пул, AF_PP альтернативная функция
выход пуш-пул
typedef enum { GPIO_Mode_AIN = 0x0, GPIO_Mode_IN_FLOATING = 0x04, GPIO_Mode_IPD = 0x28, GPIO_Mode_IPU = 0x48, GPIO_Mode_Out_OD = 0x14, GPIO_Mode_Out_PP = 0x10, GPIO_Mode_AF_OD = 0x1C, GPIO_Mode_AF_PP = 0x18 }GPIOMode_TypeDef; |
GPIO_Speed тоже перечисление, о чем уже упоминалось выше
typedef enum
{
GPIO_Speed_10MHz,
GPIO_Speed_2MHz,
GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;
В итоге после заполнения всех элементов структуру, мы передаем в функцию GPIO_Init указатель на структуру и говорим для какого порта эти настройки. Внутри этой функции, просто напросто соответствующие переменные из структуры, заносятся в регистры.
GPIO_Init(GPIOA, &PA10_setup);
Сложно ли это все? Пожалуй, когда первый раз читаешь, то да. Но вдумайтесь, можно ли сделать проще и при этом универсально? Довольно сложно придумать что то еще. Настоятельно рекомендую поковырять все эти вещи, в какой нить Visual C++ или чем то аналогичном.
Естественно, SPL не освобождает от чтения даташитов, но подход совершенно логичный и вполне себе понятный. Даже не имея под рукой даташит, можно родить очень много говнокода, который будет работать. Конечно размер кода инициализации могут быть просто огромным, но не так и сложно вынести его выносить в отдельный файл, ничего сверхестественного в этом нет. Надеюсь кому нибудь пригодится 🙂
Позновато с standart peripheral library. Ее ST уже не поддерживает, все переехали на STM32CubeMX + IAR. Даже если скачать новую версию кокоса, то там вообще нет поддержки STM32. К стати STM32CubeMX оч интересная штука.
старые камни все остаются на spl, если посмотреть внимательно то что генерит куб — то это обертка над spl 🙂 сам смысл это подход
Привет, пользовали кто 2 версию coocox? Что-то в ней и проект создается тормазнуто, все докачивать нужно, сервер не всегда доступен. Что за ерунда? Оставаться на 1.7?
С версией 2 аналогичная ситуация 🙂 1.7.6 работает норм
И да, Админу спасибо за такие статьи, сайт в закладках давно и ни капли не жалею) Учусь вместе с вами!
А что, форум deleted? Ссылку не могу найти.
здравствуйте подскажите ,тип сhar структуры с бит полями , есть ли возможность средствами языка си записать в эту структуру сразу байт.
Да, можно. Создаете указатель на структуру, и пишете по этому адресу
залил ваш код в квавр при компиляции получил вот:
Error:(158): undefined symbol ‘my_struct’
*str_pointer = *(struct my_struct *)&y;
к сожалению все так же осталось…так как компилятор не видит именно этой переменной с названием my_struct её и нету так как мы объявили переменную my_new_struct с типом структуры my_struct
Огромное вам спасибо действительно работает последним способом все скомпилировалось и абсолютно корректно работает..)))!
ещё раз вам большое спасибо..
у меня вопрос по структуре для меня это новое.Как меню создать на структурной основе?.На компиляции как это сказывается?
Если можете то в общих чертах.Может есть какаято литература это STM а на AVR
Была мысля написать про это, даже материал есть, но все как то лениво. В двух словах довольно сложно описать, структуры сами по себе расходуют больше памяти, но зато они более удобны, чтобы не запутаться. Если меню большое и древовидное, то тут выбор однозначный. Про сами структуры читайте в любой книжке по Си. Про древовидное меню статей в гугле достаточно.
Здравствуйте. Пытаюсь вкурить чужой код для Атмеги8 и впал в ступор. С первой вроде все ясно а со второй пол дня пытаюсь понять что она значит. Помогите разобраться пожалуйста.
struct Emp
{
char *Name; //строка
unsigned int TempADC;
unsigned int TempSet;
unsigned char status;
unsigned int Buzer;
};
struct Emp Item[4]={
{«Solder:»,0,240,0,240},
{«HotAir:»,0,300,0,300},
{«Amper: «,0,100,0},
{«Volt: «,0,100,0},
};
У меня вопрос.Почему я структуру пишу в другом файле .А элементы я могу прописать в main.c. А как прописать элементы выделив из структуры в этом же файле?