【手搓固定翼fpv头追研究笔记】(二) 姿态解算及状态灯

前言已明姿器之驱,此文接以前之未成,成姿解之法,加之灯器

介绍

本文会补充完上篇未移植的madgwick算法,并且使用ws2812b作为状态显示

Madgwick:小头脑亦能解难题

姿态解算算法介绍

为什么要用madgwick而不用陀螺仪自带的dmp?
其一,madgwick对于mcu性能的要求偏低
其二,dmp只能对mpu6050的6轴数据解算,而我们要用到9轴数据融合

算法库的移植与魔改

关于Madgwick算法的具体介绍, 网上已经有很多了,比如这个,此处不做复述,网上所有的madgwick算法基本过程都一模一样,主要区别在与积分部分与单位转换的不同,不过都大差不差
madgwick的积分以微秒为单位在时间上进行积分,在我在网上冲浪的找到的大部分madgwick库(比如arduino的库还有各种51上面用的),都是需要一个固定的运行频率,但对于我们这个项目裸机开发而言,有些时候难以保证运行周期不变,在一阵瞎摸索之后,找到了这一个解决方式:

HAL_GetTick();

没错,就是这个函数,其返回值为一个32位无符号整数,系统自启动以来经过的毫秒数,那么我们只需要这样,就能得到积分所需的delta time,即dt:

uint32_t madgwick_timer=0;

float dt = (float)(HAL_GetTick() - madgwick_timer) / 1000.0f;
madgwick_timer = HAL_GetTick();

上述代码,将上一个解算周期时的系统毫秒数存储为madgwick_timer,而后与本周期进行时的毫秒数做差,除以1000得到微秒,而后对madgwick_timer进行更新,以此得到积分所需的dt

小tip:陀螺仪量程修改

在朋友的测试下,加速度计量程如果取±2g,那么可能会因突然的剧烈撞击导致姿态发生异常,所以我们需要加大陀螺仪量程为±8g:
经过查询寄存器定义可知,加速度计的配置寄存器地址为 0x1C,本寄存器的第四五位用于配置加速度量程,这两位00对应±2g,01对应±4g,10对应±8g,11对应±16g,所以我们需要将该寄存器配置为0001 0000,此时量程为±8g:

代码修改:
在mpu6050的初始化函数中能找到配置加速度计量程的代码:

MPU_I2C_Write(MPU6050_Write_Addr, MPU_Accel_Addr, (Set_Accel_Range << 3));

Set_Accel_Range就是范围的配置,这里我懒直接修改了这里的值而没有去修改宏定义(好孩子别学):

MPU_I2C_Write(MPU6050_Write_Addr, MPU_Accel_Addr, (2 << 3)); 

这样量程就配置好了,但是在校准部分,我们z轴上减去的重力加速度值就不对了
当量程是±2g时,重力加速度1g对应值为 (0xffff>> 2),而当量程为±8g时,因为量程扩大了4倍,所以重力加速度对应值需要再右移2位,即(0xffff >> 4),具体代码如下,在MPU6050_calibrate(void)这个函数中,如此修改:

az_l = temp[2] / CL_cnt - (0xffff >> 2);

改为

az_l = temp[2] / CL_cnt - (0xffff >> 4);

OK,如此一来,基本准备都准备好,最后需要修改的就是单位的转化
对于加速度计:量程为±8g,那么 1g对应原始数据为(32768 / 8)=4096,原始数据转换为以g为单位即为
a = a_raw / 4096 即 a = a_raw * 0.000244
对于陀螺仪:量程为±2000°/s,灵敏度为:32767/2000 = 16.40 °/s,则
g = (g_raw / 16.40 °/s ) * 2*π / 360° 即 g = g_raw * 0.001064

废话不多说,直接上代码:(这里先使用6轴,磁力计的换算和数据融合放在磁力计校准那一张)
Madgwick.h:

#ifndef MADGWICK_H
#define MADGWICK_H
#include "main.h"

typedef struct AttiAgls{
    float roll;
    float pitch;
    float yaw;
}AttiAgls;

extern float Pitch, Roll, Yaw;
extern float q0, q1, q2, q3;

void MadgwickAHRSInit(void);
void MadgwickAHRSupdate_6(short ax_raw, short ay_raw, short az_raw, short gx_raw, short gy_raw, short gz_raw);
void MadgwickAHRSupdate_9(short ax_raw, short ay_raw, short az_raw, short gx_raw, short gy_raw, short gz_raw, short mx_raw, short my_raw, short mz_raw);

void Filter_Get_AttitudeAngles(AttiAgls *angles);

#endif //MADGWICK_H

Madgwick.c

#include "Madgwick.h"
#include <math.h>

#define beta 0.1f
#define GYRO_K 0.001064f
#define ACCEL_K 0.000244f
#define RAD_TO_DEG 57.295780f
#define DEG_TO_RAD 0.017453f

float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f;	// quaternion of sensor frame relative to auxiliary frame
float Pitch = 0.0f, Roll = 0.0f, Yaw = 0.0f;

uint32_t madgwick_timer=0;

/**
 * @brief   启动姿态解算
 * */
void MadgwickAHRSInit(void)
{
    madgwick_timer = HAL_GetTick();
}

//---------------------------------------------------------------------------------------------------
// Fast inverse square-root 来自《雷神之锤3》
// See: http://en.wikipedia.org/wiki/Fast_inverse_square_root

float invSqrt(float number)
{
    long i;
    float x2, y;
    const float threehalfs = 1.5F;

    x2 = number * 0.5F;
    y  = number;
    i  = * ( long * ) &y;                       // evil floating point bit level hacking
    i  = 0x5f3759df - ( i >> 1 );               // what the fuck?
    y  = * ( float * ) &i;
    y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration
//  y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed

    return y;
}

//---------------------------------------------------------------------------------------------------
// AHRS algorithm update
//---------------------------------------------------------------------------------------------------
// IMU algorithm update

void MadgwickAHRSupdate_6(short ax_raw, short ay_raw, short az_raw, short gx_raw, short gy_raw, short gz_raw)
{
    float ax, ay, az;
    float gx, gy, gz;
    float recipNorm;
    float s0, s1, s2, s3;
    float qDot1, qDot2, qDot3, qDot4;
    float _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2 ,_8q1, _8q2, q0q0, q1q1, q2q2, q3q3;

    //获取delta t用于积分
    float dt = (float)(HAL_GetTick() - madgwick_timer) / 1000.0f;
    madgwick_timer = HAL_GetTick();

    ax = (float)ax_raw * ACCEL_K;
    ay = (float)ay_raw * ACCEL_K;
    az = (float)az_raw * ACCEL_K;

    //将陀螺仪AD值转换为 rad/s
    gx = (float)gx_raw * GYRO_K;
    gy = (float)gy_raw * GYRO_K;
    gz = (float)gz_raw * GYRO_K;

    // Rate of change of quaternion from gyroscope
    qDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz);
    qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy);
    qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx);
    qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx);

    // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation)
    if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) {

        // Normalise accelerometer measurement
        recipNorm = invSqrt(ax * ax + ay * ay + az * az);
        ax *= recipNorm;
        ay *= recipNorm;
        az *= recipNorm;

        // Auxiliary variables to avoid repeated arithmetic
        _2q0 = 2.0f * q0;
        _2q1 = 2.0f * q1;
        _2q2 = 2.0f * q2;
        _2q3 = 2.0f * q3;
        _4q0 = 4.0f * q0;
        _4q1 = 4.0f * q1;
        _4q2 = 4.0f * q2;
        _8q1 = 8.0f * q1;
        _8q2 = 8.0f * q2;
        q0q0 = q0 * q0;
        q1q1 = q1 * q1;
        q2q2 = q2 * q2;
        q3q3 = q3 * q3;

        // Gradient decent algorithm corrective step
        s0 = _4q0 * q2q2 + _2q2 * ax + _4q0 * q1q1 - _2q1 * ay;
        s1 = _4q1 * q3q3 - _2q3 * ax + 4.0f * q0q0 * q1 - _2q0 * ay - _4q1 + _8q1 * q1q1 + _8q1 * q2q2 + _4q1 * az;
        s2 = 4.0f * q0q0 * q2 + _2q0 * ax + _4q2 * q3q3 - _2q3 * ay - _4q2 + _8q2 * q1q1 + _8q2 * q2q2 + _4q2 * az;
        s3 = 4.0f * q1q1 * q3 - _2q1 * ax + 4.0f * q2q2 * q3 - _2q2 * ay;
        recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitude
        s0 *= recipNorm;
        s1 *= recipNorm;
        s2 *= recipNorm;
        s3 *= recipNorm;

        // Apply feedback step
        qDot1 -= beta * s0;
        qDot2 -= beta * s1;
        qDot3 -= beta * s2;
        qDot4 -= beta * s3;
    }

    // Integrate rate of change of quaternion to yield quaternion
    q0 += qDot1 * dt;
    q1 += qDot2 * dt;
    q2 += qDot3 * dt;
    q3 += qDot4 * dt;

    // Normalise quaternion
    recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
    q0 *= recipNorm;
    q1 *= recipNorm;
    q2 *= recipNorm;
    q3 *= recipNorm;

    Pitch = asin(-2.0f * (q1*q3 - q0*q2))* RAD_TO_DEG;
    Roll = atan2(q0*q1 + q2*q3, 0.5f - q1*q1 - q2*q2) * RAD_TO_DEG;
    Yaw = atan2(q1*q2 + q0*q3, 0.5f - q2*q2 - q3*q3)* RAD_TO_DEG;
}

void MadgwickAHRSupdate_9(short ax_raw, short ay_raw, short az_raw, short gx_raw, short gy_raw, short gz_raw, short mx_raw, short my_raw, short mz_raw)
{
    // Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation)
    if((mx_raw == 0) && (my_raw == 0) && (mz_raw == 0))
    {
        MadgwickAHRSupdate_6(ax_raw, ay_raw, az_raw, gx_raw, gy_raw, gz_raw);
        return;
    }

    float ax, ay, az;
    float gx, gy, gz;
    float mx, my, mz;
    float recipNorm;
    float s0, s1, s2, s3;
    float qDot1, qDot2, qDot3, qDot4;
    float hx, hy;
    float _2q0mx, _2q0my, _2q0mz, _2q1mx, _2bx, _2bz, _4bx, _4bz, _2q0, _2q1, _2q2, _2q3, _2q0q2, _2q2q3, q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3;

    //获取delta t用于积分
    float dt = (float)(HAL_GetTick() - madgwick_timer) / 1000.0f;
    madgwick_timer = HAL_GetTick();

    ax = (float)ax_raw * ACCEL_K;
    ay = (float)ay_raw * ACCEL_K;
    az = (float)az_raw * ACCEL_K;

    //将陀螺仪AD值转换为 弧度/s
    gx = (float)gx_raw * GYRO_K;
    gy = (float)gy_raw * GYRO_K;
    gz = (float)gz_raw * GYRO_K;

    mx = (float)mx_raw;
    my = (float)my_raw;
    mz = (float)mz_raw;

    // Rate of change of quaternion from gyroscope
    qDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz);
    qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy);
    qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx);
    qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx);

    // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation)
    if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f)))
    {

        // Normalise accelerometer measurement
        recipNorm = invSqrt(ax * ax + ay * ay + az * az);
        ax *= recipNorm;
        ay *= recipNorm;
        az *= recipNorm;

        // Normalise magnetometer measurement
        recipNorm = invSqrt(mx * mx + my * my + mz * mz);
        mx *= recipNorm;
        my *= recipNorm;
        mz *= recipNorm;

        // Auxiliary variables to avoid repeated arithmetic
        _2q0mx = 2.0f * q0 * mx;
        _2q0my = 2.0f * q0 * my;
        _2q0mz = 2.0f * q0 * mz;
        _2q1mx = 2.0f * q1 * mx;
        _2q0 = 2.0f * q0;
        _2q1 = 2.0f * q1;
        _2q2 = 2.0f * q2;
        _2q3 = 2.0f * q3;
        _2q0q2 = 2.0f * q0 * q2;
        _2q2q3 = 2.0f * q2 * q3;
        q0q0 = q0 * q0;
        q0q1 = q0 * q1;
        q0q2 = q0 * q2;
        q0q3 = q0 * q3;
        q1q1 = q1 * q1;
        q1q2 = q1 * q2;
        q1q3 = q1 * q3;
        q2q2 = q2 * q2;
        q2q3 = q2 * q3;
        q3q3 = q3 * q3;

        // Reference direction of Earth's magnetic field
        hx = mx * q0q0 - _2q0my * q3 + _2q0mz * q2 + mx * q1q1 + _2q1 * my * q2 + _2q1 * mz * q3 - mx * q2q2 - mx * q3q3;
        hy = _2q0mx * q3 + my * q0q0 - _2q0mz * q1 + _2q1mx * q2 - my * q1q1 + my * q2q2 + _2q2 * mz * q3 - my * q3q3;
        _2bx = sqrt(hx * hx + hy * hy);
        _2bz = -_2q0mx * q2 + _2q0my * q1 + mz * q0q0 + _2q1mx * q3 - mz * q1q1 + _2q2 * my * q3 - mz * q2q2 + mz * q3q3;
        _4bx = 2.0f * _2bx;
        _4bz = 2.0f * _2bz;

        // Gradient decent algorithm corrective step
        s0 = -_2q2 * (2.0f * q1q3 - _2q0q2 - ax) + _2q1 * (2.0f * q0q1 + _2q2q3 - ay) - _2bz * q2 * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (-_2bx * q3 + _2bz * q1) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + _2bx * q2 * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz);
        s1 = _2q3 * (2.0f * q1q3 - _2q0q2 - ax) + _2q0 * (2.0f * q0q1 + _2q2q3 - ay) - 4.0f * q1 * (1 - 2.0f * q1q1 - 2.0f * q2q2 - az) + _2bz * q3 * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (_2bx * q2 + _2bz * q0) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + (_2bx * q3 - _4bz * q1) * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz);
        s2 = -_2q0 * (2.0f * q1q3 - _2q0q2 - ax) + _2q3 * (2.0f * q0q1 + _2q2q3 - ay) - 4.0f * q2 * (1 - 2.0f * q1q1 - 2.0f * q2q2 - az) + (-_4bx * q2 - _2bz * q0) * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (_2bx * q1 + _2bz * q3) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + (_2bx * q0 - _4bz * q2) * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz);
        s3 = _2q1 * (2.0f * q1q3 - _2q0q2 - ax) + _2q2 * (2.0f * q0q1 + _2q2q3 - ay) + (-_4bx * q3 + _2bz * q1) * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (-_2bx * q0 + _2bz * q2) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + _2bx * q1 * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz);
        recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitude
        s0 *= recipNorm;
        s1 *= recipNorm;
        s2 *= recipNorm;
        s3 *= recipNorm;

        // Apply feedback step
        qDot1 -= beta * s0;
        qDot2 -= beta * s1;
        qDot3 -= beta * s2;
        qDot4 -= beta * s3;
    }

    // Integrate rate of change of quaternion to yield quaternion
    q0 += qDot1 * dt;
    q1 += qDot2 * dt;
    q2 += qDot3 * dt;
    q3 += qDot4 * dt;

    // Normalise quaternion
    recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
    q0 *= recipNorm;
    q1 *= recipNorm;
    q2 *= recipNorm;
    q3 *= recipNorm;

    Pitch = asin(-2.0f * (q1*q3 - q0*q2))* RAD_TO_DEG;
    Roll = atan2(q0*q1 + q2*q3, 0.5f - q1*q1 - q2*q2) * RAD_TO_DEG;
    Yaw = atan2(q1*q2 + q0*q3, 0.5f - q2*q2 - q3*q3)* RAD_TO_DEG;
}

void Filter_Get_AttitudeAngles(AttiAgls *angles){
    angles->roll = Roll;
    angles->pitch = Pitch;
    angles->yaw = Yaw;
}


//====================================================================================================
// END OF CODE
//====================================================================================================

使用时,先声明一个AttiAgls结构体用于存储三个姿态角的值,在数据解算开始之前的最后一步运行MadgwickAHRSInit();用于将madgwick_timer中的数据刷新至最新,而后在每一个训练周期调用MadgwickAHRSupdate_6()输入原始数据进行数据解算,如果是九轴解算就调用MadgwickAHRSupdate_9,然后调用Filter_Get_AttitudeAngles()获取姿态角

代码示例:
main.c:

#include "main.h"
#include "usbd_cdc_if.h"
#include "mpu6050.h"
#include "Madgwick.h"

int main(void)
{
	//原始数据包
	MPU6050_Data mpu_data;
	//姿态角
	AttiAgls attitudeAngles;
	
	MPU_6050_Init();//MPU6050初始化
    MPU6050_calibrate();//MPU6050校准
    
	MadgwickAHRSInit();//滤波器初始化
	while(1)
	{
		MPU_Read_ALL(&mpu_data);
		MadgwickAHRSupdate_6(mpu_data.Accel_X_RAW,
                             mpu_data.Accel_Y_RAW,
                             mpu_data.Accel_Z_RAW,
                             mpu_data.Gyro_X_RAW,
                             mpu_data.Gyro_Y_RAW,
                             mpu_data.Gyro_Z_RAW);
        Filter_Get_AttitudeAngles(&attitudeAngles);
        usb_printf("Roll:%f, Pitch:%f, Yaw:%f\r\n", attitudeAngles.roll, attitudeAngles.pitch, attitudeAngles.yaw);
	}
}

在串口监视器中查看结果:
madgwick

ws2812b:一个美梦的延续

原理简单介绍

ws2812b,也是大家耳熟能详的可编程灯带了,这里简单讲解一下原理:
ws2812b单线控制,以800khz运行,一个数据包中,每个灯珠的数据为3 * 8 = 24位,其中1码为2/3高电平+1/3低电平,0码为1/3高电平+2/3低电平, 所有数据发送结束之后,以超过50μs的低电平表示数据传输结束,第一个灯珠接收到数据包之后,会截取第一个收到的数据作为自己的颜色,将剩下的数据通过VOUT引脚发送给下一个灯珠,以此直到最后一个灯珠。
原理详细解释在这里有详细说明。

代码实现

参照这里在cubemx上对pwm和dma进行配置
废话不多说直接上库文件:
RGB.h

#ifndef __RGB_H__
#define __RGB_H__

#include "main.h"

#define MAX_LED 1

/*建立一个定义单个LED三原色值大小的结构体*/
typedef struct
{
    uint8_t R;
    uint8_t G;
    uint8_t B;
}RGB_Color_TypeDef;

extern const RGB_Color_TypeDef RED;
extern const RGB_Color_TypeDef GREEN;
extern const RGB_Color_TypeDef BLUE;
extern const RGB_Color_TypeDef WHITE;
extern const RGB_Color_TypeDef BLACK;

void WS2812_Send(void);

void LED_Set_Color(uint16_t LedId, RGB_Color_TypeDef color);
void LED_Set_All_Colors(RGB_Color_TypeDef colors[]);

// 以下是HSV颜色空间和RGB颜色空间相互转换接口
/*********************************************************************************************
红:
    R   255         H:0
    G   0           S:100
    B   0           V:100
绿:
    R   0           H:120
    G   255         S:100
    B   0           V:100
蓝:
    R   0           H:240
    G   0           S:100
    B   255         V:100
*********************************************************************************************/

void hsv2rgb(float h, float s, float v, RGB_Color_TypeDef *rgb); //hsv转rgb色彩空间


#endif

RGB.c:

#include "RGB.h"
#include "tim.h"
#include <math.h>

/*Some Static Colors------------------------------*/
const RGB_Color_TypeDef RED = {255, 0, 0};
const RGB_Color_TypeDef GREEN = {0, 255, 0};
const RGB_Color_TypeDef BLUE = {0, 0, 255};
const RGB_Color_TypeDef WHITE = {255, 255, 255};
const RGB_Color_TypeDef BLACK = {0, 0, 0};

uint8_t LED_Data[MAX_LED][4];//LED数据
uint16_t pwmData[(24 * MAX_LED) + 50];//pwm形式的底层数据

/**
 * @brief   一次性设置全部灯珠
 * @param   colors:颜色数组,要求和灯珠数目移植
 */
void LED_Set_All_Colors(RGB_Color_TypeDef colors[])
{
    for (uint16_t i = 0; i < MAX_LED; i++)
    {
        LED_Set_Color(i, colors[i]);
    }
}

/**
 * @brief   设置单个灯珠颜色
 * @param   LedId:灯珠id,从0开始
 * @param   color:单个灯珠颜色rgb数据
 * @return  none
 */
void LED_Set_Color(uint16_t LedId, RGB_Color_TypeDef color)
{
    LED_Data[LedId][0] = LedId;
    LED_Data[LedId][1] = color.G;
    LED_Data[LedId][2] = color.R;
    LED_Data[LedId][3] = color.B;
}

/**
 * @brief   将LED_Data中的数据转化为pwm外设可使用的数据并发送
 * @param   none
 * @return  none
 */
void WS2812_Send(void)
{
    uint32_t index = 0;
    uint32_t color;


    for (uint16_t i = 0; i < MAX_LED; i++)
    {
        color = ((LED_Data[i][1] << 16) | (LED_Data[i][2] << 8) | (LED_Data[i][3]));

        for (int i = 23; i >= 0; i--)
        {
            if (color & (1 << i))
            {
                pwmData[index] = 60;  // 2/3 of 90
            } else pwmData[index] = 30;  // 1/3 of 90

            index++;
        }

    }

    for (uint8_t i = 0; i < 50; i++)
    {
        pwmData[index] = 0;
        index++;
    }

    HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_1, (uint32_t *) pwmData, index);
}

void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM1)
    {
        HAL_TIM_PWM_Stop_DMA(&htim1, TIM_CHANNEL_1);
    }
}

/**
 1. @brief   HSV颜色空间 转 RGB颜色空间
 2. @param   h:色相(0-360)
 3. @param   s:饱和度(0-1)
 4. @param   v:明度(0-1)
 5. @param   rgb:RGB颜色空间参数
 6. @return  none
*/
void hsv2rgb(float h, float s, float v, RGB_Color_TypeDef *rgb)
{
    int i;
    float f, a, b, c;

    if (h >= 360)
    {
        h = 0;
    }

    if (s == 0)
    {
        rgb->R = (unsigned char) ((int) (v * 255));
        rgb->G = (unsigned char) ((int) (v * 255));
        rgb->B = (unsigned char) ((int) (v * 255));
    } else
    {
        h /= 60.0f;  // sector 0 to 5, h_max=360 360/60=6[0,1,2,3,4,5]
        i = (int) floor(h); // floor(h)
        f = h - i; // factorial path of h
        a = v * (1 - s);
        b = v * (1 - s * f);
        c = v * (1 - s * (1 - f));
        switch (i)
        {
            case 0:
                rgb->R = (unsigned char) ((int) (v * 255)); //v*255
                rgb->G = (unsigned char) ((int) (c * 255)); //c*255;
                rgb->B = (unsigned char) ((int) (a * 255)); //a*255;
                break;
            case 1:
                rgb->R = (unsigned char) ((int) (b * 255)); //b*255;
                rgb->G = (unsigned char) ((int) (v * 255)); //v*255;
                rgb->B = (unsigned char) ((int) (a * 255)); //a*255;
                break;
            case 2:
                rgb->R = (unsigned char) ((int) (a * 255)); //a*255;
                rgb->G = (unsigned char) ((int) (v * 255)); //v*255;
                rgb->B = (unsigned char) ((int) (c * 255)); //c*255;
                break;
            case 3:
                rgb->R = (unsigned char) ((int) (a * 255));//a*255;
                rgb->G = (unsigned char) ((int) (b * 255));//b*255;
                rgb->B = (unsigned char) ((int) (v * 255));//v*255;
                break;
            case 4:
                rgb->R = (unsigned char) ((int) (c * 255)); //c*255;
                rgb->G = (unsigned char) ((int) (a * 255)); //a*255;
                rgb->B = (unsigned char) ((int) (v * 255)); //v*255;
                break;
            default:
                rgb->R = (unsigned char) ((int) (v * 255)); //v*255;
                rgb->G = (unsigned char) ((int) (a * 255)); //a*255;
                rgb->B = (unsigned char) ((int) (b * 255)); //b*255;
                break;
        }

    }
}

具体的定时器和其对应通道请自行修改,此处启用tim1的通道1,pwm引脚为PA8
驱动方式有两种:

  1. 一种为LED_Set_Color(),单独变化一个灯珠的颜色使用,参数有两个,第一个参数为灯珠的地址(从0开始),第二个为RGB_Color_TypeDef类型的结构体,里面存储的为你所需要的RGB数据
  2. 另一种为LED_Set_All_Colors(),仅需传入一个RGB_Color_TypeDef类型的rgb数据,用途时将所有灯都设置为该颜色

设置完颜色时候,需要
调用WS2812_Send();进行发送数据,灯才会亮

还有额外一点,因为rgb色彩空间偏向于底层驱动,而和人的直觉不符,所以我在RGB.c中写了hsv转rgb色彩空间的转换函数,使用时,只需要调用该函数,传入hsv三个三处,然后传入一个RGB_Color_TypeDef结构体的地址,改rgb数据包就会转换为你所输入的hsv数据转化而来的数据,由此可以做出幻彩变换的效果:

代码示例:
main.c:

#include "RGB.h"

int main(void)
{
	RGB_Color_TypeDef color;
	uint16_t i = 0, hue = 0;
	while(1)
	{
		if (i == 10)
        {
            hsv2rgb((float) hue, 1.0f, 0.4f, &color);
            LED_Set_Color(0, color);
            WS2812_Send();
            i = 0;
            if (hue > 360) hue = 0;
            hue++;
        }
        i++;
        HAL_Delay(5);
	}
}

效果:

RGB

  • 17
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值