Как стать автором
Обновить
361.37
Рейтинг
Timeweb
VDS и инфраструктура. По коду Habr10 — 10% скидка

Маленькие хитрости для STM32

Блог компании TimewebПрограммированиеC++Промышленное программированиеПрограммирование микроконтроллеров
Tutorial

В процессе работы у каждого программиста иногда встречаются неожиданные проблемы, которые возникли как будто на ровном месте. Практически по Черномырдину «никогда такого не было, и вот опять!». После этого начинаешь искать решение в интернете или закапываешься в чтение мануалов и документации, чтобы разобраться в пустячном, на первый взгляд вопросе.

Через какое-то время решение находится (или ошибка исправляется), и ты даешь себе зарок запомнить этот случай, а еще лучше записать, чтобы не забыть выясненный нюанс в будущем. Но проходит время и свое обещание счастливо забывается. И в этом нет ничего удивительного, если трудоемкость решения оказалась очень низкой, такой, что при желании его может повторить практически любой желающий буквально за несколько часов, правда если он будет знать, в какую строну следует копать.

Продолжая серию статей про маленькие хитрости разработки под STM32, хочу поделиться двумя очень простыми, но полезными функциями. Они никак не тянут на полноценный проект на github.com, но способны облегчить жизнь (или наоборот, выпить немало крови), при определенном стечении обстоятельств.

  1. Буферизированный вывод отладочной информации в последовательный порт
  2. Автоматическое включение защиты от чтения и модификации прошивки

Буферизированный вывод в последовательный порт


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

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

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

Ниже приведена простая реализация вывода отладочной информации через последовательный порт для STM32 с реализацией на функциях HAL. Чтобы не тормозить работу потока отладочным выводом, данные сперва накапливаются в буфер и передача по DMA начинается только после получения флага flush или при заполнении буфера целиком.

Код функции
#ifdef UART_TRACE

static volatile bool dma_send = false;
static char dma_buffer[150]; // Статический буфер для вывода через DMA
static volatile size_t dma_size = 0;

// Завершение передачи по DMA
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
	if (huart == &UART_TRACE) {
		dma_send = false;
		dma_size = 0;
	}
}

// Вывод отладочной информации в UART
// str - Текстовая строка для отладочного вывода с завершающим нулевым символом
// flush - вывод накопленного буфера
void stm32_debug_trace(const char *str, bool flush) {

	size_t time_start = HAL_GetTick();
	// Ждать завершения предыдущей передачи в порт (если она есть)
	while (dma_send) {
		if ((HAL_GetTick() - time_start) > 100) { // Костыль
			dma_send = false;
			dma_size = 0;
			HAL_UART_AbortTransmit_IT(&UART_TRACE);
			break;
		}
	}

	size_t len = strlen(str);

	if (len + dma_size <= sizeof(dma_buffer)) {
		// Строка влезает, просто добавим её в конец буфера
		memcpy(&dma_buffer[dma_size], str, len);
		dma_size += len;
		len = 0;
	} else {
		if (dma_size > 0) {
			// Остатка буфера не хватает, передаем что там уже есть в блокирующем режиме
			HAL_UART_Transmit(&UART_TRACE, (uint8_t*) dma_buffer, dma_size, 0xFFFF);
			dma_size = 0;
		}
		if (len > sizeof(dma_buffer)) {
			// Если не влезает в буфер
			size_t over = len - sizeof(dma_buffer);
			// Передаем начало, оставляя размер буфера
			HAL_UART_Transmit(&UART_TRACE, (uint8_t*) str, over, 0xFFFF);
			memcpy(dma_buffer, &str[over], sizeof(dma_buffer));
			dma_size = sizeof(dma_buffer);
		} else {
			// все влезает
			memcpy(dma_buffer, str, len);
			dma_size = len;
		}
	}

	if (flush || dma_size == sizeof(dma_buffer)) {
		dma_send = true;
		// Передача в фоне через
		HAL_UART_Transmit_DMA(&UART_TRACE, (uint8_t*) dma_buffer, dma_size);
	}
}

#endif // UART_TRACE


Автоматическое включение защиты от чтения и модификации прошивки


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

Мне нравятся простые и универсальные решения, а в этом случае пришлось ломать голову из-за особенностей работы разных серий микроконтроллеров.

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

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

В результате различных тестов и экспериментов пришел к конфигурации, когда защита от модификации прошивки ставится на всю память, кроме второго и третьего сектора. Они используются для хранения настроек устройства и записи посмертного core dump`a при сбое прошивки.

Код функции
void stm32_read_protection_force() {
	FLASH_OBProgramInitTypeDef OBconfig;
	HAL_FLASHEx_OBGetConfig(&OBconfig);
	if (((OBconfig.OptionType & OPTIONBYTE_RDP) != OPTIONBYTE_RDP) || OBconfig.RDPLevel != OB_RDP_LEVEL_1) {

		HAL_FLASH_Unlock();
		HAL_FLASH_OB_Unlock();

		OBconfig.OptionType = OPTIONBYTE_RDP | OPTIONBYTE_WRP;
		// Защита от чтения прошивки
		OBconfig.RDPLevel = OB_RDP_LEVEL_1; 

		// Защита от случайного изменения прошивки
#ifdef FLASH_END // Для STM32F2xx и STM32F4xx
		OBconfig.WRPState = OB_WRPSTATE_ENABLE;
		OBconfig.WRPSector = OB_WRP_SECTOR_All;
		OBconfig.WRPSector &= ~OB_WRP_SECTOR_2; 
		OBconfig.WRPSector &= ~OB_WRP_SECTOR_3;
#else // Для STM32F1xx
		OBconfig.WRPState = OB_WRPSTATE_ENABLE;
		OBconfig.WRPPage = ~OB_WRP_PAGES2TO3; 
#endif
		HAL_FLASHEx_OBProgram(&OBconfig);
		HAL_FLASH_OB_Lock();
		HAL_FLASH_Lock();
		HAL_FLASH_OB_Launch(); // Перезагрузка
	}
}

Теги:stm32отладкамикроконтроллерыотладочный выводзащита прошивки
Хабы: Блог компании Timeweb Программирование C++ Промышленное программирование Программирование микроконтроллеров
Всего голосов 20: ↑18 и ↓2+16
Просмотры7.1K

Похожие публикации

Лучшие публикации за сутки

Информация

Дата основания
Местоположение
Россия
Сайт
timeweb.com
Численность
201–500 человек
Дата регистрации

Блог на Хабре