Урок 51.
Часть 3
Магнитометр LIS3MDL
В прошлой части нашего урока мы продолжили и закончили писать инициализацию датчика и начали писать код для получения данных с его осей.
Начнём теперь вносить изменения в функцию Mag_Read. Для этого будем использовать фрагменты кода из файла lsm303dlhc.c из проекта с занятия по другому магнитометру (lsm303dlhc).
void Mag_Read(void)
{
int16_t buffer[3] = {0};
static int16_t val[3], tmp16;
Mag_GetXYZ(buffer);
Исправим дальнейший код
val[0]=buffer[0];
val[1]=buffer[1];
val[2]=buffer[2];
Раскомментируем строки кода, отвечающие за вывод считанной с осей информации в текстовый вид, а в графический закомментируем, заодно исправим переменные показаний осей
val[2]=buffer[2];
sprintf(str1,»X:%06d Y:%06d Z:%06drn», val[0], val[1], val[2]);
HAL_UART_Transmit_DMA(&huart2, (uint8_t*)str1,strlen(str1));
// buf2[0]=0x11;
// buf2[1]=0x55;
// buf2[2]=(uint8_t)(val[0]>>8);
// buf2[3]=(uint8_t)val[0];
// buf2[4]=(uint8_t)(val[1]>>8);
// buf2[5]=(uint8_t)val[1];
// buf2[6]=(uint8_t)(val[2]>>8);
// buf2[7]=(uint8_t)val[2];
HAL_UART_Transmit_DMA(&huart2,buf2,8);
Изменим код дальше
HAL_UART_Transmit_DMA(&huart2,buf2,8);
if(val[0]>500)
Чтобы нам посмотреть пока работу датчика в программе HyperTerminal, мы добавим-таки вызов функции mag_ini() в бесконечный цикл, а в обработчике прерывания пока закомментируем
/* USER CODE BEGIN 3 */
Mag_Read();
}
{
//Mag_Read();
HAL_UART_Receive_IT(&huart2, (uint8_t*) str, 8);
Скомпилируем код, прошьём контроллер и проверим работу датчика в программе HyperTerminal
Из данной картины, а также используя таблицу из технической документации, в которой находится чувствительность (дискретность одного гаусса, при диапазоне 4 гаусса она равна 6842) мы с вами видим, что показания по X у нас в районе 0,07 гаусс, по Y – около 0,3, по Z – около 0,33, что в принципе нормально.
Чтобы оценить шум, нам желательно показания отследить уже в программе визуализации. Для этого мы наоборот, раскомментируем код для визуализации, а для текстового вывода закомментируем
// sprintf(str1,»X:%06d Y:%06d Z:%06drn», val[0], val[1], val[2]);
// HAL_UART_Transmit_DMA(&huart2, (uint8_t*)str1,strlen(str1));
buf2[0]=0x11;
buf2[1]=0x55;
buf2[2]=(uint8_t)(val[0]>>8);
buf2[3]=(uint8_t)val[0];
buf2[4]=(uint8_t)(val[1]>>8);
buf2[5]=(uint8_t)val[1];
buf2[6]=(uint8_t)(val[2]>>8);
buf2[7]=(uint8_t)val[2];
HAL_UART_Transmit_DMA(&huart2,buf2,8);
Перед тем как запустить программу визуализации, обратно раскомментируем вызов функции в обработчике и закомментируем в бесконечном цикле. Также уберём задержку из функции считывания
/* USER CODE BEGIN 3 */
//Mag_Read();
}
Mag_Read();
HAL_UART_Receive_IT(&huart2, (uint8_t*) str, 8);
}
LD2_OFF;
}
//HAL_Delay(20);
}
Запустим программу визуализации (кстати я её маленько усовершенствовал опять и включил в нее переключатель диапазонов ну и также добавил отправку 8 байтов для запуска функции чтения с осей магнитометра в контроллере) и посмотрим на графики осей
Теперь применим фильтр скользящего среднего. Функцию для этого мы возьмём из урока по предыдущему магнитометру. Также вместе с функцией возьмём все глобальные переменные, предназначенные для данного фильтра
char str1[30]={0};
//буферы для скользящего среднего
volatile int16_t xbuf_avg[8]={0},ybuf_avg[8]={0},zbuf_avg[8]={0};
//счётчик наполнения буферов скользящего среднего
volatile int8_t avg_cnt;
//сумма для среднего арифметического
volatile int64_t tmp64[3];
//———————————————
//———————————————
void MovingAverage(int16_t* dt)
{
if(avg_cnt<8)
{
xbuf_avg[avg_cnt]=dt[0];
ybuf_avg[avg_cnt]=dt[1];
zbuf_avg[avg_cnt]=dt[2];
if(avg_cnt==7)
{
tmp64[0]=xbuf_avg[7]+xbuf_avg[6]+xbuf_avg[5]+xbuf_avg[4]+xbuf_avg[3]+xbuf_avg[2]+xbuf_avg[1]+xbuf_avg[0];
tmp64[1]=ybuf_avg[7]+ybuf_avg[6]+ybuf_avg[5]+ybuf_avg[4]+ybuf_avg[3]+ybuf_avg[2]+ybuf_avg[1]+ybuf_avg[0];
tmp64[2]=zbuf_avg[7]+zbuf_avg[6]+zbuf_avg[5]+zbuf_avg[4]+zbuf_avg[3]+zbuf_avg[2]+zbuf_avg[1]+zbuf_avg[0];
dt[0]=tmp64[0]/8;
dt[1]=tmp64[1]/8;
dt[2]=tmp64[2]/8;
}
avg_cnt++;
}
else
{
//вычтем из общих сумм последние элементы
tmp64[0]-=xbuf_avg[0];
tmp64[1]-=ybuf_avg[0];
tmp64[2]-=zbuf_avg[0];
//сдвинем буферы на 1 элемент
memcpy((void*)xbuf_avg,(void*)(xbuf_avg+1),sizeof(int16_t)*7);
memcpy((void*)ybuf_avg,(void*)(ybuf_avg+1),sizeof(int16_t)*7);
memcpy((void*)zbuf_avg,(void*)(zbuf_avg+1),sizeof(int16_t)*7);
//заменим на 7 элементы на новые
xbuf_avg[7]=dt[0];
ybuf_avg[7]=dt[1];
zbuf_avg[7]=dt[2];
//прибавим новые элементы
tmp64[0]+=dt[0];
tmp64[1]+=dt[1];
tmp64[2]+=dt[2];
//обновим средние значения
dt[0]=tmp64[0]/8;
dt[1]=tmp64[1]/8;
dt[2]=tmp64[2]/8;
}
}
//———————————————
Вызовем данную функцию в функции чтения данных
val[2]=buffer[2];
//Применим фильтр скользящего среднего
MovingAverage(val);
Также не забываем про инициализацию
void Mag_Ini(void)
{
uint16_t ctrl = 0x0000;
//инициализируем счетчик заполнения скользящего среднего
avg_cnt=0;
HAL_Delay(1000);
Соберем код и прошьём контроллер.
Совсем другое дело
Давайте теперь попробуем добавить возможность пользователю включать и отключать фильтр, так как у нас все равно не используется синяя кнопка USER_BUTTON. Как мы можем наблюдать из схемы, подключена она к лапке порта PC13
Настроим данную ножку порта в Cube MX
Заново сгенерируем проект, скомпилируем его.
Добавим проверку статуса кнопки
val[2]=buffer[2];
tmp16 = HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_13);
//Если кнопка не нажата
//Применим фильтр скользящего среднего
if(tmp16!=0) MovingAverage(val);
Теперь при нажатой кнопке фильтр будет отключаться. Проверим это, скомпилировав код и прошив контроллер
Теперь мы можем наблюдать в реальном времени, как работает алгоритм чтения данных с магнитометра с фильтром и без фильтра.
Попробуем работу нашего датчика в другой программе визуализации. Положим датчик на неподвижную поверхность и запустим программу визуализации. В данную программу я тоже включил делитель. В данный момент он у нас выступит регулятором чувствительности.
Пока выставим при старте программы вот такие значения
Но так как у нас оси не скорректированы, то шарик, который должен отклоняться при изменении магнитного поля, у нас укатится.
Прочитаем в программе показания по осям и скорректируем их в программе МК с обратным знаком. Возможно впоследствии я внесу возможность корректировки значений в программу визуализации. А пока так
val[0]=buffer[0]-745;
val[1]=buffer[1]+3500;
val[2]=buffer[2]-2309;
Скомпилируем код и прошьём контроллер. Затем запустим программу с теми же установками делителя. Шарик хотя и укатывается, но уже очень медленно
Можно подобрать делитель так, что шарик вообще остановится. У меня получилось 1024.
Теперь можно поиграться магнитом возле датчика и понаблюдать за шариком.
Предыдущая часть Программирование МК STM32 Следующий урок
Техническая документация на датчик
Отладочную плату можно приобрести здесь Nucleo STM32F401RE
Оценочную плату можно приобрести здесь STM32 X-NUCLEO-IKS01A1
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
Доброго времени суток! Ссылка на скачивание NS Port Monitor выдает код 404, исправьте, пожалуйста.
Исправил. Спасибо за замечание!