共阳数码管段码表_写了个硬件无关的七段LED数码管驱动

七段数码管应该是个入门级的外设了, 用单片机管脚直接扫描驱动, 或者用TM1650之类专用IC驱动都不难, 但是直接扫描驱动时写起来还是比较繁琐, 以及换个单片机基本得重写一遍.

前几天做了个电压表模块,

张浩:这次做个简单的, 3位电压表模块​zhuanlan.zhihu.com
424ba2c6e54329a733b5488dce35d5e6.png

顺便实现了一个硬件无关的数码管驱动, 以后再折腾这东西就省事多了. 源代码如下:

zss.h:

#ifndef _ZSS_H
#define _ZSS_H

typedef struct {
    void (*set_segment_f)(unsigned int segment, int state);          // 写段码
    void (*set_digit_f)(unsigned int digit, int state);            // 写位码
    int digits, segments;
} zss_cfg_t;

void zss_update(void);           // 刷新, 每1~10ms调用一次
void zss_init(zss_cfg_t* cfg);
void zss_set_num(int num);         // 显示数值
void zss_set_dot(int pos);          // 设置小数点位置
void zss_set_raw(unsigned char* raw);          // 显示自定义字符 
#endif

zss.c:

#include "zss.h"

enum {
    ZSS_MODE_RAW, ZSS_MODE_NUM
};
static const unsigned char masks[10] = {    //
    0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f    // char mask of digit 0~9
    };

static struct {
    zss_cfg_t cfg;
    int num;
    unsigned char mode, dot_pos;
    unsigned char* raw;
} g;

void zss_set_num(int num)
{
    g.mode = ZSS_MODE_NUM;
    g.num = num;
}

void zss_update(void)
{
    static int curr_digit = 0;
    int i, j, n;
    static int num;

    for(i = 0; i < g.cfg.digits; i++)
        g.cfg.set_digit_f(i, 1);    // 先全部关闭

    switch(g.mode) {
        case ZSS_MODE_NUM:
            if(curr_digit == 0)
                num = g.num;
            else
                num /= 10;

            for(j = 0; j < g.cfg.segments; j++) {
                if(masks[num % 10] & (1 << j))
                    g.cfg.set_segment_f(j, 1);
                else
                    g.cfg.set_segment_f(j, 0);
            }
            if(curr_digit == g.dot_pos)
                g.cfg.set_segment_f(7, 1);
            else
                g.cfg.set_segment_f(7, 0);
            g.cfg.set_digit_f(g.cfg.digits - curr_digit - 1, 0);    // 数字从右向左显示
            break;
        case ZSS_MODE_RAW:
            for(j = 0; j < g.cfg.segments; j++) {
                if(g.raw[curr_digit] & (1 << j))
                    g.cfg.set_segment_f(j, 1);
                else
                    g.cfg.set_segment_f(j, 0);
            }
            g.cfg.set_digit_f(curr_digit, 0);
            break;
    }
    curr_digit++;
    curr_digit %= g.cfg.digits;
}

void zss_set_dot(int pos)
{
    g.dot_pos = pos;
}

void zss_set_raw(unsigned char* raw)
{
    g.raw = raw;
    g.mode = ZSS_MODE_RAW;
}

void zss_init(zss_cfg_t* cfg)
{
    g.cfg = *cfg;
}

用户需要提供两个回调函数来向段码/位码的管脚写1或写0, 在STM8S平台的代码如下, 其他平台上稍微改改就行了. 每个管脚独立设置所在的GPIO组, 虽然效率低一些, 但是不需要把8个段码放在同一组GPIO上, 这样布线可以就近走线, 轻松多了. 我这次用的是共阴数码管, 点亮时是段码写1, 位码写0. 如果是共阳数码管, 在回调函数里的if(state)这里, state前面加个感叹号就行.

typedef struct {
    GPIO_TypeDef* gpiox;
    GPIO_Pin_TypeDef pin;
} gpio_t;

const gpio_t segments[8] = {
//
    {GPIOD, GPIO_PIN_4},    //
    {GPIOC, GPIO_PIN_4},    //
    {GPIOC, GPIO_PIN_7},    //
    {GPIOD, GPIO_PIN_2},    //
    {GPIOD, GPIO_PIN_3},    //
    {GPIOD, GPIO_PIN_5},    //
    {GPIOC, GPIO_PIN_6},    //
    {GPIOC, GPIO_PIN_5}    //
};

const gpio_t digits[3] = {
//
    {GPIOA, GPIO_PIN_3},    //
    {GPIOB, GPIO_PIN_5},    //
    {GPIOB, GPIO_PIN_4}    //
};

void set_digit(unsigned int digit, int state)
{
    if(digit < 3) {
        if(state)
            digits[digit].gpiox->ODR |= digits[digit].pin;
        else
            digits[digit].gpiox->ODR &= ~(digits[digit].pin);
    }
}

void set_segment(unsigned int segment, int state)
{
    if(segment < 8) {
        if(state)
            segments[segment].gpiox->ODR |= segments[segment].pin;
        else
            segments[segment].gpiox->ODR &= ~(segments[segment].pin);
    }
}

初始化, 提供回调函数指针, 并且设定最大段数和位数即可. 想想其实可以改成把字符mask也一起作为参数传入, 这样要换九段管米字管之类也容易了.

        zss_cfg_t cfg;
        cfg.set_digit_f = set_digit;
        cfg.set_segment_f = set_segment;
        cfg.digits = 3;
        cfg.segments = 8;
        zss_init(&cfg);

使用时每1~10ms调用zss_update()函数刷新, 需要显示数值时用zss_set_num(), 指定小数点位置用zss_set_dot(), 显示HI, LO, Err之类字符时用zss_set_raw()即可.

Github链接:

https://github.com/tomzbj/zss​github.com
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值