max7219共阴极数码管驱动 stm32 hal

因为做这个需要用到max7219,所以在网上找了一些前车之鉴。和以前找其他资料一样,我认为他们写的真的不好,甚至都辣眼睛,更有甚者上来就放两句话,最后一句是我实现了,倒不如发

目的

实现max7219驱动数码管显示

步骤

  • max7219资料解读
  • stm32 参数配置
  • 驱动实现
max7219资料解读

此部分只摘取官方文档部分内容。我认为足够使用了

  • max7219 与stm32通信方式
    max7219通信方式为SPI,16bit 数据格式,先发高位,最大10MHz (圈重点)
    在这里插入图片描述
  • max7219 控制方式
    spi一次发送16bit,就是2字节 = 地址 + 数据 。地址代表着不同的功能,数据代表想设置的参数。按需要直接到地址的表查,然后按照地址的功能设置数据。初次上电时进入的是shutdown模式

下图寄存器表就是地址

在这里插入图片描述
ditgit 代表要控制哪个数据管,max7219可以控制0 - 7个数码管,矩阵led这里不说了。实现步骤就是,想控制的 数码管地址 + 想要显示的数据(可以通过decode mode 设置BCD解码或者不解码)

功能概述
  • 解码模式 支持BCD解码,或者直接显示某个单独的线端
  • 亮度设置
  • 扫描限制 就是用几个数码管,数量小于等于三个时必须进行电流设置(请查手册)
  • 掉电模式 为了省电,掉电时候只是不显示而已,还是可以进行设置操作,初次上电也是处于这个模式
  • 显示测试 就是测试能不能都亮,或者正常的模式(自己设定的想让哪个亮的模式)

功能描述表
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

stm32 参数配置

在这里插入图片描述
重点注意红框部分 硬件NSS不要使用,因为这个是你想的不是一个东西。速率不能超过10Mhz,差不多就行了,显示几个数字哪怕设置为最慢也是没问题的。

驱动实现
static int max7219_send(uint8_t address,uint8_t data)
{
    uint16_t Transmit_buf = (0x00ff&data)|(0xff00&(address<<8));
    
    HAL_GPIO_WritePin(SPI1_NSS_GPIO_Port,SPI1_NSS_Pin,GPIO_PIN_RESET);
    HAL_SPI_Transmit(&MAX7219_SPI,(uint8_t*)&Transmit_buf,1,3000);
    HAL_GPIO_WritePin(SPI1_NSS_GPIO_Port,SPI1_NSS_Pin,GPIO_PIN_SET);
    
    return 0;
}
char tem[2] = {0x01,0x02};

只需要进行发送即可,注意!16bit,先发高字节,假如使用数组tem 将先发送0x02,发送的字节1代表着sizeof(uint16_t)= 2字节
代码如下,使用面向对象的抽象思想

/**
  ******************************************************************************
  * File Name          : max7219.h
  * Description        : 头文件.
  ******************************************************************************
  * @attention
  ******************************************************************************
  */

#ifndef __MAX7219_H
#define __MAX7219_H
#ifdef __cplusplus
 extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* address define */
#define NO_OP              0x00
#define DIGIT0             0x01
#define DIGIT1             0x02
#define DIGIT2             0x03
#define DIGIT3             0x04
#define DIGIT4             0x05
#define DIGIT5             0x06
#define DIGIT6             0x07
#define DIGIT7             0x08
#define DECODE_MODE        0x09
#define INTENSITY          0x0A
#define SCAN_LIMIT         0x0B
#define SHUT_DOWN          0x0C
#define DISPLAY_TEST       0x0F
/* mode define */
#define NORMAL_MODE           0
#define NO_DECODE_D0_D7    0x00
#define DECODE_D0_ONLY     0x01
#define DECODE_D0_D3_ONLY  0x0F
#define DECODE_D0_D7       0xFF

/*
 *   AAAAAA\      
 *  FF  __BB\     
 *  FF /  BB |    
 *   GGGGGG  |    
 *  EE  __CC<     
 *  EE /  CC |    
 *  \DDDDDD  |  DP\ 
 *   \______/   \__|
 *  
 *    数码管
 */
#define Segment_Line_DP    (1<<7)
#define Segment_Line_A     (1<<6)
#define Segment_Line_B     (1<<5)
#define Segment_Line_C     (1<<4)
#define Segment_Line_D     (1<<3)
#define Segment_Line_E     (1<<2)
#define Segment_Line_F     (1<<1)
#define Segment_Line_G     (1<<0)

/*
 * flash_user_start_addr 数据开始的地址
 * Data                  数据
 * flash_user_size       数据长度
 */
typedef struct
{
    int (*max7219_api_find)(uint8_t* mode);
    int (*max7219_api)(uint8_t mode);
    int (*max7219_digit)(uint8_t digit,uint8_t data);
} max7219TypeDef;

extern max7219TypeDef max7219_api;

#ifdef __cplusplus
}
#endif
#endif /*__ max7219_H */

/**
  ******************************************************************************
  * File Name          : max7219.c
  * Description        : 驱动文件.
  ******************************************************************************
  * @attention	1.延时函数自己需要实现
  * 			2.spi发送函数需要自己实现
  * 			3.printf自己实现
  ******************************************************************************
  */
#include "max7219.h"
#include "spi.h"
#include "stdio.h"
#include "string.h"

/* 配置主函数log功能*/
#define MAX7219_LOG_ENABLE

#ifdef MAX7219_LOG_ENABLE
#define MAX7219_LOG(...) \
do{\
	printf(__VA_ARGS__);\
    printf("\r\n");\
  } while (0)
#else
#define MAX7219_LOG(...)
#endif
  
#define MAX7219_SPI hspi1

static const uint8_t to_ascii_tab[][2] = {
    'A', 0x77,  '=',0x09,
    'B', 0x1F,  '-',0x01,
    'C', 0x4E,  '+',0x31,
    'D', 0x3D,  '(',0x4E,
    'E', 0x4f,  ')',0x78,
    'F', 0x47,  0,  0,
    'G', 0x5e,
    'H', 0x37,
    'I', 0x78,
    'J', 0x38,
    'K', 0x57,
    'L', 0x0e,
    'M', 0x76,
    'N', 0x15,
    'O', 0x1d,
    'P', 0x67,
    'Q', 0x73,
    'R', 0x46,
    'S', 0x49,
    'T', 0x0f,
    'U', 0x3e,
    'V', 0x1c,
    'W', 0x3f,
    'X', 0x13,
    'Y', 0x3b,
    'Z', 0x2d,
};
  
static int max7219_send(uint8_t address,uint8_t data)
{
    uint16_t Transmit_buf = (0x00ff&data)|(0xff00&(address<<8));
    
    HAL_GPIO_WritePin(SPI1_NSS_GPIO_Port,SPI1_NSS_Pin,GPIO_PIN_RESET);
    HAL_SPI_Transmit(&MAX7219_SPI,(uint8_t*)&Transmit_buf,1,3000);
    HAL_GPIO_WritePin(SPI1_NSS_GPIO_Port,SPI1_NSS_Pin,GPIO_PIN_SET);
    
    return 0;
}

/* 掉电模式 
 * mode:
 * NORMAL_MODE 0 -> 正常模式
 *             1 -> 掉电模式
 */
int max7219_shutdown(uint8_t mode)
{
    max7219_send(SHUT_DOWN,mode == NORMAL_MODE?1:0);
    return 0;
}

/* 译码器模式 
 * mode:
 *  NO_DECODE_D0_D7    0x00 -> 不译码模式
 *  DECODE_D0_ONLY     0x01 -> digits 0   译码 7-1 不译码
 *  DECODE_D0_D3_ONLY  0x0F -> digits 3-0 译码 7-4 不译码
 *  DECODE_D0_D7       0xFF -> digits 7-0 译码
 */
int max7219_decode_mode(uint8_t mode)
{
    max7219_send(DECODE_MODE,mode);
    return 0;
}

/* 亮度控制 
 * mode:0x00-0x0f
 */
int max7219_intensity(uint8_t mode)
{
    max7219_send(INTENSITY,mode&0x0f);
    return 0;
}

/* 扫描控制 
 * mode:0-7 digits 0 - 7
 */
int max7219_scan_limit(uint8_t mode)
{
    max7219_send(SCAN_LIMIT,mode&0x07);
    return 0;
}

/* digits显示 
 *  digit -> 显示位置'0'-'7'
 *  data  -> 显示的数据 
 * note :
 *          字符直接显示,不需要查手册 data|Segment_Line_DP
 *          带小数点时最高位为1 ->0x80 + x
 */
int max7219_digit(uint8_t digit,uint8_t data)
{
    uint8_t tem = data&0x7f;
    if ((tem >= '0' && tem <= '9'))
    {
        tem = data - '0';
        max7219_api.max7219_api_find((uint8_t*)"decode");
        max7219_api.max7219_api(DECODE_D0_D7);
    }
    else if(tem == '-')
    {
        tem = 0x0A;
        max7219_api.max7219_api_find((uint8_t*)"decode");
        max7219_api.max7219_api(DECODE_D0_D7);
    }
    else if(tem == 'E')
    {
        tem = 0x0B;
        max7219_api.max7219_api_find((uint8_t*)"decode");
        max7219_api.max7219_api(DECODE_D0_D7);
    }
    else if(tem == 'H')
    {
        tem = 0x0C;
        max7219_api.max7219_api_find((uint8_t*)"decode");
        max7219_api.max7219_api(DECODE_D0_D7);
    }
    else if(tem == 'L')
    {
        tem = 0x0D;
        max7219_api.max7219_api_find((uint8_t*)"decode");
        max7219_api.max7219_api(DECODE_D0_D7);
    }
    else if(tem == 'P')
    {
        tem = 0x0E;
        max7219_api.max7219_api_find((uint8_t*)"decode");
        max7219_api.max7219_api(DECODE_D0_D7);
    }
    else if(tem == ' ')
    {
        tem = 0x0F;
        max7219_api.max7219_api_find((uint8_t*)"decode");
        max7219_api.max7219_api(DECODE_D0_D7);
    }
    else
    {
//        tem = data&0x7f;
        for(int i = 0;i < sizeof(to_ascii_tab)/2;i++)
        {
            if (tem == to_ascii_tab[i][0])
            {
                tem = to_ascii_tab[i][1];
                break;
            }
        }
//        tem |= data&(Segment_Line_DP);
        
        max7219_api.max7219_api_find((uint8_t*)"decode");
        max7219_api.max7219_api(NO_DECODE_D0_D7);
    }
    task_delay(50);//注意:延迟函数,自己实现!!!
    max7219_send(digit-'0' + 1,tem|(data&(Segment_Line_DP)));//这里加 1 因为1对应digit0
    task_delay(10);//注意:延迟函数,自己实现!!!
    return 0;
}

/* 显示检测 
 * mode:
 *  0 -> 正常模式
 *  1 -> 测试全部显示
 */
int max7219_test(uint8_t mode)
{
    max7219_send(DISPLAY_TEST,mode == NORMAL_MODE?0:1);
    return 0;
}

/* 设置api */
int max7219_api_find(uint8_t* mode)
{
    max7219_api.max7219_api = NULL;
    if (!strcmp((char*)mode,"shutdown"))
    {
        max7219_api.max7219_api = max7219_shutdown;
    }
    else if(!strcmp((char*)mode,"scan"))
    {
        max7219_api.max7219_api = max7219_scan_limit;
    }
    else if(!strcmp((char*)mode,"test"))
    {
        max7219_api.max7219_api = max7219_test;
    }
    else if(!strcmp((char*)mode,"decode"))
    {
        max7219_api.max7219_api = max7219_decode_mode;
    }
    else if(!strcmp((char*)mode,"intensity"))
    {
        max7219_api.max7219_api = max7219_intensity;
    }
    else
    {
        MAX7219_LOG("max7219_api_find error :%s",mode);
    }
    
    if(!max7219_api.max7219_api)
        return -1;
    return 0;
}

max7219TypeDef max7219_api = {
.max7219_api_find = max7219_api_find,
.max7219_digit = max7219_digit,
};

max7219_digit 函数直接输入字符,注释写的很清楚
使用例子

void led_display_task  (void*arg)
{
    delay(100);
    
    max7219_api.max7219_api_find((uint8_t*)"shutdown");
    max7219_api.max7219_api(NORMAL_MODE);
    
    max7219_api.max7219_api_find((uint8_t*)"intensity");
    max7219_api.max7219_api(0x8);
    
    max7219_api.max7219_api_find((uint8_t*)"test");
    max7219_api.max7219_api(1);
    
    delay(500);
    
    max7219_api.max7219_api(NORMAL_MODE);

    max7219_api.max7219_api_find((uint8_t*)"decode");
    max7219_api.max7219_api(NO_DECODE_D0_D7);
    
    max7219_api.max7219_api_find((uint8_t*)"scan");
    max7219_api.max7219_api(3);
    
    while (1)
    {
        max7219_api.max7219_digit('0','0'|Segment_Line_DP);// |Segment_Line_DP 是带小数点
        max7219_api.max7219_digit('1','1');
        max7219_api.max7219_digit('2','2');
        max7219_api.max7219_digit('3','3');

        max7219_api.max7219_digit('2','='|Segment_Line_DP);
        max7219_api.max7219_digit('3','-'|Segment_Line_DP);
        max7219_api.max7219_digit('3','+'|Segment_Line_DP);
        max7219_api.max7219_digit('3','('|Segment_Line_DP);
        max7219_api.max7219_digit('3',')'|Segment_Line_DP);
    }
}

注意: 改变编码方式时,原有的显示会有影响,比如 1 位置显示 bcd编码,2位置想要更新为 无编码模式,那1 位置也将变为无编码模式。

  • 17
    点赞
  • 85
    收藏
    觉得还不错? 一键收藏
  • 15
    评论
以下是使用STM32 HAL库实现四段数码管动态显示的代码: ```c #include "main.h" #include "stm32f1xx_hal.h" // 数码管阳极接口,定义数字0-9的数码管段码 const uint8_t DIGITS[10] = { 0b01111110, // 0 0b00110000, // 1 0b01101101, // 2 0b01111001, // 3 0b00110011, // 4 0b01011011, // 5 0b01011111, // 6 0b01110000, // 7 0b01111111, // 8 0b01111011 // 9 }; // 数码管的引脚定义 #define DIGIT1_Pin GPIO_PIN_0 #define DIGIT1_GPIO_Port GPIOA #define DIGIT2_Pin GPIO_PIN_1 #define DIGIT2_GPIO_Port GPIOA #define DIGIT3_Pin GPIO_PIN_2 #define DIGIT3_GPIO_Port GPIOA #define DIGIT4_Pin GPIO_PIN_3 #define DIGIT4_GPIO_Port GPIOA #define SEG_A_Pin GPIO_PIN_4 #define SEG_A_GPIO_Port GPIOA #define SEG_B_Pin GPIO_PIN_5 #define SEG_B_GPIO_Port GPIOA #define SEG_C_Pin GPIO_PIN_6 #define SEG_C_GPIO_Port GPIOA #define SEG_D_Pin GPIO_PIN_7 #define SEG_D_GPIO_Port GPIOA #define SEG_E_Pin GPIO_PIN_8 #define SEG_E_GPIO_Port GPIOA #define SEG_F_Pin GPIO_PIN_9 #define SEG_F_GPIO_Port GPIOA #define SEG_G_Pin GPIO_PIN_10 #define SEG_G_GPIO_Port GPIOA #define SEG_DP_Pin GPIO_PIN_11 #define SEG_DP_GPIO_Port GPIOA // 数码管显示缓存数组,用于存储要显示的数字 uint8_t display_buffer[4]; // 数码管显示位置,用于控制哪一个数码管显示 uint8_t display_position = 0; // 定时器回调函数,用于驱动数码管动态显示 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { // 关闭当前数码管 switch (display_position) { case 0: HAL_GPIO_WritePin(DIGIT1_GPIO_Port, DIGIT1_Pin, GPIO_PIN_SET); break; case 1: HAL_GPIO_WritePin(DIGIT2_GPIO_Port, DIGIT2_Pin, GPIO_PIN_SET); break; case 2: HAL_GPIO_WritePin(DIGIT3_GPIO_Port, DIGIT3_Pin, GPIO_PIN_SET); break; case 3: HAL_GPIO_WritePin(DIGIT4_GPIO_Port, DIGIT4_Pin, GPIO_PIN_SET); break; default: break; } // 显示下一个数字 display_position++; if (display_position > 3) { display_position = 0; } switch (display_position) { case 0: HAL_GPIO_WritePin(DIGIT1_GPIO_Port, DIGIT1_Pin, GPIO_PIN_RESET); break; case 1: HAL_GPIO_WritePin(DIGIT2_GPIO_Port, DIGIT2_Pin, GPIO_PIN_RESET); break; case 2: HAL_GPIO_WritePin(DIGIT3_GPIO_Port, DIGIT3_Pin, GPIO_PIN_RESET); break; case 3: HAL_GPIO_WritePin(DIGIT4_GPIO_Port, DIGIT4_Pin, GPIO_PIN_RESET); break; default: break; } // 显示当前数字 uint8_t digit = display_buffer[display_position]; HAL_GPIO_WritePin(SEG_A_GPIO_Port, SEG_A_Pin, (digit & 0x01) ? GPIO_PIN_RESET : GPIO_PIN_SET); HAL_GPIO_WritePin(SEG_B_GPIO_Port, SEG_B_Pin, (digit & 0x02) ? GPIO_PIN_RESET : GPIO_PIN_SET); HAL_GPIO_WritePin(SEG_C_GPIO_Port, SEG_C_Pin, (digit & 0x04) ? GPIO_PIN_RESET : GPIO_PIN_SET); HAL_GPIO_WritePin(SEG_D_GPIO_Port, SEG_D_Pin, (digit & 0x08) ? GPIO_PIN_RESET : GPIO_PIN_SET); HAL_GPIO_WritePin(SEG_E_GPIO_Port, SEG_E_Pin, (digit & 0x10) ? GPIO_PIN_RESET : GPIO_PIN_SET); HAL_GPIO_WritePin(SEG_F_GPIO_Port, SEG_F_Pin, (digit & 0x20) ? GPIO_PIN_RESET : GPIO_PIN_SET); HAL_GPIO_WritePin(SEG_G_GPIO_Port, SEG_G_Pin, (digit & 0x40) ? GPIO_PIN_RESET : GPIO_PIN_SET); HAL_GPIO_WritePin(SEG_DP_GPIO_Port, SEG_DP_Pin, GPIO_PIN_SET); } int main(void) { HAL_Init(); // 初始化GPIO口 GPIO_InitTypeDef GPIO_InitStructure; __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStructure.Pin = DIGIT1_Pin | DIGIT2_Pin | DIGIT3_Pin | DIGIT4_Pin; GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.Pin = SEG_A_Pin | SEG_B_Pin | SEG_C_Pin | SEG_D_Pin | SEG_E_Pin | SEG_F_Pin | SEG_G_Pin | SEG_DP_Pin; GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化定时器 TIM_HandleTypeDef htim; __HAL_RCC_TIM2_CLK_ENABLE(); htim.Instance = TIM2; htim.Init.Period = 1000; // 定时器周期为1ms htim.Init.Prescaler = 72 - 1; // 定时器预分频系数为72 htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim.Init.CounterMode = TIM_COUNTERMODE_UP; HAL_TIM_Base_Init(&htim); HAL_TIM_Base_Start_IT(&htim); while (1) { // 更新显示缓存数组 for (uint8_t i = 0; i < 10; i++) { display_buffer[0] = DIGITS[i]; display_buffer[1] = DIGITS[(i + 1) % 10]; display_buffer[2] = DIGITS[(i + 2) % 10]; display_buffer[3] = DIGITS[(i + 3) % 10]; // 延时,调整显示速度 HAL_Delay(500); } } } ``` 以上代码中,首先定义了数码管阳极接口和数字0-9的数码管段码。然后定义了数码管引脚的宏定义,以及显示缓存数组和当前显示位置的全局变量。在定时器回调函数`HAL_TIM_PeriodElapsedCallback`中,每隔一定时间切换要显示的数码管,并将对应的数字显示在数码管上。在主循环中,通过循环更新显示缓存数组,实现数码管动态显示。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值