- void MX_GPIO_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStruct = {0};
- // Enable GPIO Clocks
- __DAL_RCM_GPIOA_CLK_ENABLE();
- __DAL_RCM_GPIOB_CLK_ENABLE();
- __DAL_RCM_GPIOE_CLK_ENABLE();
- // Configure GPIO pins for buttons (PA0, PA1)
- GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1;
- GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- DAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
- // Configure GPIO pin for PWM (PB4)
- GPIO_InitStruct.Pin = GPIO_PIN_4;
- GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
- GPIO_InitStruct.Alternate = GPIO_AF1_TMR1;
- DAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
- // EXTI interrupt init
- DAL_NVIC_SetPriority(EXTI0_IRQn, 2, 0);
- DAL_NVIC_EnableIRQ(EXTI0_IRQn);
- DAL_NVIC_SetPriority(EXTI1_IRQn, 2, 0);
- DAL_NVIC_EnableIRQ(EXTI1_IRQn);
- }
I2C初始化部分
- I2C_HandleTypeDef hi2c1;
- I2C_HandleTypeDef hi2c2;
- void MX_I2C1_Init(void) {
- hi2c1.Instance = I2C1;
- hi2c1.Init.ClockSpeed = 100000;
- hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
- hi2c1.Init.OwnAddress1 = 0;
- hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
- hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
- hi2c1.Init.OwnAddress2 = 0;
- hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
- hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
- DAL_I2C_Init(&hi2c1);
- }
- void MX_I2C2_Init(void) {
- hi2c2.Instance = I2C2;
- hi2c2.Init.ClockSpeed = 100000;
- hi2c2.Init.DutyCycle = I2C_DUTYCYCLE_2;
- hi2c2.Init.OwnAddress1 = 0;
- hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
- hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
- hi2c2.Init.OwnAddress2 = 0;
- hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
- hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
- DAL_I2C_Init(&hi2c2);
- }
ADC 初始化和功能设置
- ADC_HandleTypeDef hadc1;
- void MX_ADC1_Init(void) {
- ADC_ChannelConfTypeDef sConfig = {0};
- hadc1.Instance = ADC1;
- hadc1.Init.ScanConvMode = DISABLE;
- hadc1.Init.ContinuousConvMode = DISABLE;
- hadc1.Init.DiscontinuousConvMode = DISABLE;
- hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
- hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
- hadc1.Init.NbrOfConversion = 1;
- DAL_ADC_Init(&hadc1);
- sConfig.Channel = ADC_CHANNEL_1;
- sConfig.Rank = 1U;
- sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
- DAL_ADC_ConfigChannel(&hadc1, &sConfig);
- }
- u16 Get_Adc(ADC_HandleTypeDef *hadc1)
- {
- while(!__DAL_ADC_GET_FLAG(&hadc1, ADC_FLAG_EOC ));//等待转换结束
- return DAL_ADC_GetValue(&hadc1); //返回最近一次ADC1规则组的转换结果
- }
- u16 Get_Adc_Average(ADC_HandleTypeDef *hadc1, u8 times) {
- u32 temp_sum = 0;
- u16 adc_value;
- u8 t;
- for(t = 0; t < times; t++) {
- adc_value = Get_Adc(hadc1);
- temp_sum += adc_value;
- DAL_Delay(5);
- }
- return (times > 1) ? (u16)(temp_sum / times) : adc_value;
- }
PWM 初始化和功能设置
- TMR_HandleTypeDef hTMR1;
- void MX_TMR1_Init(void) {
- TMR_OC_InitTypeDef sConfigOC = {0};
- hTMR1.Instance = TMR1;
- hTMR1.Init.Prescaler = 0;
- hTMR1.Init.CounterMode = TMR_COUNTERMODE_UP;
- hTMR1.Init.Period = 255;
- hTMR1.Init.ClockDivision = TMR_CLOCKDIVISION_DIV1;
- hTMR1.Init.RepetitionCounter = 0;
- if (DAL_TMR_PWM_Init(&hTMR1) != DAL_OK) {
- DAL_ErrorHandler();
- }
- sConfigOC.OCMode = TMR_OCMODE_PWM1;
- sConfigOC.Pulse = 0;
- sConfigOC.OCPolarity = TMR_OCPOLARITY_HIGH;
- sConfigOC.OCFastMode = TMR_OCFAST_DISABLE;
- if (DAL_TMR_PWM_ConfigChannel(&hTMR1, &sConfigOC, TMR_CHANNEL_1) != DAL_OK) {
- DAL_ErrorHandler();
- }
- DAL_TMR_PWM_MspInit(&hTMR1);
- }
- // 设置 PWM 占空比和频率的函数
- void setPWM(uint32_t dutyCycle, uint32_t frequency) {
- uint32_t period = DAL_RCC_GetHCLKFreq() / frequency;
- __DAL_TMR_SET_AUTORELOAD(&hTMRx, period);
- __DAL_TMR_SET_COMPARE(&hTMRx, TMR_CHANNEL_x, dutyCycle);
- __DAL_TMR_ENABLE(&hTMRx);
- }
- // 5. UART Initialization
- UART_HandleTypeDef huart1;
- void MX_USART1_UART_Init(void) {
- huart1.Instance = USART1;
- huart1.Init.BaudRate = 115200;
- huart1.Init.WordLength = UART_WORDLENGTH_8B;
- huart1.Init.StopBits = UART_STOPBITS_1;
- huart1.Init.Parity = UART_PARITY_NONE;
- huart1.Init.Mode = UART_MODE_TX_RX;
- huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
- huart1.Init.OverSampling = UART_OVERSAMPLING_16;
- DAL_UART_Init(&huart1);
- }
马达控制功能函数
- // PWM 参数设置函数
- void set_soft_mode_params(void) {
- setPWM(50, 1000); // 设置占空比为 50%,频率为 1000Hz
- }
- void set_normal_mode_params(void) {
- setPWM(80, 1500); // 设置占空比为 80%,频率为 1500Hz
- }
- void set_strong_mode_params(void) {
- setPWM(100, 2000); // 设置占空比为 100%,频率为 2000Hz
- }
- // 渐进模式启动函数
- void start_progressive_mode() {
- uint32_t step = 10; // 渐进模式的步长
- uint32_t currentDutyCycle = 0; // 渐进模式的当前占空比
- while (currentDutyCycle <= 100) { // 渐进模式持续到占空比达到 100%
- setPWM(currentDutyCycle, 1000); // 设置当前占空比和频率
- currentDutyCycle += step; // 增加占空比
- DAL_Delay(100);
- }
- }
传感器基于模式启动函数
- void start_sensor_based_mode() {
- process_sensor_data_and_set_motor_strength();
- DAL_Delay(100);
- }
- }
- void set_stop_mode_params(void) {
- setPWM(100, 2000); // 设置占空比为 100%,频率为 2000Hz
- }
Button 检测功能
按键有按键一,按键二,分别是pa0 pa1,按下按键1时候菜单选中项向上一个,按下按键2时候,菜单选中项向下一个,两个同时按下一下为选中,并启动相应功能,
短时间按下两次为返回上级菜单,如果没有上级菜单则停止功能,同时按下两秒,则停止并停留在当前选项。菜单分为功能菜单和测试菜单,
功能菜单分为轻柔、普通、强力三种模式来驱动振动马达,测试菜单分为强弱渐进和依靠MPU6050和薄膜压力传感器读数为权重来计算强度;
功能菜单设置振动马达的力度基数,测试菜单来确定变化逻辑
OLED 显示功能
- typedef enum {
- MENU_FUNCTION,
- MENU_TEST,
- MENU_FUNCTION_SOFT,
- MENU_FUNCTION_NORMAL,
- MENU_FUNCTION_STRONG,
- MENU_TEST_PROGRESSIVE,
- MENU_TEST_SENSOR_BASED
- } Menu;
- Menu current_menu = MENU_FUNCTION;
- Menu previous_menu = MENU_FUNCTION_SOFT;
- void display_menu(Menu menu) {
- ssd1306_Fill(SSD1306_COLOR_BLACK);
- ssd1306_SetCursor(0, 0);
- ssd1306_WriteString("Menu:", SSD1306_FONT_6X8, SSD1306_COLOR_WHITE);
- ssd1306_SetCursor(0, 16);
- switch (menu) {
- case MENU_FUNCTION:
- ssd1306_WriteString("Function Menu", SSD1306_FONT_6X8, SSD1306_COLOR_WHITE);
- break;
- case MENU_TEST:
- ssd1306_WriteString("Test Menu", SSD1306_FONT_6X8, SSD1306_COLOR_WHITE);
- break;
- case MENU_FUNCTION_SOFT:
- ssd1306_WriteString("Soft Function", SSD1306_FONT_6X8, SSD1306_COLOR_WHITE);
- break;
- case MENU_FUNCTION_NORMAL:
- ssd1306_WriteString("Normal Function", SSD1306_FONT_6X8, SSD1306_COLOR_WHITE);
- break;
- case MENU_FUNCTION_STRONG:
- ssd1306_WriteString("Strong Function", SSD1306_FONT_6X8, SSD1306_COLOR_WHITE);
- break;
- case MENU_TEST_PROGRESSIVE:
- ssd1306_WriteString("Progressive Test", SSD1306_FONT_6X8, SSD1306_COLOR_WHITE);
- break;
- case MENU_TEST_SENSOR_BASED:
- ssd1306_WriteString("Sensor-Based Test", SSD1306_FONT_6X8, SSD1306_COLOR_WHITE);
- break;
- }
- ssd1306_UpdateScreen();
- }
- void handle_buttons(void) {
- if (button1_pressed) {
- button1_pressed = 0;
- if (buttons_held) {
- current_menu = MENU_FUNCTION;
- } else {
- switch (current_menu) {
- case MENU_FUNCTION:
- current_menu = previous_menu;
- break;
- case MENU_TEST:
- current_menu = previous_menu;
- break;
- default:
- previous_menu = current_menu;
- current_menu = MENU_FUNCTION;
- break;
- }
- }
- }
- if (button2_pressed) {
- button2_pressed = 0;
- switch (current_menu) {
- case MENU_FUNCTION:
- current_menu = MENU_TEST;
- break;
- case MENU_TEST:
- current_menu = MENU_FUNCTION_SOFT;
- break;
- case MENU_FUNCTION_SOFT:
- current_menu = MENU_FUNCTION_NORMAL;
- break;
- case MENU_FUNCTION_NORMAL:
- current_menu = MENU_FUNCTION_STRONG;
- break;
- case MENU_FUNCTION_STRONG:
- current_menu = MENU_TEST_PROGRESSIVE;
- break;
- case MENU_TEST_PROGRESSIVE:
- current_menu = MENU_TEST_SENSOR_BASED;
- break;
- case MENU_TEST_SENSOR_BASED:
- current_menu = MENU_FUNCTION;
- break;
- }
- }
- display_menu(current_menu);
- }
mpu6050陀螺仪初始化和数据读取功能
- #include <math.h>
- <div style="color: #cccccc;background-color: #1f1f1f;font-family: 'Droid Sans Mono', 'monospace', monospace;font-weight: normal;font-size: 14px;line-height: 19px;white-space: pre;"><div><span style="color: #c586c0;">#define</span><span style="color: #569cd6;"> </span><span style="color: #569cd6;">MPU6050_ADDR</span><span style="color: #569cd6;"> </span><span style="color: #b5cea8;">0x68</span></div><div><span style="color: #c586c0;">#define</span><span style="color: #569cd6;"> </span><span style="color: #569cd6;">MPU6050_REG_PWR_MGMT_1</span><span style="color: #569cd6;"> </span><span style="color: #b5cea8;">0x6B</span></div><div><span style="color: #c586c0;">#define</span><span style="color: #569cd6;"> </span><span style="color: #569cd6;">MPU6050_REG_SMPLRT_DIV</span><span style="color: #569cd6;"> </span><span style="color: #b5cea8;">0x19</span></div><div><span style="color: #c586c0;">#define</span><span style="color: #569cd6;"> </span><span style="color: #569cd6;">MPU6050_REG_CONFIG</span><span style="color: #569cd6;"> </span><span style="color: #b5cea8;">0x1A</span></div><div><span style="color: #c586c0;">#define</span><span style="color: #569cd6;"> </span><span style="color: #569cd6;">MPU6050_REG_GYRO_CONFIG</span><span style="color: #569cd6;"> </span><span style="color: #b5cea8;">0x1B</span></div><div><span style="color: #c586c0;">#define</span><span style="color: #569cd6;"> </span><span style="color: #569cd6;">MPU6050_REG_ACCEL_CONFIG</span><span style="color: #569cd6;"> </span><span style="color: #b5cea8;">0x1</span></div><div><span style="color: #c586c0;">#define</span><span style="color: #569cd6;"> </span><span style="color: #569cd6;">MPU6050_REG_ACCEL_XOUT_H</span><span style="color: #569cd6;"> </span><span style="color: #b5cea8;">0x3B</span></div><div><span style="color: #c586c0;">#define</span><span style="color: #569cd6;"> </span><span style="color: #569cd6;">MPU6050_ACCEL_XOUT_L</span><span style="color: #569cd6;"> </span><span style="color: #b5cea8;">0x3</span></div><div><span style="color: #c586c0;">#define</span><span style="color: #569cd6;"> </span><span style="color: #569cd6;">MPU6050_ACCEL_YOUT_H</span><span style="color: #569cd6;"> </span><span style="color: #b5cea8;">0x3D</span></div><div><span style="color: #c586c0;">#define</span><span style="color: #569cd6;"> </span><span style="color: #569cd6;">MPU6050_ACCEL_YOUT_L</span><span style="color: #569cd6;"> </span><span style="color: #b5cea8;">0x3E</span></div><div><span style="color: #c586c0;">#define</span><span style="color: #569cd6;"> </span><span style="color: #569cd6;">MPU6050_ACCEL_ZOUT_H</span><span style="color: #569cd6;"> </span><span style="color: #b5cea8;">0x3F</span></div><div><span style="color: #c586c0;">#define</span><span style="color: #569cd6;"> </span><span style="color: #569cd6;">MPU6050_ACCEL_ZOUT_L</span><span style="color: #569cd6;"> </span><span style="color: #b5cea8;">0x40</span></div><div><span style="color: #c586c0;">#define</span><span style="color: #569cd6;"> </span><span style="color: #569cd6;">MPU6050_TEMP_OUT_H</span><span style="color: #569cd6;"> </span><span style="color: #b5cea8;">0x41</span></div><div><span style="color: #c586c0;">#define</span><span style="color: #569cd6;"> </span><span style="color: #569cd6;">MPU6050_TEMP_OUT_L</span><span style="color: #569cd6;"> </span><span style="color: #b5cea8;">0x42</span></div><div><span style="color: #c586c0;">#define</span><span style="color: #569cd6;"> </span><span style="color: #569cd6;">MPU6050_GYRO_XOUT_H</span><span style="color: #569cd6;"> </span><span style="color: #b5cea8;">0x43</span></div><div><span style="color: #c586c0;">#define</span><span style="color: #569cd6;"> </span><span style="color: #569cd6;">MPU6050_GYRO_XOUT_L</span><span style="color: #569cd6;"> </span><span style="color: #b5cea8;">0x44</span></div><div><span style="color: #c586c0;">#define</span><span style="color: #569cd6;"> </span><span style="color: #569cd6;">MPU6050_GYRO_YOUT_H</span><span style="color: #569cd6;"> </span><span style="color: #b5cea8;">0x45</span></div><div><span style="color: #c586c0;">#define</span><span style="color: #569cd6;"> </span><span style="color: #569cd6;">MPU6050_GYRO_YOUT_L</span><span style="color: #569cd6;"> </span><span style="color: #b5cea8;">0x46</span></div><div><span style="color: #c586c0;">#define</span><span style="color: #569cd6;"> </span><span style="color: #569cd6;">MPU6050_GYRO_ZOUT_H</span><span style="color: #569cd6;"> </span><span style="color: #b5cea8;">0x47</span></div><div><span style="color: #c586c0;">#define</span><span style="color: #569cd6;"> </span><span style="color: #569cd6;">MPU6050_GYRO_ZOUT_L</span><span style="color: #569cd6;"> </span><span style="color: #b5cea8;">0x48</span></div><div><span style="color: #c586c0;">#define</span><span style="color: #569cd6;"> </span><span style="color: #569cd6;">MPU6050_WHO_AM_I</span><span style="color: #569cd6;"> </span><span style="color: #b5cea8;">0x75</span></div></div>
- void MPU6050_Init(void) {
- uint8_t check;
- uint8_t Data;
- // 检查设备ID
- DAL_I2C_Mem_Read(&hi2c1, MPU6050_ADDR, 0x75, 1, &check, 1, 1000);
- if (check == 0x68) {
- // 唤醒MPU6050
- Data = 0;
- DAL_I2C_Mem_Write(&hi2c1, MPU6050_ADDR, MPU6050_REG_PWR_MGMT_1, 1, &Data, 1, 1000);
- // 设置采样率
- Data = 7;
- DAL_I2C_Mem_Write(&hi2c1, MPU6050_ADDR, MPU6050_REG_SMPLRT_DIV, 1, &Data, 1, 1000);
- // 设置加速度计和陀螺仪
- Data = 0;
- DAL_I2C_Mem_Write(&hi2c1, MPU6050_ADDR, MPU6050_REG_ACCEL_CONFIG, 1, &Data, 1, 1000);
- DAL_I2C_Mem_Write(&hi2c1, MPU6050_ADDR, MPU6050_REG_GYRO_CONFIG, 1, &Data, 1, 1000);
- }
- }
- void MPU6050_Read_Accel(float* Ax, float* Ay, float* Az) {
- uint8_t Rec_Data[6];
- int16_t Accel_X_RAW, Accel_Y_RAW, Accel_Z_RAW;
- // 读取加速度计数据
- DAL_I2C_Mem_Read(&hi2c1, MPU6050_ADDR, MPU6050_REG_ACCEL_XOUT_H, 1, Rec_Data, 6, 1000);
- Accel_X_RAW = (int16_t)(Rec_Data[0] << 8 | Rec_Data[1]);
- Accel_Y_RAW = (int16_t)(Rec_Data[2] << 8 | Rec_Data[3]);
- Accel_Z_RAW = (int16_t)(Rec_Data[4] << 8 | Rec_Data[5]);
- *Ax = Accel_X_RAW / 16384.0;
- *Ay = Accel_Y_RAW / 16384.0;
- *Az = Accel_Z_RAW / 16384.0;
- }
- void MPU6050_Read_Gyro(float* Gx, float* Gy, float* Gz) {
- uint8_t Rec_Data[6];
- int16_t Gyro_X_RAW, Gyro_Y_RAW, Gyro_Z_RAW;
- // 读取陀螺仪数据
- DAL_I2C_Mem_Read(&hi2c1, MPU6050_ADDR, MPU6050_REG_GYRO_XOUT_H, 1, Rec_Data, 6, 1000);
- Gyro_X_RAW = (int16_t)(Rec_Data[0] << 8 | Rec_Data[1]);
- Gyro_Y_RAW = (int16_t)(Rec_Data[2] << 8 | Rec_Data[3]);
- Gyro_Z_RAW = (int16_t)(Rec_Data[4] << 8 | Rec_Data[5]);
- *Gx = Gyro_X_RAW / 131.0;
- *Gy = Gyro_Y_RAW / 131.0;
- *Gz = Gyro_Z_RAW / 131.0;
- }
- // 计算倾角
- void calculate_tilt(float Ax, float Ay, float Az, float* roll, float* pitch) {
- *roll = atan2(Ay, Az) * 180.0 / M_PI;
- *pitch = atan2(-Ax, sqrt(Ay * Ay + Az * Az)) * 180.0 / M_PI;
- }
压力薄膜传感器初始化和读数函数
- #define PRESS_MIN 20
- #define PRESS_MAX 6000
- #define VOLTAGE_MIN 150
- #define VOLTAGE_MAX 3300
- uint8_t state = 0;
- uint16_t val = 0;
- uint16_t value_AD = 0;
- long PRESS_AO = 0;
- int VOLTAGE_AO = 0;
- long map(long x, long in_min, long in_max, long out_min, long out_max) {
- return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
- }
- float readPressureSensor() {
- uint16_t value_AD = 0;
- uint16_t voltage_AO = 0;
- float pressure_AO = 0;
- value_AD = Get_Adc_Average(1, 10); // 10次平均值
- voltage_AO = map(value_AD, 0, 4095, 0, 3300);
- if (voltage_AO < VOLTAGE_MIN) {
- pressure_AO = 0;
- } else if (voltage_AO > VOLTAGE_MAX) {
- pressure_AO = PRESS_MAX;
- } else {
- pressure_AO = map(voltage_AO, VOLTAGE_MIN, VOLTAGE_MAX, PRESS_MIN, PRESS_MAX);
- }
- return pressure_AO;
- }
权重公式部分
通过压力的传感器和陀螺仪来自动选择振动的模式,
在原有的强力、普通、轻柔的基础上进行加强或者减弱,
比如压力传感器感受的压力增大就增加强度,直到到达上限制,比如陀螺仪感受到的角度变
化判断在脸部薄弱位置就减轻强度,陀螺仪的优先级高于压力传感器,如果在薄弱位置,
强度上限会减小,并且适当减小当前强度,如果在下颌、额头等位置就会适当增加当前强度,
通过一个权重公式,通过接收到的mpu6050的数据和压力传感器数据,并来确定当下的强度
// 压力传感器权重:PP
// 角度权重:AA
// 总强度权重:WW
// 振动强度:SS
// 权重公式逻辑
// 设定陀螺仪感应到的面部区域:例如,面部薄弱区域(脸颊)与强度较高区域(下颌、额头)。
// 陀螺仪感应到的角度范围与区域映射:
// 面部薄弱区域:角度范围A1 (比如 -45° < 角度 < 45°)
// 强度较高区域:角度范围A2 (比如 角度 < -45° 或者 角度 > 45°)
// 根据压力传感器和陀螺仪的权重公式调整振动强度:
// W=P×KP+A×KAW=P×KP+A×KA
// 如果在面部薄弱区域,则总强度权重 WW 减小。
// 如果在强度较高区域,则总强度权重 WW 增加。
- #define KP 0.5 // 压力传感器权重
- #define KA 0.5 // 角度权重
- #define BASE_INTENSITY 50
- #define MAX_INTENSITY 255
- float calculate_vibration_intensity(float angle, float pressure) {
- float intensity;
- if (-45 <= angle && angle <= 45) {
- // 面部薄弱区域,减少强度
- intensity = (pressure * KP + angle * KA) * 0.5; // 减少强度权重
- } else {
- // 强度较高区域,增加强度
- intensity = (pressure * KP + angle * KA) * 1.5; // 增加强度权重
- }
- // 确保强度在范围内
- if (intensity > MAX_INTENSITY) {
- intensity = MAX_INTENSITY;
- } else if (intensity < BASE_INTENSITY) {
- intensity = BASE_INTENSITY;
- }
- return intensity;
- }
- void complementary_filter(float* roll, float* pitch, float gx, float gy, float gz, float dt) {
- static float roll_acc = 0, pitch_acc = 0;
- float roll_gyro = *roll + gx * dt;
- float pitch_gyro = *pitch + gy * dt;
- float Ax, Ay, Az;
- MPU6050_Read_Accel(&Ax, &Ay, &Az);
- calculate_tilt(Ax, Ay, Az, &roll_acc, &pitch_acc);
- *roll = ALPHA * roll_gyro + (1.0 - ALPHA) * roll_acc;
- *pitch = ALPHA * pitch_gyro + (1.0 - ALPHA) * pitch_acc;
- }
- // 处理接收到的数据并确定强度的函数
- void process_sensor_data_and_set_motor_strength(void) {
- // 读取加速度和陀螺仪数据
- float Ax, Ay, Az;
- float Gx, Gy, Gz;
- MPU6050_Read_Accel(&Ax, &Ay, &Az);
- MPU6050_Read_Gyro(&Gx, &Gy, &Gz);
- // 计算倾角
- float roll, pitch;
- calculate_tilt(Ax, Ay, Az, &roll, &pitch);
- // 使用互补滤波器融合数据
- float dt = 0.01; // 假设每10ms调用一次
- complementary_filter(&roll, &pitch, Gx, Gy, Gz, dt);
- // 读取压力传感器数据
- float tmp_pressure = readPressureSensor();
- // 计算振动马达强度
- float intensity = calculate_vibration_intensity(roll,tmp_pressure);
- // 设置振动马达的PWM占空比
- set_motor_strength((uint8_t)intensity);
- }
// Main Function
- int main(void) {
- DAL_Init();
- SystemClock_Config();
- MX_GPIO_Init();
- MX_I2C1_Init();
- MX_I2C2_Init();
- MX_ADC1_Init();
- MX_TMR1_Init();
- MX_TMR2_Init();
- MX_USART1_UART_Init();
- ssd1306_Init();
-
- // 显示初始模式
- display_mode(current_mode);
- // 启动PWM
- DAL_TMR_PWM_Start(&hTMR1, TMR_CHANNEL_1);
- // 主循环
- while (1) {
- // 处理按键
- handle_button_press();
-
- }
- }
定时器配置
- TMR_HandleTypeDef htmr2;
- void MX_TMR2_Init(void) {
- htmr2.Instance = TMR2;
- htmr2.Init.Prescaler = 8399;
- htmr2.Init.CounterMode = TMR_COUNTERMODE_UP;
- htmr2.Init.Period = 9999;
- htmr2.Init.ClockDivision = TMR_CLOCKDIVISION_DIV1;
- htmr2.Init.AutoReloadPreload = TMR_AUTORELOAD_PRELOAD_DISABLE;
- DAL_TMR_Base_Init(&htmr2);
- DAL_TMR_Base_Start_IT(&htmr2);
- DAL_NVIC_SetPriority(TMR2_IRQn, 0, 0);
- DAL_NVIC_EnableIRQ(TMR2_IRQn);
- }
- // 定时器中断服务函数
- float roll, pitch, gx, gy, gz, dt = 0.01;
- void TMR2_IRQHandler(void) {
- if (__DAL_TMR_GET_FLAG(&htmr2, TMR_FLAG_UPDATE) != RESET) {
- if (__DAL_TMR_GET_IT_SOURCE(&htmr2, TMR_IT_UPDATE) != RESET) {
- __DAL_TMR_CLEAR_IT(&htmr2, TMR_IT_UPDATE);
- process_sensor_data_and_set_motor_strength();
- }
- }
- }