СОДЕРЖАНИЕ
1. Задание на разрабатываемое устройство
1.1 Требования к охранным, пожарным шлейфам
1.3 Требования к конфигурированию и настройке устройства
2. Электронные компоненты печатной платы
.1 Технические характеристики и параметры основных компонентов печатной платы
.2 Применяемые схемотехнические решения
3. Программное обеспечение микроконтроллера
.2 Задачи операционной системы реального времени
3.3 Взаимодействие задач и прерываний
.4 Функции обработчиков прерываний
.6 Передача параметров настройки по USB
4. Программное обеспечение Windows Form C#
.1 Определение общей структуры приложения
4.2 Создание библиотеки из ссылочных типов содержащие поля данных
Проект содержит группу файлов распределенных по назначению Рис 4.1.1 Каждая группа хранится в отдельной папке.
Основные файлы проекта хранятся в папках
STM32F4xx_StdPeriphDriver - хранятся основные функции и настройки управления
для: _adc. c - аналогово-цифрового преобразования, _exit - внешние прерывания
на выводах, _dma-управлением непрямого доступа к памяти минуя процессор
(используем для копирования значений замеров по шлейфам в буфер), _usart -
периферия для асинхронной передачи данных используя интерфейс RS232
(используется два интерфейса один для управления GSM модулем Sim900, второй для
вывода отладочной информации в окно терминала), _rcc-настройка тактовых частот
периферийных шин, _tim-настройка и работа с таймером 1-wire устройств.
Рис 4.1.1 Структура проекта МК
Системные настройки находятся в папке STM32F4xx, это startup_stm32f4xx. s - ассемблерный файл содержит определение таблицы векторов прерываний, определение размера стека (для сохранения контекста при вызове функций и в нем же сохраняются значения СФР специальных функциональных регистров R0-R3,LR,PSR,PC) и кучи (для работы с памятью используя memset, memcpy, malloc, для нужд ОСРВ выделяется отдельная куча из пространства RAM памяти) из этого файла вызывается вначале основная функция инициализации ядра void SystemInit (void) которая находится в файле system_stm32f4xx. c. Затем управление передается в main ().
Для конфигурации USB содержится несколько папок. В нашем проекте используется конфигурация USB OTG Device Virtual Com Port (OTG-on-the-go подключение на ходу). Virtual COM Port - это отдельный подкласс USB устройств когда обмен выполняется по интерфейсу RS232, в этом случае STMElectronics поставляет уже готовый драйвер (VCP_V1.3.1.) под Windows 7 для работы с виртуальным портом. Это упрощает разработку устройства, так как в случае использования полноценного HID (Human interface devices) устройства необходимо описывать дескриптор устройств, интерфейса, конфигурации, конечных точек. Для непосредственной работы с данными служит файл usbd_cdc_vcp. c в папке App, этот файл согласно документации [4] - пользовательский уровень для интерфейса USB, папка USB Driver содержит файлы нижнего уровня где происходит взаимодействие рабочих регистров с функциями нижнего уровня. Взаимодействие пользовательских функций и обработка прерываний с драйверами нижнего уровня выполняется с помощью функций среднего уровня в папке USB Device. В папке USB Device в файле usbd_cdc_core. c содержутся основные дескрипторы устройства. Максимальная скорость передачи информации по USB в нашем случае состовляет 64Кб\сек, а само устройство определяется как FS (full speed) 12Мбит\с.
Папка RTOS содержит функции для работы ОСРВ. Файлы входящие в эту структуру: heap_4. c - содержит механизм выделения памяти для нужд FreeRTOS, tasks. c,queue. c, croutine. c - содержат реализацию механизма задач, реализация очередей и сопрограмм.
Папка User - содержит пользовательские файлы для работы с Sim900, 1-Wire устройствами и АЦП. Основной файл приложения, где начинается выполнение пользовательских задач - app. c.
Сигнализация GSM должна быть мультизадачной - поочередно выполнять поставленные задачи в порядке их приоритета и важности. В нашем проекте определим следующие задачи: задача обработки измерений напряжений шлейфов сигнализации (в ПО конфигурации шлейфы обозначены как канал1-канал16), задача определения состояния входов и в соответствии с активированным входом она должна активировать необходимый канал, задача отправки SMS сообщения, задача инициализации USB - после выполнения инициализации она удаляет сама себя из планировщика, задачи попеременного мигания светодиодов (в них также выполняется инициализация GSM модуля SIM900 если она проходит неуспешно, может повторяться несколько раз, и выполняется проверка необходимости срабатывания выхода), задачи считывания температуры и номера с устройств 1-wire:
(vTaskLED1, (signed char *)
"LED1”, configMINIMAL_STACK_SIZE, NULL, 2,&xTaskXandleSvet);(vTaskLED2, (signed char *)
"LED2”, configMINIMAL_STACK_SIZE, NULL, 1, (xTaskHandle *) NULL);(vInitUSB, (signed char *)
"InitUSB”, configMINIMAL_STACK_SIZE, NULL, 3
(xTaskHandle *) NULL);(vInitOwire, (signed char *)
"InitOwire”, configMINIMAL_STACK_SIZE, NULL, 1, (xTaskHandle *) NULL);(vOwireNomer, (signed char *)
"OwireNomer”, configMINIMAL_STACK_SIZE, NULL, 2, (xTaskHandle *) NULL);(vObrabUstADC, (signed char *)
"ObrabUstavok”, configMINIMAL_STACK_SIZE, NULL, 1, (xTaskHandle *) NULL);(vObrVhodov, (signed char *)
"vObrabVhodov”, configMINIMAL_STACK_SIZE, NULL, 1, (xTaskHandle *) NULL);(vOtpravSMS, (signed char *)
"OtpravSms”, configMAXIMAL_STACK_SIZE, NULL, 2,&OtpSoobSim);(); - запускаем планировщик
позволяет достаточно гибко управлять приоритетами выполнения задач, позволяет приостановить или выгрузить задачу из очереди планировщика или изменить ее приоритет в ходе ее выполнения, или из другой задачи используя ее хэндл и содержит дополнительные функции таймеры, очереди, семафоры. Хэндл или описание - каждой задаче задается свой номер по которому ОСРВ распознает текущую выполняемую задачу, этот номер и есть хэндл задачи, например для задачи отправки SMS хэндлом является OtpSoobSim-указав его из другой задачи мы можем изменить ее приоритет или приостановить выполнение. ОСРВ поддерживает три типа многозадачности: вытесняющую, кооперативную, гибридную. Все основные настройки ОСРВ находятся в файле FreeRTOSconfig. h:
#defineconfigUSE_PREEMPTION 1-установили вытесняющую многозадачность, каждая задача выполняется в течении одного кванта времени планировщика, в проекте этот квант #define configTICK_RATE_HZ ( (portTickType) 1000) равен 1милисекунду. Для каждой созданной нами задаче выделяется стек размер которого #define configMINIMAL_STACK_SIZE ( (unsigned short) 100) равен 200 байт. Для работы ОСРВ выделяется область памяти, под все задачи и механизмы, в куче равной configTOTAL_HEAP_SIZE ( (size_t) (60 * 1024) 60 Кбайт. Максимальное число приоритетов задач равно 5.
После создания наших задач планировщик отдаст управление,
задаче с самым большим приоритетом - это задача инициализации USB "InitUSB"
с приоритетом 3 (после выполнения данная задача удаляется из планировщика и
освобождает стек ОСРВ). После этого он отдаст управление задачам
"LED1" - где выполняется инициализация GSM модуля в течении 13 секунд
vTaskDelay (13000) и пока эта задача временно блокирована планировщик отдаст
управление другой задаче с приоритетом 2 - задаче отправки SMS - OtpravSms но
данная задача ждет данные в очереди, а поскольку данных в очереди нету то
xStatus=xQueueReceive (xQueueSoob,&ReciveData,0) выполнение этой задачи
планировщиком приостанавливается(NULL); - а вот возобновить теперь выполнение
этой задачи может только задача сравнения значений АЦП она же отправляет в
очередь номер телефона на который необходимо отправить сообщение. Как только
планировщик отсчитает 13 сек с момента блокирования "LED1" она
разблокируется и управление снова передается ей, если инициализация прошла
успешно, в ее теле понижается ее же приоритет до 1 и происходит попеременной
мигание светодиодов - говорит об успешной инициализации. В нашем случае каждой
задаче планировщик уделяет каждой задачи 1мсек, через 1 мсек происходит
прерывание системного таймера SysTick при этом данные задачи сохраняются в
стеке задачи. Это прерывание запускает планировщик который оценивает есть ли
готовые, неблокированные к выполнению задачи и в порядке приоритета отдает
управление той, у которой приоритет максимальный. В данном случае если
высокоприоритетная задача не имеет временной блокировки или приостановки,
задачи с меньшим приоритетом никогда не получат управления.
Рассмотрим как обрабатываются прерывания без ОСРВ. Каждый тип прерывания микроконтроллера имеет свой приоритет. Его можно установить в СФР где для задания приоритета выделено 4 бита - это соответствует уровням приоритета от 0 до 15. Однако данные биты различными комбинациями могут быть разделены на подгруппы (например 2 бита на приоритет группы и 2 на приоритет подгруппы).
В нашем проекте выполним настройку при которой все 4 бита - соответствуют уровням приоритета, без деления на подгруппы:_PriorityGroupConfig (NVIC_PriorityGroup_4);
Правила обработки прерывания без деления на подгруппы:
вызов прерывания с более высоким приоритетом приостанавливает выполнение текущего обработчика;
- выполнение функции обработки прерывания не может быть прервано вызовом с тем же или более низким уровнем приоритета;
при одновременном вызове несколько прерываний будут обработаны в порядке приоритета, при одинаковом приоритете по возрастанию номера прерывания (порядковые номера задаются в файле STM32F4xx. h);
В нашем проекте задаем следующие приоритеты прерываний (0-означает максимально возможный приоритет):
) прерывание получения данных от модуля Sim900 по USART_2 (файл Sim900. c)_InitStructure. NVIC_IRQChannelPreemptionPriority = 0;
) прерывание таймера TIM3 запуска преобразований АЦП (по истечении установленной выдержки времени происходит прерывание, в программе прерывания выполняется запуск преобразования, установка приоритета в файле ADC. c)_InitStructure. NVIC_IRQChannelPreemptionPriority = 4;
) прерывание при получении данных по USB (файл usb_bsp. c)_InitStructure. NVIC_IRQChannelPreemptionPriority = 0;
) прерывание по нажатию кнопок, и управлению с брелока (файл app. c)_InitStructure. NVIC_IRQChannelPreemptionPriority = 4;
Самый низкий приоритет у прерываний TIM3 и прерываний по нажатию кнопок, и эти прерывания имеют одинаковый приоритет, чтобы они не приостанавливали друг друга. А самые высокие приоритеты имеют обработчики прерываний по USB - при программировании конфигурации устройства и получение сообщения по Sim900. Такие настройки выбраны потому что самыми приоритетными задачами является конфигурирование устройства по USB, при этом передача информации между ПК и МК не должно прерываться низкоприоритетными прерываниями, и получение сообщений по GSM связи которое будет выполнять управление сигнализацией.
Приоритет задач не имеет никакого отношения к приоритету прерываний. Задачи не выполняются во время выполнения обработчиков прерываний. Ядро FreeRTOS позволяет с помощью макросредства config MAX_SYSCALL_INTERRUPT_PRIORITY задать наибольший приоритет прерывания, из обработчика которого можно вызывать API функции FreeRtos. Что-бы защитить выполнение текущего кода задачи от прерываний используются критические секции - это участки кода, во время выполнения которого запрещены прерывания процессора, и не происходит прерывание контекста каждый квант времени, а также запрещено прерывание системного таймера SysTick.
Определим в нашем проекте
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 3
т.е. на все прерывания с приоритетом до 3 (это прерывание USB и прерывание получения информации модулем Sim900) не влияют критические секции, так что ничего что делает ядро в текущий момент, не помешает выполнению текущего обработчика. Например в задаче "LED1” выполняется инициализация Sim900 следующим образом:
vTaskLED1 (void *pvParameters)
{_CRITICAL (); // вхожу в критическую секцию_Sim900 (2,4,0); // передаем две команды AT_Sim900 (0,1,0); // передаем команду отключение эха_Sim900 (8,13,0); // включаем текстовый режим_CRITICAL (); // выходим из критической секции
……….
}
В вышеприведенном примере при входе в критическую секцию - когда выполняется инициализация SIM900 запрещаем прерывания по TIM3 (запуску АЦП преобразования), и прерывания брелока. Также критические секции используются при отправке SMS сообщения (задача "OtpravSms") - в этот момент выполняется последовательность из нескольких команд прерывать выполнение которых нельзя.
Рассмотрим некоторые фрагменты кода для нескольких функций обработчиков прерываний (файл stm32fxxx_it. c):
) Прерывание по таймеру выполняются каждые 2,5 милисекунды, в обработчике прерывания выполняется анализ положения ключей мультиплексора каналов сигнализации, и в зависимости от текущего положения переключаются мультиплексоры на следующие входы. После того как завершится каждое преобразование АЦП, DMA сохраняет значения напряжений по каналам в глобальном массиве (объявленном в app. c): __IO uint16_t ADC3ConvertedValue [16]. Таким образом полный цикл замеров по всем каналам завершится после четырех преобразований 4*2.5=10 мсек.
TIM3_IRQHandler (void)
{
………(DMA_GetCurrDataCounter (DMA2_Stream0) ==0x10) // массив пуст
{>BSRRH= GPIO_BSRR_BS_7; // переключаем мультиплексор>BSRRH= GPIO_BSRR_BS_8;
// отдаем семафор задаче обработки результатов замеров(xSemPrerADC,NULL)
}
// если массив заполнен на ¼if (DMA_GetCurrDataCounter (DMA2_Stream0) ==0xC)
{>BSRRL= GPIO_BSRR_BS_7; // переключаем мультиплексор>BSRRH= GPIO_BSRR_BS_8;
} ……
// обновляем значение счетного регистра таймера= TIM_GetCapture1 (TIM3);_SetCompare1 (TIM3, capture + CCR1_Val);
// запускаем следующее преобразование АЦП_SoftwareStartConv (ADC1);
/*передаем управление не планировщику, а если есть на очереди прерывание
то в следующее прерывание, не допуская лишнее переключение
контекста*/_FROM_ISR (1);
}
) Прерывание по нажатию кнопок и брелока. Мультиплексоры как аналоговых сигналов, так и дискретных управляются от одних и тех же выводов МК. По Рис 3.2.3 прерывания на брелоке настроены на появление логической единицы (точнее появление 5В через нажатую кнопку на брелоке) на выводе RF, прерывания кнопок настроены на появление логического нуля на выводе key (на key включена подтяжка к питанию 3.3 Вольт). Таким образом, при возникновении прерывания, номер нажатой кнопки соответствует положению мультиплексорного ключа в данный момент времени. Зная продолжительность кратковременного нажатия кнопки 105 мсек и продолжительность каждого переключения мультиплексора 2.5 мсек, для четырех кнопок будем выполнять счет нажатий и сравнивать его со значением 105/4*2.5=10 равным десяти. Определение что кнопка нажата будем выполнять в задаче "vObrabVhodov" где для каждой кнопки сравниваем значение счета со значением 10 и если chetEXTIA [номер кнопки] >=10 значит кнопка нажата. Но перед сравнением значений мы будем блокировать выполнение этой задачи каждые vTaskDelay (105) - 105мсек.