Здесь собраны основные ошибки, которые возникают при работе с микроконтроллерами "Миландр". Если возникают проблемы с работоспособностью проекта, попробуйте бегло просмотреть представленные пункты. Возможно какие-то ответы помогут в решении задачи.
Пример инициализация порта:
// 1 - ВКЛЮЧАЕМ ТАКТИРОВАНИЕ ПОРТА RST_CLK_PCLKcmd (RST_CLK_PCLK_PORTC, ENABLE); // 2 - ИНИЦИАЛИЗИРУЕМ ПОРТ В ЗАДАННОЙ КОНФИГУРАЦИИ PORT_StructInit(&GPIOInitStruct); GPIOInitStruct.PORT_Pin = PORT_Pin_0; GPIOInitStruct.PORT_OE = PORT_OE_OUT; GPIOInitStruct.PORT_SPEED = PORT_SPEED_SLOW; GPIOInitStruct.PORT_MODE = PORT_MODE_DIGITAL; PORT_Init(MDR_PORTC, &GPIOInitStruct);
Очень часто возникают ошибки связанные с тем, что настраивается какая-то периферия, а тактирование на нее не задано. Либо может быть так, что тактирование сбрасывается в каком-то из подключенных файлов. Необходимо быть внимательнее в данном вопросе.
Это тот случай, когда тактирование в программе задано после настройки периферии. На примере настройки портов, вот что здесь происходит:
В итоге порты не инициализированы, поскольку тактирование не было включено на момент конфигурирования. Далее,
Теперь порты полностью рабочие, поскольку инициализация происходила при включенном тактировании.
Например, простейшая операция i++ происходит в несколько этапов:
В любой момент между этими шагами может возникнуть прерывание, которое может так же модифицировать ячейку памяти, занятую переменной i. При выходе из прерывания это новое значение будет затерто, и логика программы может быть нарушена.
В ядре Cortex существуют операции для защищенного обращения к памяти LDREX и STREX. Информацию по использованию можно найти в интернете. Например, на сайте Keil.
Вторым вариантом может быть использование bit-band обращения к памяти. Запись и стирание флагов в ячейках происходит атомарными операциями.
При работе с Flash памятью процессор выполняет функции, расположенные в ОЗУ. Нельзя исполнять команды из Flash и в тоже время туда писать. При такой попытке процессор свалится в Hard Fault. Поэтому важно, чтобы при работе с Flash памятью не возникало никаких прерываний. Поскольку обработчики прерываний по умолчанию расположены в памяти Flash, то переход исполнения из ОЗУ в итоге вызовет выход процессора в Hard Fault.
Выключить прерывания от NVIC функцией NVIC_DisableIRQ() в данной ситуации бывает недостаточно.
Сделать это можно таким образом:
// Выключение системного таймера SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk; или // Выключение всех прерываний с изменяемым приоритетом. Равносильно __set_PRIMASK(1); // Но NMI и HardFault остаются активны! __disable_irq();
После работы с EEPROM необходимо вернуть разрешение прерываний.
Как расположить код в ОЗУ рассказано здесь - Расположение функций в ОЗУ, программирование EEPROM.
Иногда возникает необходимость использовать выводы, совмещенные с JTAG. Например в 1986ВЕ92У настраивается работа с выводами PD0 и PD1, которые совмещены с выводами Jtag_B. Но переключить состояние этих ножек не удается никакими командами SPL.
Дело в том, что функции SPL работы с портами, например PORT_Init(), проверяют выводы на принадлежность к JTAG_B и не дают их переназначать. Такое поведение задается по умолчанию. Чтобы отключить данную проверку, необходимо в файле MDR32F9Qx_config.h отключить макро-определение USE_JTAG_B. Строка 80.
#if (defined(USE_MDR1986VE9x) || defined (USE_MDR1901VC1T)) /* #define USE_JTAG_A */ #define USE_JTAG_B - То, что нужно закомментировать! #endif
Библиотечный файл MDR32F9Qx_config.h защищен от записи, поэтому необходимо предварительно в проводнике снять с файла атрибут Read-Only (Правая клавиша мыши - Свойства - Только чтение). После этого данными выводами можно управлять как всеми прочими выводами GPIO.
Для загрузки программы придется использовать второй Jtag, например JTAG_A в данном примере, или UART.
Этот цикл даст некоторую задержку, перед переопределением выводов (сменой частоты и прочих операций, которые могут "убить" работоспособность МК). В это время подключенный Jtag успеет перехватить управление и остановить исполнение программы. Таким образом сохраняется возможность стереть программу, зашить новую либо войти в режим отладки - даже если выводы Jtag в программе используются по другому назначению.
При записи в регистр PORT→RXTX необходимо сбрасывать биты используемые в отладочных интерфейсах. Если этого не сделать, то работа данных интерфейсов будет нарушена, а отладка невозможна!
Для примера можно посмотреть как это делается в функциях PORT_SetBits() или PORT_ResetBits() библиотеки SPL.
Для смены тактовой частоты на более высокую требуется совершить следующие операции:
При переходе на более низкую частоту, изменение EEPROM_Delay и SelectRI, LOW производят после смены частоты.
Параметр задержки обращения к памяти программ - EEPROM_Delay можно заранее настроить на наибольшее значение, тогда перестройка не требуется.
Таким образом, важно запомнить, что
// Инициализация системы тактирования микроконтроллера void CPU_Initialize (void) { // Сброс настроек системы тактирования RST_CLK_DeInit(); // Инициализация генератора на внешнем кварцевом резонаторе (HSE) RST_CLK_HSEconfig (RST_CLK_HSE_ON); while (RST_CLK_HSEstatus() != SUCCESS); // Инициализация блока PLL // Включение использования PLL RST_CLK_CPU_PLLcmd (ENABLE); // Настройка источника и коэффициента умножения PLL // (CPU_C1_SEL = HSE) RST_CLK_CPU_PLLconfig (RST_CLK_CPU_PLLsrcHSEdiv1, RST_CLK_CPU_PLLmul10); while (RST_CLK_CPU_PLLstatus() != SUCCESS); // Подключение PLL к системе тактирования // (CPU_C2_SEL = PLLCPUo) RST_CLK_CPU_PLLuse (ENABLE); // Настройка коэффициента деления блока CPU_C3_SEL // (CPU_C3_SEL = CPU_C2) RST_CLK_CPUclkPrescaler (RST_CLK_CPUclkDIV1); // Использование процессором сигнала CPU_C3 // (HCLK = CPU_C3) RST_CLK_CPUclkSelection (RST_CLK_CPUclkCPU_C3); }
Это особенность компилятора Keil заменяющего не реализованный функционал printf на инструкцию программной остановки BKPT. Форум
Иногда требуется перейти на функцию расположенную по известному адресу в памяти. Если в коде это выражают так, то происходит вылет в HardFault:
// Адрес функции в памяти #define BASE_ADDR_FUNC_IN_RAM 0x20005000 // Указатель на функцию в памяти по известному адресу typedef void (*funcptr)(); funcptr funcInRAM = (funcptr) (BASE_ADDR_FUNC_IN_RAM); // Вызов функции funcInRAM();
// Аналог в ассемблере LDR R0,=(0x20005000) BX R0
Это происходит потому, что адрес перехода должен быть нечетным, чтобы ядро понимало что переход происходит на код в инструкциях THUMB, а не ARM! (ARM Info Center)
На самом деле при переходе происходит исключение USAGE_Fault, но обработчик данного исключения по умолчанию выключен, поэтому выход происходит в HardFault. Включение некоторых обработчиков исключений можно сделать так:
#define SCB_SHCSR_USGFAULTENA (1 << 18) #define SCB_SHCSR_BUSFAULTENA (1 << 17) #define SCB_SHCSR_MEMFAULTENA (1 << 16) SCB->SHCSR |= SCB_SHCSR_BUSFAULTENA; SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA; SCB->SHCSR |= SCB_SHCSR_USGFAULTENA;
Итого, чтобы переход произошел правильно, код необходимо модифицировать так:
.... // Указатель на функцию в памяти по известному адресу typedef void (*funcptr)(); funcptr funcInRAM = (funcptr) (BASE_ADDR_FUNC_IN_RAM + 1); .... // Аналог в ассемблере LDR R0,=(0x20005001) BX R0