Программирование устройства. Создание проекта, настройка микроконтроллера, адаптация библиотек

Электроника

Пришла пора начать программировать микроконтроллер нашего устройства: давайте подробно рассмотрим каждый из этапов этого процесса
Глоссарий
Для успешного освоения материала рекомендуем вам изучить следующие понятия:
Микроконтроллер
Микросхема, предназначенная для управления электронными устройствами. Типичный микроконтроллер сочетает на одном кристалле функции процессора и периферийных устройств, содержит ОЗУ и (или) ПЗУ. По сути, это однокристальный компьютер, способный выполнять относительно простые задачи. Отличается от микропроцессора интегрированными в микросхему устройствами ввода-вывода, таймерами и другими периферийными устройствами
Видеолекция
Конспект
Начнем программировать микроконтроллер нашего устройства и пройдем каждый из этапов этого процесса

Этапы программирования микроконтроллера
  1. Создание проекта
  2. Настройка модулей
  3. Подключение библиотек индикатора
  4. Создание тестовых шаблонов программ
Работа на компьютере
1. Удаляем ненужные проекты, чтобы избежать конфликтов при запуске проекта

2. Создаем пустой проект stm32

3. Выбираем наш микроконтроллер
4. Выбираем расположение проекта и проконтролируем, подгрузилась ли новая библиотека
Работа с микроконтроллером в конфигураторе
Цель: конфигурировать выводы микроконтроллера согласно созданной схеме
1. Подключаем OLED-индикатор
  • на схеме он подключается по spi1
  • заходим во вкладку connectivity, находим spi1 и включаем индикатор
  • сдвигаем необходимые выходы согласно схеме, используя клавишу ctrl
  • настраиваем конфигурацию для полученного интерфейса, устанавливаем разрядность 8 и скорость 2
  • устанавливаем частоты, близкие к максимальным, например, 80
  • добавляем дополнительные три лапки
  • настраиваем выводы на соответствующие состояния
Важно
Для всех лапок устанавливаем максимальную скорость, так как скорость работы с периферией очень высокая
2. Настройка остальных выходов микроконтроллера:
  • выходы blecs, blerst, bleboot, bleoff настраиваются как обычные цифровые выходы с максимальной скоростью
  • but кнопка от энкодера настраивается как цифровой вход
  • blemosi и blemiso настраиваются как на индикатор, но скорость ниже
  • oled и vibro — обычные цифровые выходы
  • D- и D+ настраиваются автоматически при выборе настройки модуля usb
  • S+ (подключение динамика) — обычный цифровой выход

3. Настройка IMU сенсора:
  • находим I2C1 и подключаем его
  • ставим скорость выше стандартной
  • ставим частоту 400
4. Подключение энкодера:
  • выбираем LPTIM1
  • выбираем in1 in2, т.е. используются два входа
  • проводим настройку таймера
Важно
Полностью настроенные ножки выглядят так:
Настройка PC1 (AIN2_LIPO)
1. Сигнал непосредственно с аккумулятора делим на два, чтобы не нагружать вход микроконтроллера

2. Открываем вкладку analog и включаем измерение относительно массы микроконтроллера

3. Нас интересует энергопотребление всего устройства, поэтому ставим минимальную скорость измерения

  • заполняем настройки ADC_Settings
  • заполняем настройки ADC_Regular_ConversionMode
  • настройки DMA Settings:
Отображение времени на часах
Необходимо подобрать числа так, чтобы при делении 32 на первое число+1, потом на второе+1 получался 1 Гц, тогда часы будут работать верно
Работа с кодом
1. Библиотеки
После генерации кода приступаем к заполнению проекта необходимыми библиотеками:
  • библиотека для индикатора
  • библиотека для сенсора, адаптированная для stm32
  • другие

2. Датчики
  • акселерометр, который будет измерять проекцию вектора тяжести к центру Земли
  • датчик давления, показывающий высоту устройства над уровнем моря
  • компас
  • гироскоп, измеряющий угловые ускорения при повороте
Для нашего устройства основным будет являться LIS331, т.е. акселерометр.
accel.Accelerometer_ = Accelerometer_;
   accel.Accelerometer_(ACCEL_ADDRESS_V1, &(accel.LIS331DLH_TWI));

   gyro.Gyroscope_ = Gyroscope_;
   gyro.Gyroscope_(GYRO_ADDRESS_V1, &(gyro.L3G4200D_TWI));


   compass.Compass_ = Compass_;
   compass.Compass_(COMPASS_ADDRESS_V1, &(compass.LIS3MDL_TWI));

   barometer.Barometer_ = Barometer_;
   barometer.Barometer_(BARO_ADDRESS_V1, &(barometer.LPS331));

   accel.LIS331DLH_TWI.begin(&(accel.LIS331DLH_TWI));
   gyro.L3G4200D_TWI.begin(&(gyro.L3G4200D_TWI));


   compass.LIS3MDL_TWI.begin(&(compass.LIS3MDL_TWI));
   barometer.LPS331.begin(&(barometer.LPS331));

   // устанавливаем чувствительность компаса
   // ±4 gauss Ч по умолчанию, ±8 gauss, ±12 gauss, ±16 gauss
   compass.LIS3MDL_TWI.setRange(RANGE_4_GAUSS, &(compass.LIS3MDL_TWI));
   // калибровка компаса
   compass.LIS3MDL_TWI.calibrateMatrix(compassCalibrationMatrix, compassCalibrationBias, &(compass.LIS3MDL_TWI));

   //create filter object
   Filter.Filter_=Filter_;
   Filter.Filter_(&(Filter.Madgwick));

   uint8_t whoiam = accel.LIS331DLH_TWI.readWhoIAm(&(accel.LIS331DLH_TWI));
3. Таймер
Для того, чтобы настроить взаимодействие с пользователем, заводим таймер
if(htim->Instance == TIM1)
	{//прерывание из таймера TIM1 - настроен на обновление раз в 1 мс


		//timeout sleep
		if(count_tim1_timeout_sleep!=0)
			count_tim1_timeout_sleep--;
		else
		{
			count_tim1_timeout_sleep=TIMEOUT_SLEEP_TIM1;
			signal_timeout_sleep=1;
		}

		//генерируем событие "раз в 10 мс проверка объектов"
		htim1_event_10ms_count_obj--; //уменьшаем счетчик для события
		if(htim1_event_10ms_count_obj==0)
		{//счетчик достиг конца - генерим событие
			htim1_event_10ms_count_obj = HTIM1_EVENT_10ms_obj; //установим счетчик в изначальное состояние
			event_10ms_flag_obj = 1; //установим флаг события
		}


		//update imu
		if(count_tim1_update_imu!=0)
			count_tim1_update_imu--;
		else
		{
			count_tim1_update_imu=UPDATE_IMU_TIM1;
			to_update_imu=1;
		}


		btn_bounce_tim1--;
		if(btn_bounce_tim1==0)
		{//update 100 ms

			//keys

			if(HAL_GPIO_ReadPin(BUTENC_GPIO_Port,BUTENC_Pin)==GPIO_PIN_RESET)
				state_btn=0;
			else
				state_btn=1;
4. Кнопки
Делаем отдельный цикл, который будет вызываться каждые 100 мс и будет проверять состояние кнопки. При каждом нажатии будет меняться состояние яркого светодиода
btn_bounce_tim1--;
		if(btn_bounce_tim1==0)
		{//update 100 ms

			//keys

			if(HAL_GPIO_ReadPin(BUTENC_GPIO_Port,BUTENC_Pin)==GPIO_PIN_RESET)
				state_btn=0;
			else
				state_btn=1;


			if(state_btn_old==0 && state_btn==1)
				{
					btn_try_poll=1;
					count_tim1_timeout_sleep=TIMEOUT_SLEEP_TIM1;
				}

			if(state_btn_old==1 && state_btn==0)
				{
					btn_try_push=1;
					count_tim1_timeout_sleep=TIMEOUT_SLEEP_TIM1;
				}

			state_btn_old=state_btn;

			//encoder roller
			int32_t lev_enc = 0;
			state_enc = HAL_LPTIM_ReadCounter(&hlptim1);
			if(state_enc > state_enc_old + lev_enc)
				enc_try_inc=1;

			if(state_enc < state_enc_old - lev_enc)
				enc_try_dec=1;

			state_enc_old = state_enc;

			btn_bounce_tim1=BTN_BOUNCE_PERIOD_TIM1;
		}

	}
}
5. Энкодер
Каждые 100 мс он будет считывать значение счетчика. Если значение увеличилось до определенного порога, то мы ставим состояние увеличения на 1
//encoder roller
			int32_t lev_enc = 0;
			state_enc = HAL_LPTIM_ReadCounter(&hlptim1);
			if(state_enc > state_enc_old + lev_enc)
				enc_try_inc=1;

			if(state_enc < state_enc_old - lev_enc)
				enc_try_dec=1;

			state_enc_old = state_enc;

			btn_bounce_tim1=BTN_BOUNCE_PERIOD_TIM1;
		}

	}
}
6. Счетчик неактивности
Обыкновенный счетчик, который уменьшается. Как только он становится равен нулю, то устанавливается сигнал, по которому необходимо уйти в спящий режим. При нажатии на кнопку этот счетчик инициализируется заново
if(htim->Instance == TIM2)
	{//прерывание из таймера TIM2 - настроен на обновление раз в 200 мс


	}

	if(htim->Instance == TIM1)
	{//прерывание из таймера TIM1 - настроен на обновление раз в 1 мс


		//timeout sleep
		if(count_tim1_timeout_sleep!=0)
			count_tim1_timeout_sleep--;
		else
		{
			count_tim1_timeout_sleep=TIMEOUT_SLEEP_TIM1;
			signal_timeout_sleep=1;
		}

		//генерируем событие "раз в 10 мс проверка объектов"
		htim1_event_10ms_count_obj--; //уменьшаем счетчик для события
		if(htim1_event_10ms_count_obj==0)
		{//счетчик достиг конца - генерим событие
			htim1_event_10ms_count_obj = HTIM1_EVENT_10ms_obj; //установим счетчик в изначальное состояние
			event_10ms_flag_obj = 1; //установим флаг события
		}


		//update imu
		if(count_tim1_update_imu!=0)
			count_tim1_update_imu--;
		else
		{
			count_tim1_update_imu=UPDATE_IMU_TIM1;
			to_update_imu=1;
		}
Тестирование
Проверяем работу всех датчиков и энкодера. Энкодер при вращении меняет нижнее значение, а при нажатии включает светодиод и звуковой сигнал
Мы рассмотрели настройку микроконтроллера, создали проект и тестовую прошивку.
Давайте проверим, как вы запомнили материал!
Интерактивное задание
Тест
Для закрепления полученных знаний пройдите тест
Стартуем!
Какая частота выставляется при настройке IMU сенсора?
Дальше
Проверить
Узнать результат
Для чего делится на два сигнал, поступающий непосредственно с аккумулятора?
Дальше
Проверить
Узнать результат
Какой датчик измеряет проекцию вектора тяжести к центру Земли?
Дальше
Проверить
Узнать результат
Зачем нужен счетчик неактивности?
Дальше
Проверить
Узнать результат
К сожалению, вы ответили неправильно
Прочитайте лекцию и посмотрите видео еще раз
Пройти еще раз
Неплохо!
Но можно лучше. Прочитайте лекцию и посмотрите видео еще раз
Пройти еще раз
Отлично!
Вы отлично справились. Теперь можете ознакомиться с другими компетенциями
Пройти еще раз