发新帖本帖赏金 100.00元(功能说明)我要提问
12下一页
返回列表
打印
[MM32生态]

【MM32+模块】系列:06、数码管显示(一)

[复制链接]
4071|22
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
#申请原创#   @21小跑堂


在之前的多功能单相/三相电表项目上使用的是LED数码管作为显示接口,借此来跟大家分享一下LED数码管的一些驱动方式。
LED数码管是一种比较常见的显示外设,它由多个发光二极管按照图形顺序进行排列的显示器件;我们常见的有7段、8段数码管等,还有一些定制的多段位数码管,点阵序列的数码管也是其中的一种表现形式;
LED数码管按照极性可分为共阴极数码管和共阳极数码管;共阴极数码管就是所有的显示字段共用一个电源负极,另一端高电平时点亮;共阳极数码管就是所有的显示字段共用一个电源正极,另一端低电平时点亮。
数码管的驱动方式有很多,我们将分两篇来讲述数码管的不同驱动实现方式;本篇主要内容如下:
1、数码管显示驱动之74HC595
2、数码管显示驱动之TM1650
3、数码管显示驱动之HT16K33
4、数码管显示驱动之MAX7219

在本篇开始之前,我们先引入SYS中新添加的一个通用程序:模拟I2C程序(下面简称sI2C,在后续的驱动编写时,需要使用到。sI2C是通过结构体的方式,定义了SCLSDA的端口及引脚,设定了模拟I2C的速度;通过传递结构体参数的方式,来实现模拟I2C的时序控制。这样在一个程序中可以通过定义多个sI2C结构体变量,使用不同的引脚来实现对多个I2C设备的操作,底层的模拟I2C时序操作还仅仅只需要一套代码,在节省了代码空间同时也优化了代码框架。对于类似的驱动方式还可以应用到其它的MCU外设上,后面使用到了,我们再详细说明。如下所示,就是模拟I2C的实现源码:
typedef struct
{
    uint32_t      SCL_RCC;
    GPIO_TypeDef *SCL_PORT;
    uint16_t      SCL_PIN;

    uint32_t      SDA_RCC;
    GPIO_TypeDef *SDA_PORT;
    uint16_t      SDA_PIN;

    uint32_t      TIME;
} sI2C_TypeDef;

#define sI2C_SCL_H(sI2C)    GPIO_WriteBit(sI2C->SCL_PORT, sI2C->SCL_PIN, Bit_SET)
#define sI2C_SCL_L(sI2C)    GPIO_WriteBit(sI2C->SCL_PORT, sI2C->SCL_PIN, Bit_RESET)

#define sI2C_SDA_H(sI2C)    GPIO_WriteBit(sI2C->SDA_PORT, sI2C->SDA_PIN, Bit_SET)
#define sI2C_SDA_L(sI2C)    GPIO_WriteBit(sI2C->SDA_PORT, sI2C->SDA_PIN, Bit_RESET)

#define sI2C_SCL_GET(sI2C)  GPIO_ReadOutputDataBit(sI2C->SCL_PORT, sI2C->SCL_PIN)
#define sI2C_SDA_GET(sI2C)  GPIO_ReadInputDataBit( sI2C->SDA_PORT, sI2C->SDA_PIN)
void sI2C_Delay(uint32_t Cnt)
{
    while(Cnt--);
}

void sI2C_SDA_SetDirection(sI2C_TypeDef *sI2C, uint8_t Direction)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_AHBPeriphClockCmd(sI2C->SDA_RCC, ENABLE);

    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin   = sI2C->SDA_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    if(Direction)   /* Input */
    {
        GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IN_FLOATING;
    }
    else            /* Output */
    {
        GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
    }

    GPIO_Init(sI2C->SDA_PORT, &GPIO_InitStructure);
}

void sI2C_SCL_SetDirection(sI2C_TypeDef *sI2C, uint8_t Direction)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_AHBPeriphClockCmd(sI2C->SCL_RCC, ENABLE);

    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin   = sI2C->SCL_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    if(Direction)   /* Input */
    {
        GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IN_FLOATING;
    }
    else            /* Output */
    {
        GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
    }

    GPIO_Init(sI2C->SCL_PORT, &GPIO_InitStructure);
}

void sI2C_GenerateStart(sI2C_TypeDef *sI2C)
{
    sI2C_SDA_H(sI2C); sI2C_Delay(sI2C->TIME);
    sI2C_SCL_H(sI2C); sI2C_Delay(sI2C->TIME);
    sI2C_SDA_L(sI2C); sI2C_Delay(sI2C->TIME);
    sI2C_SCL_L(sI2C); sI2C_Delay(sI2C->TIME);
}


void sI2C_GenerateStop(sI2C_TypeDef *sI2C)
{
    sI2C_SDA_L(sI2C); sI2C_Delay(sI2C->TIME);
    sI2C_SCL_H(sI2C); sI2C_Delay(sI2C->TIME);
    sI2C_SDA_H(sI2C); sI2C_Delay(sI2C->TIME);

    sI2C_Delay(sI2C->TIME); /* Must delay before next start */
}

void sI2C_PutACK(sI2C_TypeDef *sI2C, uint8_t ack)
{
    if(ack) sI2C_SDA_H(sI2C);   /* NACK */
    else    sI2C_SDA_L(sI2C);   /* ACK  */

    sI2C_Delay(sI2C->TIME);

    sI2C_SCL_H(sI2C); sI2C_Delay(sI2C->TIME);
    sI2C_SCL_L(sI2C); sI2C_Delay(sI2C->TIME);
}

uint8_t sI2C_GetACK(sI2C_TypeDef *sI2C)
{
    uint8_t ack = 0;

    sI2C_SDA_H(sI2C); sI2C_Delay(sI2C->TIME);

    sI2C_SDA_SetDirection(sI2C, 1);

    sI2C_SCL_H(sI2C); sI2C_Delay(sI2C->TIME);

    ack = sI2C_SDA_GET(sI2C);

    sI2C_SCL_L(sI2C); sI2C_Delay(sI2C->TIME);

    sI2C_SDA_SetDirection(sI2C, 0);

    return ack;
}

uint8_t sI2C_ReadByte(sI2C_TypeDef *sI2C)
{
    uint8_t Data = 0;

    sI2C_SDA_H(sI2C);

    sI2C_SDA_SetDirection(sI2C, 1);

    for(uint8_t i = 0; i < 8; i++)
    {
        sI2C_SCL_H(sI2C); sI2C_Delay(sI2C->TIME);

        Data <<= 1;

        if(sI2C_SDA_GET(sI2C))
        {
            Data |= 0x01;
        }

        sI2C_SCL_L(sI2C); sI2C_Delay(sI2C->TIME);
    }

    sI2C_SDA_SetDirection(sI2C, 0);

    return Data;
}

void sI2C_WriteByte(sI2C_TypeDef *sI2C, uint8_t Data)
{
    for(uint8_t i = 0; i < 8; i++)
    {
        if(Data & 0x80) sI2C_SDA_H(sI2C);
        else            sI2C_SDA_L(sI2C);

        Data <<= 1;

        sI2C_SCL_H(sI2C); sI2C_Delay(sI2C->TIME);
        sI2C_SCL_L(sI2C); sI2C_Delay(sI2C->TIME);
    }
}

void sI2C_Init(sI2C_TypeDef *sI2C)
{
    /* Configure Simulate I2C SCL & SDA In Output Mode */
    sI2C_SDA_SetDirection(sI2C, 0);
    sI2C_SCL_SetDirection(sI2C, 0);

    sI2C_SCL_H(sI2C); sI2C_Delay(sI2C->TIME);
    sI2C_SDA_H(sI2C); sI2C_Delay(sI2C->TIME);
}

1、数码管显示驱动之74HC595
很早之前在没有数码管显示驱动芯片时,我们常用的就是使用很多的三极管来搭建驱动电路;这种实现方式占用的MCU引脚资源比较多,一段显示就需要占用一个MCU引脚,一个数码管又需要一个选择引脚,如果要驱动一个8位的8段数码管,就需要16MCU引脚来实现。
后来为了节省资源就使用了74HC595这样的芯片,它可以进行芯片间的级连,加上驱动一个74HC595所需要的引脚数较少,在减少MCU占用引脚资源的同时又大大的提升了可驱动数码管的数量,但缺点和三级管驱动一样,需要通过MCU的程序资源来实现显示和扫描;以常用的8段数码管为例(ABCDEFG DP),如果要驱动88段的数码管时,就需要使用到274HC595芯片,一个芯片做显示段的驱动,另外一个芯片做数码管的选通控制。
在淘宝上买了一个由274HC595驱动的88段数码管,结合MM32F0140的核心板,进行连接驱动和显示:
1.1、数码管显示驱动之74HC595驱动代码:
#define TM74HC595_ST_H()    GPIO_WriteBit(GPIOB, GPIO_Pin_0,  Bit_SET)
#define TM74HC595_ST_L()    GPIO_WriteBit(GPIOB, GPIO_Pin_0,  Bit_RESET)

#define TM74HC595_CLK_H()   GPIO_WriteBit(GPIOB, GPIO_Pin_1,  Bit_SET)
#define TM74HC595_CLK_L()   GPIO_WriteBit(GPIOB, GPIO_Pin_1,  Bit_RESET)

#define TM74HC595_SER_H()   GPIO_WriteBit(GPIOB, GPIO_Pin_2,  Bit_SET)
#define TM74HC595_SER_L()   GPIO_WriteBit(GPIOB, GPIO_Pin_2,  Bit_RESET)

#define TM74HC595_OE_H()    GPIO_WriteBit(GPIOB, GPIO_Pin_10, Bit_SET)
#define TM74HC595_OE_L()    GPIO_WriteBit(GPIOB, GPIO_Pin_10, Bit_RESET)

uint16_t TM74HC595_RAM[8] =
{
    0xFE00,0xFD00,0xFB00,0xF700,0xEF00,0xDF00,0xBF00,0x7F00,
};

void TM74HC595_Write(uint16_t Data)
{
    TM74HC595_ST_L();

    for(uint8_t i = 0; i < 16; i++)
    {
        if((Data << i) & 0x8000)  TM74HC595_SER_H();
        else                      TM74HC595_SER_L();

        TM74HC595_CLK_L();

        TM74HC595_CLK_H();
    }

    TM74HC595_ST_H();
}

void TM74HC595_DisplayChar(uint8_t Index, char ch)
{
    uint8_t  Data = 0x00;

    if(Index > 8) return;

    for(uint8_t i = 0; i < 38; i++)
    {
        if(DIGITRON_TABLE[i].ch == ch)
        {
            Data = DIGITRON_TABLE[i].Data;
        }
    }

    TM74HC595_RAM[Index] &= 0xFF00;
    TM74HC595_RAM[Index] |= Data;
}

void TM74HC595_DisplayString(char *str)
{
    uint8_t Index = 0;

    while(*str != '\0')
    {
        if(Index == 8) break;

        TM74HC595_DisplayChar(Index++, *str++);
    }
}

void TM74HC595_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOB, ENABLE);

    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    TM74HC595_OE_L();

    TM74HC595_DisplayString("--------");
}

void TM74HC595_Handler(void)
{
    static uint8_t Index = 0;

    TM74HC595_Write(TM74HC595_RAM[Index]);

    Index = (Index + 1) % 8;
}

void HMI_Init(void)
{
    TM74HC595_Init();

    TASK_Append(TASK_ID_HMI, HMI_Handler, 2);
}

void HMI_Handler(void)
{
    static uint32_t Count1 = 0;
    static uint32_t Count2 = 0;
    char   Buffer[10];

    TM74HC595_Handler();

    if(Count1++ >= 100)
    {
        Count1 = 0;

        memset(Buffer, 0, sizeof(Buffer));
        sprintf(Buffer, "%08d", Count2++);

        TM74HC595_DisplayString(Buffer);
    }
}
1.2、数码管显示驱动之74HC595运行效果:
2、数码管显示驱动之TM1650
TM1650是一种带键盘扫描接口的LED(发光二极管显示器)驱动控制专用电路,支持两路显示模式:8*4位、7*4位;提供了8级亮度控制,工作电压在2.8V~5.5V之间均可正常工作。TM1650采用2线串行传输协议进行通讯,类I2C的形式,使用模拟I2C实现更易于编程操作。
由于淘宝上购买的TM1650显示模块仅仅是显示,并没有带有按键部分,所以我们下面的代码只驱动了显示部分,按键部分的功能,我们会在下篇使用TM1638的模块进行分享。
2.1、数码管显示驱动之TM1650驱动代码:
sI2C_TypeDef sI2C_TM1650 = 
{
    RCC_AHBENR_GPIOB, GPIOB, GPIO_Pin_10,
    RCC_AHBENR_GPIOB, GPIOB, GPIO_Pin_11,
    500
};

void TM1650_WriteCMD(sI2C_TypeDef *sI2C, uint16_t Command)
{
    sI2C_GenerateStart(sI2C);

    sI2C_WriteByte(sI2C, (Command >> 8) & 0xFF);

    if(sI2C_GetACK(sI2C))
    {
        sI2C_GenerateStop(sI2C);    return;
    }

    sI2C_WriteByte(sI2C, (Command >> 0) & 0xFF);

    if(sI2C_GetACK(sI2C))
    {
        sI2C_GenerateStop(sI2C);    return;
    }

    sI2C_GenerateStop(sI2C);
}

void TM1650_DisplayString(char *str)
{
    uint8_t i = 0, j = 0, Data[4];

    memset(Data, 0, sizeof(Data));

    for(i = 0; i < 4; i++)
    {
        for(j = 0; j < 38; j++)
        {
            if(DIGITRON_TABLE[j].ch == str[i])
            {
                Data[i] = DIGITRON_TABLE[j].Data;
            }
        }
    }

    TM1650_WriteCMD(&sI2C_TM1650, TM1650_DIG0 | Data[0]);
    TM1650_WriteCMD(&sI2C_TM1650, TM1650_DIG1 | Data[1]);
    TM1650_WriteCMD(&sI2C_TM1650, TM1650_DIG2 | Data[2]);
    TM1650_WriteCMD(&sI2C_TM1650, TM1650_DIG3 | Data[3]);
}

void TM1650_DisplayChar(uint8_t Index, char ch)
{
    uint8_t  Data       = 0;
    uint16_t Command[4] = {TM1650_DIG0, TM1650_DIG1, TM1650_DIG2, TM1650_DIG3};

    if(Index > 3) Index = 3;

    for(uint8_t i = 0; i < 38; i++)
    {
        if(DIGITRON_TABLE[i].ch == ch)
        {
            Data = DIGITRON_TABLE[i].Data;
        }
    }

    TM1650_WriteCMD(&sI2C_TM1650, Command[Index] | Data);
}

void TM1650_Init(void)
{
    sI2C_Init(&sI2C_TM1650);

    TM1650_WriteCMD(&sI2C_TM1650, TM1650_SYSON_7_8SEG_ON);

    TM1650_DisplayString("----");
}

void TM1650_Handler(void)
{
    static uint16_t Count = 0;

    char Number[10];

    memset(Number, 0, sizeof(Number));
    sprintf(Number,  "%03d0",  Count);

    TM1650_DisplayString(Number);

    Count = (Count + 0x1) % 1000;
}

void HMI_Init(void)
{
    TM1650_Init();

    TASK_Append(TASK_ID_HMI, HMI_Handler, 100);
}

void HMI_Handler(void)
{
    TM1650_Handler();
}
2.2、数码管显示驱动之TM1650运行效果:
3、数码管显示驱动之HT16K33
HT16K33是合泰推出的一个带有显示缓存和按键检测功能的LED控制器驱动芯片,最大支持128段位显示(16个段和8个公共端)和13*3矩阵按键扫描;此外HT16K33还具有16级显示亮度调节、闪烁频率控制等功能;HT16K33采用I2C的通讯方式,I2C从机地址可根据要求进行硬件配置,减少I2C地址冲突。
3.1、数码管显示驱动之HT16K33驱动代码:
void HT16K33_WriteCMD(uint8_t Command)
{
    I2C_SendData(I2C1, Command);
    while(!I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFE));

    I2C_GenerateSTOP(I2C1, ENABLE);
    while(!I2C_GetFlagStatus(I2C1, I2C_FLAG_STOP_DET));
}

void HT16K33_WriteDAT(uint8_t Address, uint8_t *Buffer, uint8_t Length)
{
    I2C_SendData(I2C1, Address);
    while(!I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFE));

    for(uint8_t i = 0; i < Length; i++)
    {
        I2C_SendData(I2C1, Buffer[i]);
        while(!I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFE));

        I2C_SendData(I2C1, 0x00);
        while(!I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFE));
    }

    I2C_GenerateSTOP(I2C1, ENABLE);
    while(!I2C_GetFlagStatus(I2C1, I2C_FLAG_STOP_DET));
}

void HT16K33_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    I2C_InitTypeDef  I2C1_InitStructure;

    uint8_t Heart[8] =
    {
        0x00, 0x18, 0x24, 0x42, 0x81, 0x99, 0x66, 0x00
    };

    RCC_APB1PeriphClockCmd(RCC_APB1ENR_I2C1, ENABLE);

    I2C_StructInit(&I2C1_InitStructure);
    I2C1_InitStructure.I2C_Mode       = I2C_Mode_MASTER;
    I2C1_InitStructure.I2C_OwnAddress = 0;
    I2C1_InitStructure.I2C_Speed      = I2C_Speed_STANDARD;
    I2C1_InitStructure.I2C_ClockSpeed = 100000;
    I2C_Init(I2C1, &I2C1_InitStructure);

    I2C_Send7bitAddress(I2C1, 0xE0, I2C_Direction_Transmitter);

    I2C_Cmd(I2C1, ENABLE);

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOB, ENABLE);

    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_10 | GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_OD;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_1);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_1);

    HT16K33_WriteCMD(0x21); /* System Setup  */
    HT16K33_WriteCMD(0x81); /* Display Setup */
    HT16K33_WriteCMD(0xEA); /* Dimming Set   */

    HT16K33_WriteDAT(0x00, Heart, 0x08);
}

void HT16K33_Handler(void)
{
    uint8_t NUM_FONT[10][8] =
    {
        {0x3E,0x22,0x22,0x22,0x22,0x22,0x3E,0x00},
        {0x04,0x0C,0x04,0x04,0x04,0x04,0x0E,0x00},
        {0x1C,0x22,0x02,0x04,0x08,0x10,0x3E,0x00},
        {0x1C,0x22,0x02,0x04,0x02,0x22,0x1C,0x00},
        {0x04,0x0C,0x14,0x24,0x3E,0x04,0x04,0x00},
        {0x3E,0x20,0x20,0x3E,0x02,0x02,0x3E,0x00},
        {0x3E,0x20,0x20,0x3E,0x22,0x22,0x3E,0x00},
        {0x3E,0x02,0x04,0x08,0x08,0x08,0x08,0x00},
        {0x3E,0x22,0x22,0x3E,0x22,0x22,0x3E,0x00},
        {0x3E,0x22,0x22,0x3E,0x02,0x02,0x3E,0x00},
    };

    static uint8_t Index = 0;

    HT16K33_WriteDAT(0x00, NUM_FONT[Index], 0x08);

    Index = (Index + 1) % 10;
}

void HMI_Init(void)
{
    HT16K33_Init();

    TASK_Append(TASK_ID_HMI, HMI_Handler, 500);
}

void HMI_Handler(void)
{
    HT16K33_Handler();
}
3.2、数码管显示驱动之HT16K33运行效果:
4、数码管显示驱动之MAX7219
MAX7219是美信推出的共阴极LED显示驱动芯片,使用3线SPI串行接口进行通讯控制。MAX7219内含硬件动态扫描电路、BCD译码器、段位驱动器;内部还含有8*8位静态RAM用于存放8个数字的显示数据,还可以直接驱动64段的LED点阵显示;此外MAX7219芯片之间还可以进行级连,用于控制显示更多的数码管或者点阵LED
4.1、数码管显示驱动之MAX7219驱动代码:
void MAX7219_InitSPI1(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    SPI_InitTypeDef  SPI_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2ENR_SPI1, ENABLE);

    SPI_StructInit(&SPI_InitStructure);
    SPI_InitStructure.SPI_Mode              = SPI_Mode_Master;
    SPI_InitStructure.SPI_DataSize          = SPI_DataSize_8b;
    SPI_InitStructure.SPI_DataWidth         = 8;
    SPI_InitStructure.SPI_CPOL              = SPI_CPOL_High;
    SPI_InitStructure.SPI_CPHA              = SPI_CPHA_2Edge;
    SPI_InitStructure.SPI_NSS               = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
    SPI_InitStructure.SPI_FirstBit          = SPI_FirstBit_MSB;
    SPI_Init(SPI1, &SPI_InitStructure);

    SPI_Cmd(SPI1, ENABLE);

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_0);   /* PA4 SPI1_NSS  */
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_0);   /* PA5 SPI1_SCK  */
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_0);   /* PA6 SPI1_MISO */
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_0);   /* PA7 SPI1_MOSI */

    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IPU;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    SPI_BiDirectionalLineConfig(SPI1, SPI_Direction_Rx);
    SPI_BiDirectionalLineConfig(SPI1, SPI_Direction_Tx);
}

uint8_t MAX7219_SPI1_SendData(uint8_t Data)
{
    SPI_SendData(SPI1, Data);
    while(!SPI_GetFlagStatus(SPI1, SPI_FLAG_TXEPT));

    while(!SPI_GetFlagStatus(SPI1, SPI_FLAG_RXAVL));
    return SPI_ReceiveData(SPI1);

}

void MAX7219_Write(uint8_t Address, uint8_t Data)
{
    SPI_CSInternalSelected(SPI1, ENABLE);

    MAX7219_SPI1_SendData(Address);

    MAX7219_SPI1_SendData(Data);

    SPI_CSInternalSelected(SPI1, DISABLE);
}

void MAX7219_DisplayChar(uint8_t Index, char ch)
{
    char CodeBFont[16] = {'0','1','2','3','4','5','6','7','8','9','-','E','H','L','P',' ',};

    for(uint8_t i = 0; i < 16; i++)
    {
        if(ch == CodeBFont[i])
        {
            MAX7219_Write(Index, i);
        }
    }
}

void MAX7219_DisplayString(char *str)
{
    uint8_t Index = 8;

    while(*str != '\0')
    {
        if(Index == 0) break;

        MAX7219_DisplayChar(Index--, *str++);
    }
}

void MAX7219_Init(void)
{
    MAX7219_InitSPI1();

    MAX7219_Write(0x09, 0xFF);  /* Decode Mode  */
    MAX7219_Write(0x0A, 0x03);  /* Intensity    */
    MAX7219_Write(0x0B, 0x07);  /* Scan Limit   */
    MAX7219_Write(0x0C, 0x01);  /* Shut Down    */
    MAX7219_Write(0x0F, 0x00);  /* Display Test */

    MAX7219_DisplayString("--------");
}

void MAX7219_Handler(void)
{
    static uint16_t Count = 0;
    char   Number[10];

    memset(Number, 0, sizeof(Number));
    sprintf(Number, "%08d",  Count++);

    MAX7219_DisplayString(Number);
}

void HMI_Init(void)
{
    MAX7219_Init();

    TASK_Append(TASK_ID_HMI, HMI_Handler, 100);
}

void HMI_Handler(void)
{
    MAX7219_Handler();
}
4.2、数码管显示驱动之MAX7219运行效果:

附件:
TM74HC595数据手册: TM74HC595_V1.2.PDF (987.3 KB)
TM1650数据手册: TM1650_V1.10.PDF (599.45 KB)
HT16K33数据手册: HT16K33.PDF (1.21 MB)
MAX7219数据手册: MAX7219CWG.PDF (179.88 KB)
软件工程源代码: NIXIETUBE1.zip (716.09 KB)



使用特权

评论回复

打赏榜单

21小跑堂 打赏了 50.00 元 2022-04-15

21小跑堂 打赏了 50.00 元 2022-04-14
理由:恭喜通过原创文章审核!请多多加油哦!

沙发
yangxiaor520| | 2022-4-12 21:07 | 只看该作者
现在数码管都很少用到了

使用特权

评论回复
板凳
xld0932|  楼主 | 2022-4-12 21:37 | 只看该作者
yangxiaor520 发表于 2022-4-12 21:07
现在数码管都很少用到了

看应用领域的

使用特权

评论回复
地板
xld0932|  楼主 | 2022-4-13 11:48 | 只看该作者
Kevinsirlee 发表于 2022-4-13 09:35
专业IC 原装进口 从业10年
NXP、ST、ON、TI、INFINEON、等半导体代理分销商,现
致力于汽车电子产品的领域 ...

你代理MM32就找你买,在MM32分论坛发其它的代理广告不好吧

使用特权

评论回复
5
www5911839| | 2022-4-13 14:10 | 只看该作者
楼主,这系列教程质量太高了,请问下会一直连载下去吗,后续会将那些内容?
请问有微信公众号或B站吗,想关注一波

使用特权

评论回复
6
xld0932|  楼主 | 2022-4-13 17:33 | 只看该作者
本帖最后由 xld0932 于 2022-5-11 11:22 编辑
www5911839 发表于 2022-4-13 14:10
楼主,这系列教程质量太高了,请问下会一直连载下去吗,后续会将那些内容?
请问有微信公众号或B站吗,想关 ...

前期会使用自己做的MM32核心板,结合市面上常见的模块来做应用分享;

使用特权

评论回复
7
xld0932|  楼主 | 2022-4-13 20:24 | 只看该作者
Kevinsirlee 发表于 2022-4-13 20:18
感谢楼主,希望有机会合作,资源交换,共享

使用特权

评论回复
8
huquanz711| | 2022-4-14 08:43 | 只看该作者
现在数码管基本上已经退出历史舞台了

使用特权

评论回复
9
xld0932|  楼主 | 2022-4-14 08:48 | 只看该作者
huquanz711 发表于 2022-4-14 08:43
现在数码管基本上已经退出历史舞台了

存在即有市场

使用特权

评论回复
10
shifeng88| | 2022-4-16 22:18 | 只看该作者
LED数码管有市场。

使用特权

评论回复
11
shifeng88| | 2022-4-16 22:21 | 只看该作者
MM32性价比好!

使用特权

评论回复
12
麻花油条| | 2022-4-26 15:07 | 只看该作者
干货满满,这一系列教程太棒了

使用特权

评论回复
13
xld0932|  楼主 | 2022-4-26 18:14 | 只看该作者
麻花油条 发表于 2022-4-26 15:07
干货满满,这一系列教程太棒了

使用特权

评论回复
14
tpgf| | 2022-5-3 11:31 | 只看该作者
常用的是哪种数码管啊

使用特权

评论回复
15
guanjiaer| | 2022-5-3 11:38 | 只看该作者
性价比还是相当高的

使用特权

评论回复
16
heimaojingzhang| | 2022-5-3 11:47 | 只看该作者
市场相当广阔了

使用特权

评论回复
17
keaibukelian| | 2022-5-3 11:56 | 只看该作者
有没有可能最大化的节省引脚的占用呢

使用特权

评论回复
18
labasi| | 2022-5-3 12:02 | 只看该作者
很多低成本的都在用啊

使用特权

评论回复
19
paotangsan| | 2022-5-3 12:08 | 只看该作者
现在大屏幕都是用的这种吧

使用特权

评论回复
20
xld0932|  楼主 | 2022-5-3 15:12 | 只看该作者
paotangsan 发表于 2022-5-3 12:08
现在大屏幕都是用的这种吧

实现的方案有很多,帖子中只是买了些常见的模块来分享一下的

使用特权

评论回复
发新帖 本帖赏金 100.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:上海灵动微电子股份有限公司资深现场应用工程师
简介:诚信·承诺·创新·合作

70

主题

3001

帖子

31

粉丝