В предыдущих статьях[1, 2] по Quectel M66 OpenCPU было рассказано об основных особенностях системы OpenCPU и ее архитектуре. Настало время показать на практике эту архитектуру и как вообще начать программировать свое приложение под OpenCPU. По традиции будем решать задачу “Hello World!” для МК в виде мигания светодиодом.
Что нам понадобится для начала?
1. Отладочная плата M10-EVBKit, описание которого находим здесь, с модулем M66‐TE‐A и прошивкой M66FAR01A01;
2. Beta-версия прошивки M66 OpenCPU от Quectel: M66FAR01A02_BETA1008;
3. SDK-пакет от Quectel: M66_OpenCPU_GS3_SDK_V1.1 для среды разработки Eclipse;
4. GCC-компилятор для ARM: arm-none-eabi-gcc, сразу устанавливаем на диск С;
5. Сама среда разработки Eclipse: eclipse-cpp-kepler-SR2-win32.
Все архивы можно будет взять по ссылке в конце статьи.
Итак, заливаем в наш M66-модуль прошивку OpenCPU_Beta:
— Подключаем COM-порт (USB->COM-преобразователь) к разъему Main платы;
— Открываем программу QFlash_V3.0 и, нажав на кнопку “Load FW Files”, указываем файл нашей прошивки M66FAR01A02_BETA1008:
— Выбираем Com Port, соответствующий подключенному USB-COM-преобразователю. Скорость ставим 460800;
— Жмем “Start” и включаем на плате последовательно: “Тумблер D/L” -> “Тумблер Power” -> Зажимаем кнопку “Pwrkey” на 1сек;
— Начинается процесс прошивки:
— При успешном окончании прошивки появляется надпись PASS, FW upgrade success:
В принципе уже можно начать работать с модулем в режиме отправки AT-команд, однако наша цель все-таки OpenCPU, потому продолжим.
Теперь, скопируем из архива eclipse-cpp-kepler-SR2-win32.zip содержимое в корень жесткого диска (для удобства). Это заранее настроенный C/C++ Eclipse.
Также копируем из архива M66_OpenCPU_GS3_SDK_V1.1_Eclipse.rar в корень жесткого диска настроенный под Eclipse SDK.
Открываем eclipse.exe из каталога со средой eclipse-cpp-kepler. Нажимаем File -> Import -> Existing Project into Workspace:
Жмем Next и выбираем путь к нашему SDK, отмечаем галочкой найденный проект и жмем Finish:
Осталось перевести наш проект в режим Release: Project -> Build Configurations -> Set Active -> Release. И настроим проект: Project -> Properties:
В Environment -> GCC_PATH выбираем путь с установленным компилятором GCC.
Также во вкладке Settings опционально можно настроить проект и компилятор:
Теперь уже можно скомпилировать проект с заранее отлаженным файлом main: Project -> Clean…; Project -> Build Project. В окне Console можно посмотреть результат компиляции:
А в окне Problems увидим все предупреждения и ошибки, которые нужно(или можно) исправить:
Теперь, перед загрузкой прошивки в проект нужно после компиляции сделать: Project -> Make Target -> Build (Shft+F9), что добавит дополнительную информацию в бинарник прошивки, который находится в каталоге Release проекта.
Загрузка прошивки в модуль производится аналогично вышеописанному способу, только в Load FW Files выбираем .bin файл из каталога Release проекта.
Теперь мы можем проверить на деле OpenCpu. Возьмем файл примера example_gpio.c из каталога example проекта. Открываем этот файл и копируем его содержимое полностью и вставляем в пустой файл main. Удаляем в начале файла: #ifdef __EXAMPLE_GPIO__ и в конце файла: #endif //__EXAMPLE_GPIO__.
Компилируем проект и получаем некоторые предупреждения (Warnings), однако уже можно загружать прошивку в модуль и проверять работу.
Немного о структуре данного примера
Основной функцией любого проекта OpenCPU является void proc_main_task(s32 taskId)
, также как и функция main() при программировании МК:
/************************************************************************/ /* The entrance for this example application */ /************************************************************************/ void proc_main_task(s32 taskId) { ST_MSG msg; Ql_Debug_Trace("\r\n<-- OpenCPU: GPIO Example -->\r\n"); // Start to program GPIO pin GPIO_Program(); // Start message loop of this task while (TRUE) { Ql_OS_GetMessage(&msg); switch(msg.message) { case MSG_ID_USER_START: break; default: break; } } }
В начале функции идет строка Ql_Debug_Trace("\r\n< — OpenCPU: GPIO Example -->\r\n");
, которая выводит в порт Debug платы некоторую информацию о примере. Эта функция аналогична printf(“\r\nString\r\n”);
в привычном виде. Описание данной функции можно прочитать, зажав CTRL и щелкнув по функции:
Таким же образом можно получать информацию обо всех функциях SDK.
Далее по листингу идет инициализация порта ввода-вывода для мигания светодиодом Netlight:
static void GPIO_Program(void) { // Specify a GPIO pin Enum_PinName gpioPin = PINNAME_NETLIGHT; // Define the initial level for GPIO pin Enum_PinLevel gpioLvl = PINLEVEL_HIGH; // Initialize the GPIO pin (output high level, pull up) Ql_GPIO_Init(gpioPin, PINDIRECTION_OUT, gpioLvl, PINPULLSEL_PULLUP); Ql_Debug_Trace("<-- Initialize GPIO pin (PINNAME_STATUS): output, high level, pull up -->\r\n"); // Get the direction of GPIO Ql_Debug_Trace("<-- Get the GPIO direction: %d -->\r\n", Ql_GPIO_GetDirection(gpioPin)); // Get the level value of GPIO Ql_Debug_Trace("<-- Get the GPIO level value: %d -->\r\n\r\n", Ql_GPIO_GetLevel(gpioPin)); // Set the GPIO level to low after 500ms. Ql_Debug_Trace("<-- Set the GPIO level to low after 500ms -->\r\n"); Ql_Sleep(500); Ql_GPIO_SetLevel(gpioPin, PINLEVEL_LOW); Ql_Debug_Trace("<-- Get the GPIO level value: %d -->\r\n\r\n", Ql_GPIO_GetLevel(gpioPin)); // Set the GPIO level to high after 500ms. Ql_Debug_Trace("<-- Set the GPIO level to high after 500ms -->\r\n"); Ql_Sleep(500); Ql_GPIO_SetLevel(gpioPin, PINLEVEL_HIGH); Ql_Debug_Trace("<-- Get the GPIO level value: %d -->\r\n", Ql_GPIO_GetLevel(gpioPin)); }
Здесь выполняется инициализация
gpioPin = PINNAME_NETLIGHT;
настройка на выход, в высокое состояние с подтяжкой к питанию:
Ql_GPIO_Init(gpioPin, PINDIRECTION_OUT, gpioLvl, PINPULLSEL_PULLUP);
Далее проверяется состояние выхода:
Ql_GPIO_GetLevel(gpioPin));
Переводится в низкое состояние на 500 мсек:
Ql_GPIO_SetLevel(gpioPin, PINLEVEL_LOW);
И переводится в высокое состояние:
Ql_GPIO_SetLevel(gpioPin, PINLEVEL_HIGH);
Все комментарии по операциям выведутся в Debug-порт.
Для проверки выполнения команды рекомендуется использовать следующую конструкцию при инициализации и выполнении других функций:
s32 ret; // Initialize GPIO ret=Ql_GPIO_Init(m_gpioPin,PINDIRECTION_OUT, PINLEVEL_LOW, PINPULLSEL_PULLUP); if(QL_RET_OK==ret) { Ql_Debug_Trace("<--Initialize GPIO successfully -->\r\n"); }else{ Ql_Debug_Trace("<--Fail to initialize GPIO pin, cause=%d -->\r\n",ret); }
Т.е. можно через Debug-порт посмотреть результат выполнения функции.
Важной частью программы OpenCPU является выполнение функции Ql_OS_GetMessage(&msg);, которая получает сообщения из очереди сообщений текущей задачи, где &msg – указатель на сообщение. Пример получения сообщений от системы:
// START MESSAGE LOOP OF THIS TASK while(TRUE) { Ql_OS_GetMessage(&msg); switch(msg.message) { case MSG_ID_RIL_READY: Ql_Debug_Trace("<-- RIL is ready -->\r\n"); Ql_RIL_Initialize(); break; case MSG_ID_URC_INDICATION: //Ql_Debug_Trace("<-- Received URC: type: %d, -->\r\n", msg.param1); switch (msg.param1) { case URC_SIM_CARD_STATE_IND: SIM_Card_State_Ind(msg.param2); break; default: break; } } }
Здесь получаем сообщения о готовности RIL-слоя: MSG_ID_RIL_READY
Также получаем сообщения URC: MSG_ID_URC_INDICATION, где URC_SIM_CARD_STATE_IND – сообщение о состоянии SIM-карты.
Вышеописанный пример позволит начать программировать OpenCPU M66 и проверять написанный код «в железе». Основную информацию по OpenCPU нужно читать в файле Quectel_OpenCPU_User_Guide_V6.0_Preliminary.pdf
Скачать необходимые файлы можно здесь.
Спасибо, что дочитали до конца. Подписывайтесь на нашу группу в ВК и заходите общаться в наш чат.
M66_OpenCPU_GS3_SDK_V2.0_Eclipse вместо M66_OpenCPU_GS3_SDK_V1.1 для среды разработки Eclipse подойдёт?
Всем здравствовать! — ищу единомышлинников
Привет, добро пожаловать https://t.me/radiotechkz