【STM32L476RG】第七节——基于官方DMP库的简易计步器
<div class='showpostmsg'> 本帖最后由 Zhao_kar 于 2023-10-24 20:35 编辑<p style="text-align: center;"><strong><span style="font-size:24px;">【STM32L476RG】第七节——基于官方DMP库的简易计步器</span></strong></p>
<p>项目简介:</p>
<p>【STM32L476RG测评】——最终成品:基于MPU6050的计步器</p>
<p>本次参加eeworld的ST开发板测评的活动,申请到了STM32L476RG这个板子,现已完成作品,故于此发布成品视频。</p>
<p><span style="color:#e74c3c;">项目大概简介:</span></p>
<p><span style="color:#e74c3c;">本项目使用L476的开发板完成了一个简易计步器</span></p>
<p><span style="color:#e74c3c;">1、使用mpu6050传感器,移植官方DMP的库,除了计步,还可以显示陀螺仪的两组基本参数。</span></p>
<p><span style="color:#e74c3c;">2、使用HC05与手机端口通信,使得手机端也能接收到计步器的数据。</span></p>
<p><span style="color:#e74c3c;">3、使用oled屏幕的SPI协议实现简单通信,可以在屏幕上显示步数。</span></p>
<p><span style="color:#e74c3c;">4、可以与电脑端的串口调试助手进行数据测试。</span></p>
<p><span style="color:#e74c3c;">5、可以使用串口屏进行显示(这里没有使用,是因为身边没有合适的供电电源了,单片机的供电已经远远无法支持如此多的设备了)。</span></p>
<p><span style="color:#1abc9c;">闲聊:从测评开始到现在,也写了很多报告了,最后这一篇,我打算整篇重点讲DMP库,毕竟前面的报告都把其他的功能都实现了,所以这里就讲DMP和总体的调测。</span></p>
<p><span style="color:#1abc9c;">先说一下主要步骤:</span></p>
<ul>
<li><span style="color:#1abc9c;">移植官方的DMP库,毫无疑问,要cubemx配置IIC,然后移植官方的库,因为hal的存在,所以要进行删改</span></li>
<li><span style="color:#1abc9c;">cubemx配置GPIO的SPI,为了使用OLED</span></li>
<li><span style="color:#1abc9c;">配置USART,开了三个,分别用于,PC端调试,HC05,串口屏</span></li>
<li><span style="color:#1abc9c;">串口屏界面配置,因为简介说的电源问题没用上,用的话参考串口屏那篇</span></li>
<li><span style="color:#1abc9c;">调用mputest的主函数,放到main的里面,注,这个函数是死循环,如果要改的话可以自己看看,这边没有改</span></li>
<li><span style="color:#1abc9c;">大体为先把外设初始化,然后是主函数使用mputest,进入计步器功能,然后循环打印步数,同时在这个函数里面使用oled函数,去让oled显示,这个函数里面自带printf,然后再使能串口,如果是串口屏改一下函数,然后就可以输出给各个串口接收端口了。</span></li>
</ul>
<p><strong><span style="color:#f1c40f;"><span style="font-size:20px;">一、DMP库的移植</span></span></strong></p>
<p>1、cubemx的初始配置(图三:总体引脚图参考)</p>
<ul>
<li>RCC+SYS+时钟树略</li>
<li>USART三个,略</li>
<li>SPI四个引脚,之前讲过,略</li>
<li>IIC配置,快速+DMA(下图1)
<div style="text-align: center;"></div>
<p> </p>
</li>
<li>NVIC打开(图2)
<div style="text-align: center;"></div>
<div style="text-align: center;"></div>
</li>
</ul>
<div><span style="font-size:18px;"><strong><span style="color:#e74c3c;"><span style="background-color:#f1c40f;">2、官方库的文件配置(主要部分)</span></span></strong></span></div>
<ul>
<li>这里只需要改lib文件</li>
<li>路径大概为,如果不会翻墙的话简易去找某火,大概为:传感器\姿态_MPU6050\参考资料\官方文档及库原文件\motion_driver_6.12\mpl libraries\arm\keil</li>
<li>这个路径下面会有如下几个压缩包
<div style="text-align: center;"></div>
</li>
</ul>
<p> </p>
<ul>
<li>这边因为是L476,也就是M4内核,所以使用M4的文件,解压,lib放到下面的文件,你的单片机是什么核心用什么,f7的也用m4</li>
<li>找到四个关键代码文件,把他复制出来,添加到工程目录,建议使用mpu6050,然后放到里面,如下四个
<div style="text-align: center;"></div>
</li>
<li>其中mpl文件夹,就是我们要操作的,里面可以看到是没有lib的,有了也没关系,删掉它,把前面解压的lib放进去,如图2
<div style="text-align: center;"></div>
<div style="text-align: center;"></div>
</li>
<li>ps,前面有点乱,这边建议就是,先把四个文件放到工程目录,然后把工程目录的lib文件删掉,把解压缩的放进去,前面讲的有点乱,我的问题。</li>
<li>keil加一下工程目录,不细说了,如图,补充一个,记得添加文件的时候,mpl要加lib,圈的那个就是lib,选择全部文件就可以看到了。
<div style="text-align: center;"></div>
</li>
<li>因为是官方库,魔术棒>C++>Define那里,原先应该是haluser什么的,这里要加一下,改成:
<pre>
<code class="language-cpp">USE_HAL_DRIVER,STM32L476xx,MPL_LOG_NDEBUG=1,MPU6050,EMPL,USE_DMP,EMPL_TARGET_STM32F4
//注释:原因见官方文档,在doc的第三个:App Note 3- Motion Driver 6.12 Porting Guide</code></pre>
<div style="text-align: center;"></div>
</li>
<li>然后编译,必定报错,这里有两个注意,第一个是,后续的所有头文件,改成你的单片机型号,如
<pre>
<code class="language-cpp">#include "stm32l4xx.h"
//我这里是l4,你的是多少该多少,比如你是f7,那就改f7</code></pre>
<p> </p>
</li>
<li>第二个是,报错应该会有说一个board-st_discovery找不到的,去motion_driver_6.12\arm\STM32F4_MD6\Projects\eMD6\DeviceSupport,两个文件里面分别包含c和h,添加到mpu6050文件夹。(记得在keil里面添加文件)</li>
<li>还有就是,因为hal是usart,官方是uart,h的标注可以改一下</li>
<li>然后log_stm32c里面会报错fputc,是因为移植库里面有usart的函数,只需要在usart里面加一个这个,然后还是会报错,因为默认是fputc,改一下其他文件里的名字就行
<pre>
<code class="language-cpp">int fputcc(int ch)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xffff);
HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, 0xffff);
return ch;
}
//在usart.c里面加这个
//别忘了在usart.h加声明
/* USER CODE BEGIN Includes */
int fputcc(int ch);
/* USER CODE END Includes */
</code></pre>
</li>
</ul>
<ul>
<li>然后再board_discovery里面把board_init删掉,用不上这个,头文件还有一个RCC和USART的,这个也不要,因为hal有了</li>
<li>然后第一个函数是一个延时的,因为hal自带,改成如下,记得在.h加声明
<pre>
<code class="language-cpp">void mdelay(unsigned long nTime)
{
HAL_Delay(nTime);
}
int get_tick_count(unsigned long *count)
{
count = HAL_GetTick();
return 0;
}
//h里面有
int get_tick_count(unsigned long *count);
void mdelay(unsigned long nTime);</code></pre>
<p> </p>
</li>
<li>然后在diver里面一个地方要改一下,在inv_mpu_dmp_motion_driver.c文件里面,有一个函数,里面的某个改成如下
<pre>
<code class="language-cpp">int dmp_set_accel_bias(long *bias)
{
long accel_bias_body;
unsigned char regs;
long long accel_sf;
unsigned short accel_sens;
mpu_get_accel_sens(&accel_sens);
accel_sf = (long long)accel_sens << 15;
__nop();//只改这一个,我是截取了一部分
//下面还有很多内容,就改上面的nop。</code></pre>
<p> </p>
</li>
<li>然后打开IIC,这里需要改一改,改成如下,这个函数来源于官方文件的emd6文件夹里面的,peripheral里的IIC有这个函数的说明。
<pre>
<code class="language-cpp">/* USER CODE BEGIN 1 */
/**
* @briefWrites a Byte to a given register to the sensors through the
control interface (I2C)
* @paramRegisterAddr: The address (location) of the register to be written.
* @paramRegisterValue: the Byte value to be written into destination register.
* @retval 0 if correct communication, else wrong communication
*/
int Sensors_I2C_WriteRegister(unsigned char slave_addr,
unsigned char reg_addr,
unsigned short len,
const unsigned char *data_ptr)
{
if(HAL_I2C_Mem_Write(&hi2c1 ,slave_addr,reg_addr ,I2C_MEMADD_SIZE_8BIT, (uint8_t *)data_ptr,len,1000)!= HAL_OK)
{
return HAL_ERROR;
}
while(HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY)
{
}
while(HAL_I2C_IsDeviceReady(&hi2c1,slave_addr ,100 ,100) == HAL_TIMEOUT);
while(HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY)
{
}
return HAL_OK;
}
int Sensors_I2C_ReadRegister(unsigned char slave_addr,
unsigned char reg_addr,
unsigned short len,
unsigned char *data_ptr)
{
if(HAL_I2C_Mem_Read(&hi2c1 ,slave_addr,reg_addr ,I2C_MEMADD_SIZE_8BIT, (uint8_t *)data_ptr,len,1000)!= HAL_OK)
{
return HAL_ERROR;
}
while(HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY)
{
}
return HAL_OK;
}
/* USER CODE END 1 */</code></pre>
<p> </p>
</li>
<li>同时别忘了声明,在h里面添加
<pre>
<code class="language-cpp">/* USER CODE BEGIN Prototypes */
int Sensors_I2C_ReadRegister(unsigned char Address, unsigned char RegisterAddr,
unsigned short RegisterLen, unsigned char *RegisterValue);
int Sensors_I2C_WriteRegister(unsigned char Address, unsigned char RegisterAddr,
unsigned short RegisterLen, const unsigned char *RegisterValue);
/* USER CODE END Prototypes */</code></pre>
<p> </p>
</li>
<li>最后把官方库的主函数搬到自己的代码里面,我这里放在了board_discovery.c里面,改一下名字,函数改成
<pre>
<code class="language-cpp">void mpu_test</code></pre>
</li>
<li>
<p>然后接下来是中断,在gpio.c里面加这个</p>
<pre>
<code class="language-cpp">/* USER CODE BEGIN 2 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == GPIO_PIN_1)
{
gyro_data_ready_cb();
}
}
/* USER CODE END 2 */
//注释:h里面别忘记包含discovery的h头文件</code></pre>
<p> </p>
</li>
<li>
<p>然后discovery的h加个声明</p>
<pre>
<code class="language-cpp">void mpu_test(void);
void gyro_data_ready_cb(void);</code></pre>
<p> </p>
</li>
<li>
<p>然后剩下的就是对discovery的c文件进行修改了,里面会有一些多余的,我把完整的文件代码放在后面。</p>
</li>
<li>
<p>差点忘了,还有一个是那个inv_mpu.c里面有一个地址,这边改成0XD0</p>
<pre>
<code class="language-cpp">const struct hw_s hw = {
.addr = 0xD0,
.max_fifo = 1024,
.num_reg = 118,
.temp_sens = 340,
.temp_offset = -521,
.bank_size = 256
#if defined AK89xx_SECONDARY
,.compass_fsr = AK89xx_FSR
#endif
};
//需要注意的是,里面有多个mpu系列,别搞错了,找到6050的改</code></pre>
</li>
<li>
<p>然后移植就差不多了,下面就是显示部分。</p>
</li>
</ul>
<p><strong><span style="font-size:20px;"><span style="color:#f1c40f;">二、显示+主函数</span></span></strong></p>
<p>1、主函数</p>
<pre>
<code>/* USER CODE BEGIN Includes */
#include "board-st_discovery.h"
#include "stdio.h"
#include "oled.h"
/* USER CODE END Includes */
void SystemClock_Config(void);
int main(void)
{
//初始化部分,包括IIC和SPI和串口和中断还有DMA
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_I2C1_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
MX_USART3_UART_Init();
/* USER CODE BEGIN 2 */
OLED_Init();//初始化
OLED_Clear();//清屏
OLED_Display_On();//打开显示
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
printf("01\r\n");//调试的
mpu_test();//计数函数
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
{
Error_Handler();
}
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
RCC_OscInitStruct.MSIState = RCC_MSI_ON;
RCC_OscInitStruct.MSICalibrationValue = 0;
RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_6;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_MSI;
RCC_OscInitStruct.PLL.PLLM = 1;
RCC_OscInitStruct.PLL.PLLN = 40;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @briefThis function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdefUSE_FULL_ASSERT
/**
* @briefReports the name of the source file and the source line number
* where the assert_param error has occurred.
* @paramfile: pointer to the source file name
* @paramline: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
</code></pre>
<p>2、discovery的变动部分</p>
<ul>
<li>首先是计步器部分
<pre>
<code class="language-cpp">static void read_from_mpl(void)
{
long msg, data;
int8_t accuracy;
unsigned long timestamp;
float float_data = {0};
{
unsigned long timestamp;
get_tick_count(&timestamp);
if (timestamp > hal.next_pedo_ms) {
hal.next_pedo_ms = timestamp + PEDO_READ_MS;
unsigned long step_count, walk_time;
dmp_get_pedometer_step_count(&step_count);
dmp_get_pedometer_walk_time(&walk_time);
printf("Walked %ld steps over %ld milliseconds..\r\n", step_count,
walk_time);//重点,计步器数据的打印
OLED_ShowNumber(59,0,step_count,2,16);//oled的显示计步器步数
}
}
}</code></pre>
<p> </p>
</li>
<li>然后是mpu函数的最后,加一个fresh,也就是oled的刷新
<pre>
<code class="language-cpp"> if (new_data) {
inv_execute_on_data();
/* This function reads bias-compensated sensor data and sensor
* fusion outputs from the MPL. The outputs are formatted as seen
* in eMPL_outputs.c. This function only needs to be called at the
* rate requested by the host.
*/
// MX_USART1_UART_Init();
// MX_USART2_UART_Init();
// MX_USART3_UART_Init();
//printf("01\r\n");
//上面不用管,调试的时候用的,原库没有刷新,这边加一个就行
read_from_mpl();
OLED_Refresh_Gram();</code></pre>
</li>
<li>
<p>至此,移植完成,可以连接硬件和测试了,下面是discovery.c的代码</p>
<pre>
<code class="language-cpp">#include "stm32l4xx.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"
#include "board-st_discovery.h"
#include "stdio.h"
#include "main.h"
#include "inv_mpu.h"
#include "inv_mpu_dmp_motion_driver.h"
#include "invensense.h"
#include "invensense_adv.h"
#include "eMPL_outputs.h"
#include "mltypes.h"
#include "mpu.h"
#include "log.h"
#include "packet.h"
#include "oled.h"
/* Private typedef -----------------------------------------------------------*/
/* Data read from MPL. */
#define PRINT_ACCEL (0x01)
#define PRINT_GYRO (0x02)
#define PRINT_QUAT (0x04)
#define PRINT_COMPASS (0x08)
#define PRINT_EULER (0x10)
#define PRINT_ROT_MAT (0x20)
#define PRINT_HEADING (0x40)
#define PRINT_PEDO (0x80)
#define PRINT_LINEAR_ACCEL (0x100)
#define PRINT_GRAVITY_VECTOR (0x200)
volatile uint32_t hal_timestamp = 0;
#define ACCEL_ON (0x01)
#define GYRO_ON (0x02)
#define COMPASS_ON (0x04)
#define MOTION (0)
#define NO_MOTION (1)
/* Starting sampling rate. */
#define DEFAULT_MPU_HZ(20)
#define FLASH_SIZE (512)
#define FLASH_MEM_START ((void*)0x1800)
#define PEDO_READ_MS (1000)
#define TEMP_READ_MS (500)
#define COMPASS_READ_MS (100)
struct rx_s {
unsigned char header;
unsigned char cmd;
};
struct hal_s {
unsigned char lp_accel_mode;
unsigned char sensors;
unsigned char dmp_on;
unsigned char wait_for_tap;
volatile unsigned char new_gyro;
unsigned char motion_int_mode;
unsigned long no_dmp_hz;
unsigned long next_pedo_ms;
unsigned long next_temp_ms;
unsigned long next_compass_ms;
unsigned int report;
unsigned short dmp_features;
struct rx_s rx;
};
static struct hal_s hal = {0};
/* USB RX binary semaphore. Actually, it's just a flag. Not included in struct
* because it's declared extern elsewhere.
*/
volatile unsigned char rx_new;
unsigned char *mpl_key = (unsigned char*)"eMPL 5.1";
/* Platform-specific information. Kinda like a boardfile. */
struct platform_data_s {
signed char orientation;
};
/* The sensors can be mounted onto the board in any orientation. The mounting
* matrix seen below tells the MPL how to rotate the raw data from the
* driver(s).
* TODO: The following matrices refer to the configuration on internal test
* boards at Invensense. If needed, please modify the matrices to match the
* chip-to-body matrix for your particular set up.
*/
static struct platform_data_s gyro_pdata = {
.orientation = { 1, 0, 0,
0, 1, 0,
0, 0, 1}
};
#if defined MPU9150 || defined MPU9250
static struct platform_data_s compass_pdata = {
.orientation = { 0, 1, 0,
1, 0, 0,
0, 0, -1}
};
#define COMPASS_ENABLED 1
#elif defined AK8975_SECONDARY
static struct platform_data_s compass_pdata = {
.orientation = {-1, 0, 0,
0, 1, 0,
0, 0,-1}
};
#define COMPASS_ENABLED 1
#elif defined AK8963_SECONDARY
static struct platform_data_s compass_pdata = {
.orientation = {-1, 0, 0,
0,-1, 0,
0, 0, 1}
};
#define COMPASS_ENABLED 1
#endif
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* ---------------------------------------------------------------------------*/
/* Get data from MPL.
* TODO: Add return values to the inv_get_sensor_type_xxx APIs to differentiate
* between new and stale data.
*/
static void read_from_mpl(void)
{
long msg, data;
int8_t accuracy;
unsigned long timestamp;
float float_data = {0};
{
unsigned long timestamp;
get_tick_count(&timestamp);
if (timestamp > hal.next_pedo_ms) {
hal.next_pedo_ms = timestamp + PEDO_READ_MS;
unsigned long step_count, walk_time;
dmp_get_pedometer_step_count(&step_count);
dmp_get_pedometer_walk_time(&walk_time);
printf("Walked %ld steps over %ld milliseconds..\r\n", step_count,
walk_time);
//printf("555");
OLED_ShowNumber(59,0,step_count,2,16);
}
}
}
#ifdef COMPASS_ENABLED
void send_status_compass() {
long data = { 0 };
int8_t accuracy = { 0 };
unsigned long timestamp;
inv_get_compass_set(data, &accuracy, (inv_time_t*) &timestamp);
MPL_LOGI("Compass: %7.4f %7.4f %7.4f ",
data/65536.f, data/65536.f, data/65536.f);
MPL_LOGI("Accuracy= %d\r\n", accuracy);
}
#endif
/* Handle sensor on/off combinations. */
static void setup_gyro(void)
{
unsigned char mask = 0, lp_accel_was_on = 0;
if (hal.sensors & ACCEL_ON)
mask |= INV_XYZ_ACCEL;
if (hal.sensors & GYRO_ON) {
mask |= INV_XYZ_GYRO;
lp_accel_was_on |= hal.lp_accel_mode;
}
#ifdef COMPASS_ENABLED
if (hal.sensors & COMPASS_ON) {
mask |= INV_XYZ_COMPASS;
lp_accel_was_on |= hal.lp_accel_mode;
}
#endif
/* If you need a power transition, this function should be called with a
* mask of the sensors still enabled. The driver turns off any sensors
* excluded from this mask.
*/
mpu_set_sensors(mask);
mpu_configure_fifo(mask);
if (lp_accel_was_on) {
unsigned short rate;
hal.lp_accel_mode = 0;
/* Switching out of LP accel, notify MPL of new accel sampling rate. */
mpu_get_sample_rate(&rate);
inv_set_accel_sample_rate(1000000L / rate);
}
}
static void tap_cb(unsigned char direction, unsigned char count)
{
switch (direction) {
case TAP_X_UP:
MPL_LOGI("Tap X+ ");
break;
case TAP_X_DOWN:
MPL_LOGI("Tap X- ");
break;
case TAP_Y_UP:
MPL_LOGI("Tap Y+ ");
break;
case TAP_Y_DOWN:
MPL_LOGI("Tap Y- ");
break;
case TAP_Z_UP:
MPL_LOGI("Tap Z+ ");
break;
case TAP_Z_DOWN:
MPL_LOGI("Tap Z- ");
break;
default:
return;
}
MPL_LOGI("x%d\n", count);
return;
}
static void android_orient_cb(unsigned char orientation)
{
switch (orientation) {
case ANDROID_ORIENT_PORTRAIT:
MPL_LOGI("Portrait\n");
break;
case ANDROID_ORIENT_LANDSCAPE:
MPL_LOGI("Landscape\n");
break;
case ANDROID_ORIENT_REVERSE_PORTRAIT:
MPL_LOGI("Reverse Portrait\n");
break;
case ANDROID_ORIENT_REVERSE_LANDSCAPE:
MPL_LOGI("Reverse Landscape\n");
break;
default:
return;
}
}
static inline void run_self_test(void)
{
int result;
long gyro, accel;
#if defined (MPU6500) || defined (MPU9250)
result = mpu_run_6500_self_test(gyro, accel, 0);
#elif defined (MPU6050) || defined (MPU9150)
result = mpu_run_self_test(gyro, accel);
#endif
if (result == 0x7) {
MPL_LOGI("Passed!\n");
MPL_LOGI("accel: %7.4f %7.4f %7.4f\n",
accel/65536.f,
accel/65536.f,
accel/65536.f);
MPL_LOGI("gyro: %7.4f %7.4f %7.4f\n",
gyro/65536.f,
gyro/65536.f,
gyro/65536.f);
/* Test passed. We can trust the gyro data here, so now we need to update calibrated data*/
#ifdef USE_CAL_HW_REGISTERS
/*
* This portion of the code uses the HW offset registers that are in the MPUxxxx devices
* instead of pushing the cal data to the MPL software library
*/
unsigned char i = 0;
for(i = 0; i<3; i++) {
gyro = (long)(gyro * 32.8f); //convert to +-1000dps
accel *= 2048.f; //convert to +-16G
accel = accel >> 16;
gyro = (long)(gyro >> 16);
}
mpu_set_gyro_bias_reg(gyro);
#if defined (MPU6500) || defined (MPU9250)
mpu_set_accel_bias_6500_reg(accel);
#elif defined (MPU6050) || defined (MPU9150)
mpu_set_accel_bias_6050_reg(accel);
#endif
#else
/* Push the calibrated data to the MPL library.
*
* MPL expects biases in hardware units << 16, but self test returns
* biases in g's << 16.
*/
unsigned short accel_sens;
float gyro_sens;
mpu_get_accel_sens(&accel_sens);
accel *= accel_sens;
accel *= accel_sens;
accel *= accel_sens;
inv_set_accel_bias(accel, 3);
mpu_get_gyro_sens(&gyro_sens);
gyro = (long) (gyro * gyro_sens);
gyro = (long) (gyro * gyro_sens);
gyro = (long) (gyro * gyro_sens);
inv_set_gyro_bias(gyro, 3);
#endif
}
else {
if (!(result & 0x1))
MPL_LOGE("Gyro failed.\n");
if (!(result & 0x2))
MPL_LOGE("Accel failed.\n");
if (!(result & 0x4))
MPL_LOGE("Compass failed.\n");
}
}
/* Every time new gyro data is available, this function is called in an
* ISR context. In this example, it sets a flag protecting the FIFO read
* function.
*/
void gyro_data_ready_cb(void)
{
hal.new_gyro = 1;
}
/*******************************************************************************/
/**
* @brief main entry point.
* @par Parameters None
* @retval void None
* @par Required preconditions: None
*/
void mpu_test(void)
{
inv_error_t result;
unsigned char accel_fsr,new_temp = 0;
unsigned short gyro_rate, gyro_fsr;
unsigned long timestamp;
struct int_param_s int_param;
#ifdef COMPASS_ENABLED
unsigned char new_compass = 0;
unsigned short compass_fsr;
#endif
// board_init();
result = mpu_init(&int_param);
if (result) {
MPL_LOGE("Could not initialize gyro.\n");
}
/* If you're not using an MPU9150 AND you're not using DMP features, this
* function will place all slaves on the primary bus.
* mpu_set_bypass(1);
*/
result = inv_init_mpl();
if (result) {
MPL_LOGE("Could not initialize MPL.\n");
}
/* Compute 6-axis and 9-axis quaternions. */
inv_enable_quaternion();
inv_enable_9x_sensor_fusion();
/* The MPL expects compass data at a constant rate (matching the rate
* passed to inv_set_compass_sample_rate). If this is an issue for your
* application, call this function, and the MPL will depend on the
* timestamps passed to inv_build_compass instead.
*
* inv_9x_fusion_use_timestamps(1);
*/
/* This function has been deprecated.
* inv_enable_no_gyro_fusion();
*/
/* Update gyro biases when not in motion.
* WARNING: These algorithms are mutually exclusive.
*/
inv_enable_fast_nomot();
/* inv_enable_motion_no_motion(); */
/* inv_set_no_motion_time(1000); */
/* Update gyro biases when temperature changes. */
inv_enable_gyro_tc();
/* This algorithm updates the accel biases when in motion. A more accurate
* bias measurement can be made when running the self-test (see case 't' in
* handle_input), but this algorithm can be enabled if the self-test can't
* be executed in your application.
*
* inv_enable_in_use_auto_calibration();
*/
#ifdef COMPASS_ENABLED
/* Compass calibration algorithms. */
inv_enable_vector_compass_cal();
inv_enable_magnetic_disturbance();
#endif
/* If you need to estimate your heading before the compass is calibrated,
* enable this algorithm. It becomes useless after a good figure-eight is
* detected, so we'll just leave it out to save memory.
* inv_enable_heading_from_gyro();
*/
/* Allows use of the MPL APIs in read_from_mpl. */
inv_enable_eMPL_outputs();
result = inv_start_mpl();
if (result == INV_ERROR_NOT_AUTHORIZED) {
while (1) {
MPL_LOGE("Not authorized.\n");
}
}
if (result) {
MPL_LOGE("Could not start the MPL.\n");
}
/* Get/set hardware configuration. Start gyro. */
/* Wake up all sensors. */
#ifdef COMPASS_ENABLED
mpu_set_sensors(INV_XYZ_GYRO | INV_XYZ_ACCEL | INV_XYZ_COMPASS);
#else
mpu_set_sensors(INV_XYZ_GYRO | INV_XYZ_ACCEL);
#endif
/* Push both gyro and accel data into the FIFO. */
mpu_configure_fifo(INV_XYZ_GYRO | INV_XYZ_ACCEL);
mpu_set_sample_rate(DEFAULT_MPU_HZ);
#ifdef COMPASS_ENABLED
/* The compass sampling rate can be less than the gyro/accel sampling rate.
* Use this function for proper power management.
*/
mpu_set_compass_sample_rate(1000 / COMPASS_READ_MS);
#endif
/* Read back configuration in case it was set improperly. */
mpu_get_sample_rate(&gyro_rate);
mpu_get_gyro_fsr(&gyro_fsr);
mpu_get_accel_fsr(&accel_fsr);
#ifdef COMPASS_ENABLED
mpu_get_compass_fsr(&compass_fsr);
#endif
/* Sync driver configuration with MPL. */
/* Sample rate expected in microseconds. */
inv_set_gyro_sample_rate(1000000L / gyro_rate);
inv_set_accel_sample_rate(1000000L / gyro_rate);
#ifdef COMPASS_ENABLED
/* The compass rate is independent of the gyro and accel rates. As long as
* inv_set_compass_sample_rate is called with the correct value, the 9-axis
* fusion algorithm's compass correction gain will work properly.
*/
inv_set_compass_sample_rate(COMPASS_READ_MS * 1000L);
#endif
/* Set chip-to-body orientation matrix.
* Set hardware units to dps/g's/degrees scaling factor.
*/
inv_set_gyro_orientation_and_scale(
inv_orientation_matrix_to_scalar(gyro_pdata.orientation),
(long)gyro_fsr<<15);
inv_set_accel_orientation_and_scale(
inv_orientation_matrix_to_scalar(gyro_pdata.orientation),
(long)accel_fsr<<15);
#ifdef COMPASS_ENABLED
inv_set_compass_orientation_and_scale(
inv_orientation_matrix_to_scalar(compass_pdata.orientation),
(long)compass_fsr<<15);
#endif
/* Initialize HAL state variables. */
#ifdef COMPASS_ENABLED
hal.sensors = ACCEL_ON | GYRO_ON | COMPASS_ON;
#else
hal.sensors = ACCEL_ON | GYRO_ON;
#endif
hal.dmp_on = 0;
hal.report = 0;
hal.rx.cmd = 0;
hal.next_pedo_ms = 0;
hal.next_compass_ms = 0;
hal.next_temp_ms = 0;
/* Compass reads are handled by scheduler. */
get_tick_count(&timestamp);
/* To initialize the DMP:
* 1. Call dmp_load_motion_driver_firmware(). This pushes the DMP image in
* inv_mpu_dmp_motion_driver.h into the MPU memory.
* 2. Push the gyro and accel orientation matrix to the DMP.
* 3. Register gesture callbacks. Don't worry, these callbacks won't be
* executed unless the corresponding feature is enabled.
* 4. Call dmp_enable_feature(mask) to enable different features.
* 5. Call dmp_set_fifo_rate(freq) to select a DMP output rate.
* 6. Call any feature-specific control functions.
*
* To enable the DMP, just call mpu_set_dmp_state(1). This function can
* be called repeatedly to enable and disable the DMP at runtime.
*
* The following is a short summary of the features supported in the DMP
* image provided in inv_mpu_dmp_motion_driver.c:
* DMP_FEATURE_LP_QUAT: Generate a gyro-only quaternion on the DMP at
* 200Hz. Integrating the gyro data at higher rates reduces numerical
* errors (compared to integration on the MCU at a lower sampling rate).
* DMP_FEATURE_6X_LP_QUAT: Generate a gyro/accel quaternion on the DMP at
* 200Hz. Cannot be used in combination with DMP_FEATURE_LP_QUAT.
* DMP_FEATURE_TAP: Detect taps along the X, Y, and Z axes.
* DMP_FEATURE_ANDROID_ORIENT: Google's screen rotation algorithm. Triggers
* an event at the four orientations where the screen should rotate.
* DMP_FEATURE_GYRO_CAL: Calibrates the gyro data after eight seconds of
* no motion.
* DMP_FEATURE_SEND_RAW_ACCEL: Add raw accelerometer data to the FIFO.
* DMP_FEATURE_SEND_RAW_GYRO: Add raw gyro data to the FIFO.
* DMP_FEATURE_SEND_CAL_GYRO: Add calibrated gyro data to the FIFO. Cannot
* be used in combination with DMP_FEATURE_SEND_RAW_GYRO.
*/
dmp_load_motion_driver_firmware();
dmp_set_orientation(
inv_orientation_matrix_to_scalar(gyro_pdata.orientation));
dmp_register_tap_cb(tap_cb);
dmp_register_android_orient_cb(android_orient_cb);
/*
* Known Bug -
* DMP when enabled will sample sensor data at 200Hz and output to FIFO at the rate
* specified in the dmp_set_fifo_rate API. The DMP will then sent an interrupt once
* a sample has been put into the FIFO. Therefore if the dmp_set_fifo_rate is at 25Hz
* there will be a 25Hz interrupt from the MPU device.
*
* There is a known issue in which if you do not enable DMP_FEATURE_TAP
* then the interrupts will be at 200Hz even if fifo rate
* is set at a different rate. To avoid this issue include the DMP_FEATURE_TAP
*
* DMP sensor fusion works only with gyro at +-2000dps and accel +-2G
*/
hal.dmp_features = DMP_FEATURE_6X_LP_QUAT | DMP_FEATURE_TAP |
DMP_FEATURE_ANDROID_ORIENT | DMP_FEATURE_SEND_RAW_ACCEL | DMP_FEATURE_SEND_CAL_GYRO |
DMP_FEATURE_GYRO_CAL;
dmp_enable_feature(hal.dmp_features);
dmp_set_fifo_rate(DEFAULT_MPU_HZ);
mpu_set_dmp_state(1);
hal.dmp_on = 1;
while(1){
unsigned long sensor_timestamp;
int new_data = 0;
get_tick_count(&timestamp);
#ifdef COMPASS_ENABLED
/* We're not using a data ready interrupt for the compass, so we'll
* make our compass reads timer-based instead.
*/
if ((timestamp > hal.next_compass_ms) && !hal.lp_accel_mode &&
hal.new_gyro && (hal.sensors & COMPASS_ON)) {
hal.next_compass_ms = timestamp + COMPASS_READ_MS;
new_compass = 1;
}
#endif
/* Temperature data doesn't need to be read with every gyro sample.
* Let's make them timer-based like the compass reads.
*/
if (timestamp > hal.next_temp_ms) {
hal.next_temp_ms = timestamp + TEMP_READ_MS;
new_temp = 1;
}
if (hal.motion_int_mode) {
/* Enable motion interrupt. */
mpu_lp_motion_interrupt(500, 1, 5);
/* Notify the MPL that contiguity was broken. */
inv_accel_was_turned_off();
inv_gyro_was_turned_off();
inv_compass_was_turned_off();
inv_quaternion_sensor_was_turned_off();
/* Wait for the MPU interrupt. */
while (!hal.new_gyro) {}
/* Restore the previous sensor configuration. */
mpu_lp_motion_interrupt(0, 0, 0);
hal.motion_int_mode = 0;
}
if (!hal.sensors || !hal.new_gyro) {
continue;
}
if (hal.new_gyro && hal.lp_accel_mode) {
short accel_short;
long accel;
mpu_get_accel_reg(accel_short, &sensor_timestamp);
accel = (long)accel_short;
accel = (long)accel_short;
accel = (long)accel_short;
inv_build_accel(accel, 0, sensor_timestamp);
new_data = 1;
hal.new_gyro = 0;
} else if (hal.new_gyro && hal.dmp_on) {
short gyro, accel_short, sensors;
unsigned char more;
long accel, quat, temperature;
/* This function gets new data from the FIFO when the DMP is in
* use. The FIFO can contain any combination of gyro, accel,
* quaternion, and gesture data. The sensors parameter tells the
* caller which data fields were actually populated with new data.
* For example, if sensors == (INV_XYZ_GYRO | INV_WXYZ_QUAT), then
* the FIFO isn't being filled with accel data.
* The driver parses the gesture data to determine if a gesture
* event has occurred; on an event, the application will be notified
* via a callback (assuming that a callback function was properly
* registered). The more parameter is non-zero if there are
* leftover packets in the FIFO.
*/
dmp_read_fifo(gyro, accel_short, quat, &sensor_timestamp, &sensors, &more);
if (!more)
hal.new_gyro = 0;
if (sensors & INV_XYZ_GYRO) {
/* Push the new data to the MPL. */
inv_build_gyro(gyro, sensor_timestamp);
new_data = 1;
if (new_temp) {
new_temp = 0;
/* Temperature only used for gyro temp comp. */
mpu_get_temperature(&temperature, &sensor_timestamp);
inv_build_temp(temperature, sensor_timestamp);
}
}
if (sensors & INV_XYZ_ACCEL) {
accel = (long)accel_short;
accel = (long)accel_short;
accel = (long)accel_short;
inv_build_accel(accel, 0, sensor_timestamp);
new_data = 1;
}
if (sensors & INV_WXYZ_QUAT) {
inv_build_quat(quat, 0, sensor_timestamp);
new_data = 1;
}
} else if (hal.new_gyro) {
short gyro, accel_short;
unsigned char sensors, more;
long accel, temperature;
/* This function gets new data from the FIFO. The FIFO can contain
* gyro, accel, both, or neither. The sensors parameter tells the
* caller which data fields were actually populated with new data.
* For example, if sensors == INV_XYZ_GYRO, then the FIFO isn't
* being filled with accel data. The more parameter is non-zero if
* there are leftover packets in the FIFO. The HAL can use this
* information to increase the frequency at which this function is
* called.
*/
hal.new_gyro = 0;
mpu_read_fifo(gyro, accel_short, &sensor_timestamp,
&sensors, &more);
if (more)
hal.new_gyro = 1;
if (sensors & INV_XYZ_GYRO) {
/* Push the new data to the MPL. */
inv_build_gyro(gyro, sensor_timestamp);
new_data = 1;
if (new_temp) {
new_temp = 0;
/* Temperature only used for gyro temp comp. */
mpu_get_temperature(&temperature, &sensor_timestamp);
inv_build_temp(temperature, sensor_timestamp);
}
}
if (sensors & INV_XYZ_ACCEL) {
accel = (long)accel_short;
accel = (long)accel_short;
accel = (long)accel_short;
inv_build_accel(accel, 0, sensor_timestamp);
new_data = 1;
}
}
#ifdef COMPASS_ENABLED
if (new_compass) {
short compass_short;
long compass;
new_compass = 0;
/* For any MPU device with an AKM on the auxiliary I2C bus, the raw
* magnetometer registers are copied to special gyro registers.
*/
if (!mpu_get_compass_reg(compass_short, &sensor_timestamp)) {
compass = (long)compass_short;
compass = (long)compass_short;
compass = (long)compass_short;
/* NOTE: If using a third-party compass calibration library,
* pass in the compass data in uT * 2^16 and set the second
* parameter to INV_CALIBRATED | acc, where acc is the
* accuracy from 0 to 3.
*/
inv_build_compass(compass, 0, sensor_timestamp);
}
new_data = 1;
}
#endif
if (new_data) {
inv_execute_on_data();
/* This function reads bias-compensated sensor data and sensor
* fusion outputs from the MPL. The outputs are formatted as seen
* in eMPL_outputs.c. This function only needs to be called at the
* rate requested by the host.
*/
// MX_USART1_UART_Init();
// MX_USART2_UART_Init();
// MX_USART3_UART_Init();
//printf("01\r\n");
read_from_mpl();
OLED_Refresh_Gram();
}
}
}
void mdelay(unsigned long nTime)
{
HAL_Delay(nTime);
}
int get_tick_count(unsigned long *count)
{
count = HAL_GetTick();
return 0;
}</code></pre>
<p> </p>
</li>
</ul>
<p><strong><span style="color:#f1c40f;"><span style="font-size:20px;">三、视频演示</span></span></strong></p>
<p>1、基本视频演示(2min)</p>
<p>69cbe15d05a38e69a3a35d2726e8b37f<br />
</p>
<p>2、详细步数测试(30s)</p>
<p>a29398ada54085bebf8d26aef8dc58c9<br />
</p>
<p>3、在官方上的视频讲解</p>
<p>见链接:<a href="https://training.eeworld.com.cn/course/68166">【STM32L476RG测评】——最终成品:基于MPU6050的计步器-EEWORLD大学堂</a></p>
<p><span style="font-size:20px;"><strong><span style="color:#f1c40f;">四、总结</span></strong></span></p>
<p>1、先说本次测评学到的东西,关键是MPU6050,说实在,计算什么的我也不是很熟悉,本次也只是一个简单的入门,而且时间和篇幅有限,我也不可能在报告里面把计算部分说完,我的唯一建议是去看官方给的那三个文档,看完后就大概知道他的官方库都是在干什么的了,虽然有不少hal和标准冲突的问题,不过最终还是成功移植了官方的库。</p>
<p>2、最终完成的作品相当的简陋,最大的问题有几个</p>
<ul>
<li>板子本身尺寸就大</li>
<li>各个模块通过杜邦线的连接非常麻烦,且容易出错</li>
<li>供电电源没做好,本来打算加串口屏的,没有电源啥也不是</li>
<li>个人感觉,说实在,如果想做一个完整的成品,我觉得应该是最小核心板+MPU6050+几个通信外设</li>
<li>而且视频里因为杜邦线,演示都不好演示,传感器最好还是绑定在一个板子上面</li>
<li>也就是说,其实应该画个扩展板,上面包含传感器+几个通信外设,然后这个板子直接连接上去就好了。</li>
</ul>
<p>3、开发过程中的遇到的问题及探索出的解决方案;</p>
<p>说实在,问题我没整合,都是那里要注意,就在那里写上了,本次DMP库其实还好,移植之后,报错基本上都是hal和官方库不兼容+一部分函数的冲突导致的,修修补补就好了。<br />
4、代码分析见文章主要内容,基本上都有,这些函数都不算难,其他的函数你也不用考虑,基本上都是官方算法<br />
5、测评总结和对厂商的建议等</p>
<ul>
<li>首先是计步器的便携性,STM32L476RG是一款低功耗微控制器,适用于便携式设备。</li>
<li>MPU6050传感器是用于测量加速度和角速度的,这些都是主要参数,同时官方库可以提供步数计数所需的算法和数据。</li>
<li>HC-05蓝牙模块允许与其他设备通信,以实现数据传输或远程监控。</li>
<li>使用USART屏幕和SPI OLED显示屏提供了用户友好的界面,可以显示步数和其他相关信息。</li>
<li>然后我这一次没有加休眠,毕竟能源管理上,考虑低功耗设计是很重要的,最好还是确保在不使用时进入休眠模式,延长电池寿命。</li>
<li>厂商建议,说实在stm32没什么好吐槽的,hal和标准库什么的各有优劣,唯一不满的就是板子太大</li>
<li>但是因为上面的stlink,这个测评板,也能理解,真要做产品和实物还是得自己画pcb。</li>
</ul>
<p>最后谢谢各位的观看,希望本次测评的所有内容对大家有所帮助,感谢本次eeworld提供的板卡测评机会</p>
<p>PS:后面应该还有几天,看看有没有时间吧,我打算测测这个板卡的ADC和DAC,还有最近在做键盘,拿过这个板子跑过hid和4*4矩阵键盘测试,有时间的话再更新吧。</p>
<div></div>
</div><script> var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;" style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
if(parseInt(discuz_uid)==0){
(function($){
var postHeight = getTextHeight(400);
$(".showpostmsg").html($(".showpostmsg").html());
$(".showpostmsg").after(loginstr);
$(".showpostmsg").css({height:postHeight,overflow:"hidden"});
})(jQuery);
} </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script> 大佬很有经验呀,希望继续在论坛多分享好的资料、经验!
页:
[1]