在F1C100S上调试的,原本打算用GPIO来驱动RGB灯的,使用GPIOD这个库来操作GPIO,实测下来翻转速度还到不了1MHZ,这肯定是不行。于是就用SPI来驱动了

  1. 首先是在设备树添加SPI1,这样系统启动后应用层才可以操作SPI设备,注意这里并没有匹配“spi-dev”,因为启动时会有错误提示,但是spi是可以用的,这里为了去除那个错误就匹配的下边这个,用起来是一样的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
        &spi1{
    status = "okay";
    spidev@0{
    compatible = "rohm,dh2228fv";
    spi-max-frequency = <50000000>;
    reg = <0>;
    status = "okay";
    };
    };
  2. 系统启动后,查看/dev是否有spidev1.0。另外虽然SPI初始化后有4个脚,但是实际测试依然可以做为普通GPIO使用,也即只使用MOSI这一个引脚即可

  3. 一切做准备就绪,首先就是要初始化SPI,这里的mode并无多大关系,任意一种都可以的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43

    int spi_init(char *spi_dev, int mode, int bits, int speed)
    {
    fd = open(spi_dev, O_RDWR);
    if (fd < 0)
    {
    printf("[%s]:[%d] open spi file error\r\n", __FUNCTION__, __LINE__);
    return -1;
    }

    //设置模式
    ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
    if (ret == -1)
    printf("can't set spi mode");

    ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
    if (ret == -1)
    printf("can't get spi mode");

    //设置一次多少位
    ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
    if (ret == -1)
    printf("can't set bits per word");

    ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
    if (ret == -1)
    printf("can't get bits per word");

    //设置最大速度
    ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
    if (ret == -1)
    printf("can't set max speed hz");

    ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
    if (ret == -1)
    printf("can't get max speed hz");

    printf("spi mode: %d\n", mode);
    printf("bits per word: %d\n", bits);
    printf("max speed: %d Hz (%d KHz)\n", speed, speed / 1000);
    return 0;
    }

  4. 我用的灯的时序如下图所示,数据格式为RGB888

    a.从下图可以看到是通过高电平和低电平时间来确认发送的是0还是1

    b.高0.3us低0.9us代表0,高0.9us低0.3us代表1

    c.总时间约1.2us士0.16us 1.04us-1.36us 这里按1Mhz进行计算,F1C100S 可以设置的时钟 1M 2M 4M 8M .......

    d. 这里采取8个时钟输出1位灯的数据,经过计算可知0xf8(1111 1100=0.125us*7=750us)就可代表1,0xc0(1100 0000=0.125us*2=250us)就代表0.

    e. 由以上可知,一个RGB灯是由24Bit组成,SPI一次需要发送24个字节才能控制一个灯

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    for (uint8_t j = 0; j < 3; j++)
    {
    for (uint8_t i = 0; i < 8; i++)
    {
    if ((data & 0x80) > 0)
    {
    temp->data[i + j * 8] = 0xfc; // 1
    }
    else
    {
    temp->data[i + j * 8 ] = 0xc0; // 0
    }
    data = data << 1;
    }
    }
  1. 最后就是关于亮度调整的问题,要想调整亮度需要把RGB数据转换到HSV姿色空间下才好调整亮度
    以下是关于RGB<—>HSV互转的代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    #define max(a, b) ((a) > (b) ? (a) : (b))
    #define min(a, b) ((a) < (b) ? (a) : (b))
    #define max3(a, b, c) (((a) > (b) ? (a) : (b)) > (c) ? ((a) > (b) ? (a) : (b)) : (c))
    #define min3(a, b, c) (((a) < (b) ? (a) : (b)) < (c) ? ((a) < (b) ? (a) : (b)) : (c))

    void led_strip_rgb2hsv(color_rgb_t *rgb,color_hsv_t *hsv)
    {


    uint8_t max, min, delta = 0;
    rgb->b = (uint8_t)(rgb->color);
    rgb->g = (uint8_t)((rgb->color)>>8);
    rgb->r = (uint8_t)((rgb->color)>>16);
    max = max3(rgb->r, rgb->g, rgb->b);
    min = min3(rgb->r, rgb->g, rgb->b);
    delta = (max - min);
    if (delta == 0)
    {
    hsv->h = 0;
    }
    else
    {
    if (rgb->r == max)
    {
    hsv->h = ((rgb->g - rgb->b) * 60 / delta);
    }
    else if (rgb->g == max)
    {
    hsv->h = 120 + (((rgb->b - rgb->r) * 60 / delta));
    }
    else if (rgb->b == max)
    {
    hsv->h = 240 + (((rgb->r - rgb->g) * 60 / delta));
    }

    if (hsv->h < 0)
    {
    hsv->h += 360;
    }
    }

    if (max == 0)
    {
    hsv->s = 0;
    }
    else
    {
    hsv->s = (delta*100 / max);
    }

    hsv->v = max;

    }

    void led_strip_hsv2rgb(color_hsv_t *hsv,color_rgb_t *rgb)
    {
    hsv->h %= 360; // h -> [0,360]
    uint32_t rgb_max = hsv->v * 2.55f;
    uint32_t rgb_min = rgb_max * (100 - hsv->s) / 100.0f;

    uint32_t i = hsv->h / 60;
    uint32_t diff = hsv->h % 60;

    // RGB adjustment amount by hue
    uint32_t rgb_adj = (rgb_max - rgb_min) * diff / 60;

    switch (i)
    {
    case 0:
    rgb->r = rgb_max;
    rgb->g = rgb_min + rgb_adj;
    rgb->b = rgb_min;
    break;
    case 1:
    rgb->r = rgb_max - rgb_adj;
    rgb->g = rgb_max;
    rgb->b = rgb_min;
    break;
    case 2:
    rgb->r = rgb_min;
    rgb->g = rgb_max;
    rgb->b = rgb_min + rgb_adj;
    break;
    case 3:
    rgb->r = rgb_min;
    rgb->g = rgb_max - rgb_adj;
    rgb->b = rgb_max;
    break;
    case 4:
    rgb->r = rgb_min + rgb_adj;
    rgb->g = rgb_min;
    rgb->b = rgb_max;
    break;
    default:
    rgb->r = rgb_max;
    rgb->g = rgb_min;
    rgb->b = rgb_max - rgb_adj;
    break;
    }

    rgb->color = rgb->r;
    rgb->color = (rgb->color << 8) | rgb->g;
    rgb->color = (rgb->color << 8) | rgb->b;
    //printf("color:%x %x %x %x\n", rgb->color,rgb->r, rgb->g, rgb->b);
    }