基于STM32通过TM1637驱动4位数码管详细解析(可直接移植使用)

目录

[1. 单位数码管概述](#1. 单位数码管概述)

[2. 对应编码](#2. 对应编码)

[2.1 共阳数码管](#2.1 共阳数码管)

[2.2 共阴数码管](#2.2 共阴数码管)

[3. TM1637驱动数码管](#3. TM1637驱动数码管)

[3.1 工作原理](#3.1 工作原理)

[3.1.1 读键扫数据](#3.1.1 读键扫数据)

[3.1.2 显示器寄存器地址和显示模式](#3.1.2 显示器寄存器地址和显示模式)

[3.2 时序](#3.2 时序)

[3.2.1 指令数据传输过程(读案件数据时序)](#3.2.1 指令数据传输过程(读案件数据时序))

[3.2.2 写SRAM数据地址自动加1模式](#3.2.2 写SRAM数据地址自动加1模式)

[3.2.3 写SRAM数据固定地址模式](#3.2.3 写SRAM数据固定地址模式)

[3.3 数据指令](#3.3 数据指令)

[3.3.1 数据命令设置](#3.3.1 数据命令设置)

[3.3.2 显示控制命令设置](#3.3.2 显示控制命令设置)

[3.3.3 地址命令设置](#3.3.3 地址命令设置)

[3.4 程序流程图](#3.4 程序流程图)

[3.4.1 采用地址自动加一模式的程序流程图](#3.4.1 采用地址自动加一模式的程序流程图)

[3.4.2 采用固定地址的程序设计流程图](#3.4.2 采用固定地址的程序设计流程图)

[4. 代码编写](#4. 代码编写)

[4.1 准备配置](#4.1 准备配置)

[4.1.1 C语言调用文件](#4.1.1 C语言调用文件)

[4.1.2 宏定义](#4.1.2 宏定义)

[4.1.3 段码表](#4.1.3 段码表)

[4.1.4 数码管地址表](#4.1.4 数码管地址表)

[4.1.5 引脚初始化](#4.1.5 引脚初始化)

[4.1.6 电平配置](#4.1.6 电平配置)

[4.2 IIC相关配置](#4.2 IIC相关配置)

[4.2.1 起始信号](#4.2.1 起始信号)

[4.2.2 终止信号](#4.2.2 终止信号)

[4.2.3 接收应答](#4.2.3 接收应答)

[4.2.4 发送一个字节](#4.2.4 发送一个字节)

[4.3 TM1637相关配置](#4.3 TM1637相关配置)

[4.3.1 发送写显存的数据命令](#4.3.1 发送写显存的数据命令)

[4.3.2 向指定地址写入数据](#4.3.2 向指定地址写入数据)

[4.3.3 在四个数码管上显示数据](#4.3.3 在四个数码管上显示数据)

[4.3.4 设置亮度](#4.3.4 设置亮度)

[4.3.5 显示开关](#4.3.5 显示开关)

[4.3.6 初始化配置](#4.3.6 初始化配置)

[4.3.7 显示配置](#4.3.7 显示配置)

[4.3.8 TM1637.h文件配置](#4.3.8 TM1637.h文件配置)

[4.4 主函数](#4.4 主函数)


1. 单位数码管概述

数码管的内部基本单元是发光二极管,数码管是发光器件之一,内部由七个条形发光二极管(a、b、c、d、e、f、g)和一个圆点发光二极管(dp)构成。

按照数码管的公共接线不同,数码管又可分类为共阴极数码管和共阳极数码管两种,共阴极数码管的公共端接地,而共阳极数码管的公共端接电源。

2. 对应编码

2.1 共阳数码管

数码管中所有的正极连接在一起,这个端口被称之为位选端口,其余的数码管引脚a-h都为段选端口2、共阳极数码管的编码(CA):

cs 复制代码
u8 code[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e}; //0-F

2.2 共阴数码管

是数码管所有的负极连接在一起,这个端口被称之为位选端口,其余的数码管引脚a-h都为段选端口4、共阴极数码管的编码(CC/CK):

cs 复制代码
u8 code[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; //0-F

3. TM1637驱动数码管

TM1637 是一种带键盘扫描接口的LED(发光二极管显示器)驱动控制专用电路,内部集成有MCU 数字接口、数据锁存器、LED 高压驱动、键盘扫描等电路。本产品性能优良,质量可靠。主要应用于电磁炉、 微波炉及小家电产品的显示屏驱动。采用DIP/SOP20的封装形式。

3.1 工作原理

微处理器的数据通过两线总线接口和 TM1637 通信,在输入数据时当 CLK 是高电平时,DIO 上的信号必须保持不变;只有 CLK 上的时钟信号为低电平时,DIO 上的信号才能改变。数据输入的开始条件是 CLK 为高电平时,DIO 由高变低;结束条件是 CLK 为高时,DIO 由低电平变为高电平。

TM1637 的数据传输带有应答信号 ACK,当传输数据正确时,会在第八个时钟的下降沿,芯片内部会产生一个应答信号 ACK 将 DIO 管脚拉低,在第九个时钟结束之后释放 DIO 口线。

|--------------|---------|--------|--------------------------------------------------------------------------|
| 符号 | 管脚名称 | 管脚号 | 说明 |
| DIO | 数据输入/输出 | 17 | 串行数据输入/输出,输入数据在 SLCK 的低电平变化,在SCLK 的高电平被传输,每传输一个字节芯片内部都将在第八个时钟下降沿产生一个 ACK |
| CLK | 时钟输入 | 18 | 在上升沿输入/输出数据 |
| K1~K2 | 键扫数据输入 | 19~20 | 输入该脚的数据在显示周期结束后被锁存 |
| SG1~SG8 | 输出(段) | 2~9 | 段输出(也用作键扫描),N 管开漏输出 |
| GRID6~GRID1 | 输出(位) | 10~15 | 位输出,P 管开漏输出 |
| VDD | 逻辑电源 | 16 | 5V±10% |
| GND | 逻辑地 | 1 | 接地 |

3.1.1 读键扫数据

在有按键按下时,读键数据如下:

|----|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|
| | SG1 | SG2 | SG3 | SG4 | SG5 | SG6 | SG7 | SG8 |
| K1 | 1110_1111 | 0110_1111 | 1010_1111 | 0010_1111 | 1100_1111 | 0100_1111 | 1000_1111 | 0000_1111 |
| K2 | 1111_0111 | 0111_0111 | 1011_0111 | 0011_0111 | 1101_0111 | 0101_0111 | 1001_0111 | 0001_0111 |

注意:在无按键按下时,读键数据为:1111_1111,低位在前,高位在后。

3.1.2 显示器寄存器地址和显示模式

该寄存器存储通过串行接口从外部器件传送到TM1637 的数据,地址00H-05H共6个字节单元,分别与芯片SGE和GRID管脚所接的LED灯对应,分配如下图:

写LED显示数据的时候,按照从显示地址低位到高位,从数据字节低位到高位操作。

|------|------|------|------|------|------|------|------|-------|
| SEG1 | SEG2 | SEG3 | SEG4 | SEG5 | SEG6 | SEG7 | SEG8 | |
| xxHL(第四位) |||| xxHU(高四位) |||| |
| B0 | B1 | B2 | B3 | B4 | B5 | B6 | B7 | |
| 00HL |||| 00HU |||| GRID1 |
| 01HL |||| 01HU |||| GRID2 |
| 02HL |||| 02HU |||| GRID3 |
| 03HL |||| 03HU |||| GRID4 |
| 04HL |||| 04HU |||| GRID5 |
| 05HL |||| 05HU |||| GRID6 |

3.2 时序

3.2.1 指令数据传输过程(读案件数据时序)

注意:先读低位在读高位

3.2.2 写SRAM数据地址自动加1模式

Command1:设置数据

Command2:设置地址

Data1~N:传输数据

Command3:控制显示

3.2.3 写SRAM数据固定地址模式

Command1:设置数据

Command2:设置地址

Data1~N:传输数据

Command3:控制显示

3.3 数据指令

指令用来设置显示模式和LED 驱动器的状态。

在CLK下降沿后由DIO输入的第一个字节作为一条指令。经过译码,取最高B7、B6两位比特位以区别不同的指令。

|----|----|----------|
| B7 | B6 | 指令 |
| 0 | 1 | 数据命令设置 |
| 1 | 0 | 显示控制命令设置 |
| 1 | 1 | 地址命令设置 |

如果在指令或数据传输时发送STOP命令,串行通讯被初始化,并且正在传送的指令或数据无效(之前传送的指令或数据保持有效)。

3.3.1 数据命令设置

3.3.2 显示控制命令设置

3.3.3 地址命令设置

3.4 程序流程图

3.4.1 采用地址自动加一模式的程序流程图

3.4.2 采用固定地址的程序设计流程图

4. 代码编写

开始前可以先了解一下IIC的用法,以及相关时序:

STM32F1之I2C通信_stm32f1 i2c-CSDN博客

STM32F1之I2C通信·软件I2C代码编写-CSDN博客

TM1637采用的是IIC通信,但是又不是标准的IIC通信,标准的IIC协议是从高位到低位传输的,即MSB方式,但是TM1637介绍也介绍到工作原理时曾介绍到,其是从低位到高位进行数据传输。

4.1 准备配置

4.1.1 C语言调用文件

STM32的编写,有些需要调用C语言的头文件才能正常使用:

cs 复制代码
#include <string.h>//提供了一些用于处理字符串和内存块的函数。
#include <stdint.h>//定义了具有确定大小的整数类型。
#include <stdbool.h>//提供布尔类型支持。

4.1.2 宏定义

这个宏定义了一个名为 TUBE_DISPLAY_NULL 的常量,值为 26。通常情况下,这个值可能用于表示一个不显示的状态或占位符,在显示器的控制代码中用来指示数字管不应显示任何有效数字:

cs 复制代码
#define TUBE_DISPLAY_NULL							26

这个宏定义了一个名为 TUBE_DISPLAY_DECIMAL_POINT_OFFSET 的常量,值为 16。这个值可能用于表示小数点在显示器中的位置偏移量。例如,在一个七段显示器上,使用这个偏移量可以帮助确定小数点的位置,以便正确显示浮点数或需要小数点的数值:

cs 复制代码
#define TUBE_DISPLAY_DECIMAL_PIONT_OFFSET			16

4.1.3 段码表

用于存储七段显示器中每个数字和字母的段码:

cs 复制代码
const uint8_t u8NumTab[] = 
{
	//0,	1,	 2,	  3,	 4,	  5,	 6,   7,   8,   9,   A,   b,  C,    d,   E,   F,  	
	0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,
	//0., 1.,	 2.,	3.,	 4.,  5.,   6.,  7.,  8.,  9. Null
	0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF,0x00
};

4.1.4 数码管地址表

cs 复制代码
//最左至最右数码管 ,依次为0-3号,对应的显示寄存器地址
const uint8_t u8TubeAddrTab[] = 
{
	0xC0,0xC1,0xC2,0xC3
};

4.1.5 引脚初始化

由于这里我们使用的软件I2C不受引脚限制,随便找两个普通的GPIO口就可以使用,首先我们随机找两个引脚对其进行初始化:

cs 复制代码
void TM1637_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	//开启GPIOB的时钟

	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);					//将PB10和PB11引脚初始化为开漏输出

	/*设置默认电平*/
	GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);			//设置PB10和PB11引脚初始化后默认为高电平(释放总线状态)
}

4.1.6 电平配置

cs 复制代码
/**
  * 函    数:I2C写SCL引脚电平
  * 参    数:BitValue 协议层传入的当前需要写入SCL的电平,范围0~1
  * 返 回 值:无
  * 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SCL为低电平,当BitValue为1时,需要置SCL为高电平
  */
void TM1637_W_SCL(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue);		//根据BitValue,设置SCL引脚的电平
	Delay_us(10);												//延时10us,防止时序频率超过要求
}

/**
  * 函    数:I2C写SDA引脚电平
  * 参    数:BitValue 协议层传入的当前需要写入SDA的电平,范围0~0xFF
  * 返 回 值:无
  * 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SDA为低电平,当BitValue非0时,需要置SDA为高电平
  */
void TM1637_W_SDA(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);		//根据BitValue,设置SDA引脚的电平,BitValue要实现非0即1的特性
	Delay_us(10);												//延时10us,防止时序频率超过要求
}

/**
  * 函    数:I2C读SDA引脚电平
  * 参    数:无
  * 返 回 值:协议层需要得到的当前SDA的电平,范围0~1
  * 注意事项:此函数需要用户实现内容,当前SDA为低电平时,返回0,当前SDA为高电平时,返回1
  */
uint8_t TM1637_R_SDA(void)
{
	uint8_t BitValue;
	BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);		//读取SDA电平
	Delay_us(10);												//延时10us,防止时序频率超过要求
	return BitValue;											//返回SDA电平
}

4.2 IIC相关配置

4.2.1 起始信号

起始条件:SCL高电平期间,SDA从高电平切换到低电平。

根据上图虚线框柱的地方进行配置:

cs 复制代码
void TM1637_Start(void)
{
	TM1637_W_SDA(1);	//释放SDA,确保SDA为高电平
	TM1637_W_SCL(1);	//释放SCL,确保SCL为高电平
	TM1637_W_SDA(0);	//在SCL高电平期间,拉低SDA,产生起始信号
	TM1637_W_SCL(0);	//起始后把SCL也拉低,即为了占用总线,也为了方便总线时序的拼接
}

4.2.2 终止信号

终止条件:SCL高电平期间,SDA从低电平切换到高电平。

同理,根据上图虚线框柱的地方进行配置:

cs 复制代码
void  TM1637_Stop(void)
{
	TM1637_W_SCL(0);  //拉低SCL,确保SCL为低电平
	TM1637_W_SDA(0);	//拉低SDA,确保SDA为低电平
	TM1637_W_SCL(1);	//释放SCL,使SCL呈现高电平
	TM1637_W_SDA(1);	//在SCL高电平期间,释放SDA,产生终止信号
}

4.2.3 接收应答

接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA)

cs 复制代码
//应答信号
uint8_t TM1637_ReceiveAck(void)
{
	uint8_t AckBit;							//定义应答位变量
	TM1637_W_SDA(1);							//接收前,主机先确保释放SDA,避免干扰从机的数据发送
	TM1637_W_SCL(1);							//释放SCL,主机机在SCL高电平期间读取SDA
	AckBit = TM1637_R_SDA();					//将应答位存储到变量里
	TM1637_W_SCL(0);							//拉低SCL,开始下一个时序模块
	return AckBit;							//返回定义应答位变量
}

4.2.4 发送一个字节

发送一个字节: SCL低电平期间,主机将数据位依次放到SDA线上(高位先行) ,然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节。

主机首先在SCL低电平,主机如果想发送0,就拉低SDA到低电平,主机如果想发送1,就放手,SDA回弹到高电平,在SCL低电平期间允许改变SDA的电平,如图黄色部分:

但是这里需要注意标准的IIC协议从高位到低位传输的,但是TM1637从低位到高位进行数据传输,因此:

cs 复制代码
// 向 TM1637 发送一个字节数据
void TM1637_Write_Byte(uint8_t data)
{
    uint8_t i;
    TM1637_W_SCL(0); // 设置时钟线 SCL 为低电平,准备开始发送数据

    // 循环发送 8 位数据
    for (i = 0; i < 8; i++)
    {
        // 根据数据的最低位决定数据线 SDA 的状态
        if(data & 0x01)
        {
            TM1637_W_SDA(1); // 发送 1
        }
        else
        {
            TM1637_W_SDA(0); // 发送 0
        }    
        
        data = data >> 1; // 右移数据,处理下一个位
        
        TM1637_W_SCL(1); // 拉高时钟线,表示位数据有效
        TM1637_W_SCL(0); // 拉低时钟线,准备发送下一个位
    }
}

4.3 TM1637相关配置

为了方便地对任意一个数码管写入数据,我们采用固定地址的方式,其流程如下:

相关时序:

4.3.1 发送写显存的数据命令

cs 复制代码
// 向 TM1637 数码管写入命令
void TM1637_WriteCmd(uint8_t u8Cmd)
{
    // 开始信号,表示通信开始
    TM1637_Start();
    
    // 发送命令字节
    TM1637_Write_Byte(u8Cmd);
    
    // 接收确认应答
    TM1637_ReceiveAck();
    
    // 停止信号,表示通信结束
    TM1637_Stop();
}

4.3.2 向指定地址写入数据

cs 复制代码
// 向指定地址写入数据
// u8Addr: 要写入的地址
// u8Data: 要写入的数据
void TM1637_WriteData(uint8_t u8Addr, uint8_t u8Data)
{
    // 开始信号,表示通信开始
    TM1637_Start();
    
    // 发送地址字节
    TM1637_Write_Byte(u8Addr);
    
    // 接收确认应答
    TM1637_ReceiveAck();
    
    // 发送数据字节
    TM1637_Write_Byte(u8Data);
    
    // 接收确认应答
    TM1637_ReceiveAck();
    
    // 停止信号,表示通信结束
    TM1637_Stop();
}

4.3.3 在四个数码管上显示数据

cs 复制代码
// 在四个数码管上显示数据
// sData: 显示数据结构体,包含四个数码管的显示值
void TM1637_TubeDisplay(TM1637Tube_ts sData)
{
    uint8_t temp[4], i;
    
    // 根据 sData 中的值,从 u8NumTab 数组中获取对应的段码
    temp[0] = u8NumTab[sData.tube0]; // 获取第一个数码管的段码
    temp[1] = u8NumTab[sData.tube1]; // 获取第二个数码管的段码
    temp[2] = u8NumTab[sData.tube2]; // 获取第三个数码管的段码
    temp[3] = u8NumTab[sData.tube3]; // 获取第四个数码管的段码
    
    // 遍历四个数码管,逐个写入数据
    for (i = 0; i < 4; i++)
    {
        // 向每个数码管的地址写入对应的段码
        TM1637_WriteData(u8TubeAddrTab[i], temp[i]);
    }
}

结构体TM1637Tube_ts,该结构体存放到.h中,若是存放到.c文件需要将其放到头文件后面,防止TM1637_TubeDisplay()函数搜索不到:

cs 复制代码
typedef struct 
{
	uint8_t tube0;
	uint8_t tube1;
	uint8_t tube2;
	uint8_t tube3;
}TM1637Tube_ts;

4.3.4 设置亮度

cs 复制代码
//设置亮度
//0x88为开显示,u8Brt亮度
void  TM1637_SetBrightness(uint8_t u8Brt)
{
	TM1637_WriteCmd(0x88 | u8Brt);
}

4.3.5 显示开关

cs 复制代码
//显示开关
//0x88为开显示,0x80关显示
void  TM1637_Switch(bool bState)
{
	bState ? TM1637_WriteCmd(0x88) : TM1637_WriteCmd(0x80);
}

4.3.6 初始化配置

cs 复制代码
void Display_Init(void)
{
	TM1637_Switch(0);//关显示
	TM1637_SetBrightness(0x87);//设置亮度,开显示
	TM1637_WriteCmd(0x44);//写数据到寄存器,固定地址模式
	memset(&sDisplayData, 0xFF, sizeof(sDisplayData));
}

4.3.7 显示配置

cs 复制代码
void Display_TubeDataProcess(uint16_t u16Data)
{
	memset(&sDisplayData, 0xFF, sizeof(sDisplayData));

	if (u16Data > 9999)
	{
		u16Data = 9999;//最多四位数
	}
	if (u16Data > 999)//四位数
	{
		sDisplayData.tube0 = (uint8_t)(u16Data / 1000);//千位
		sDisplayData.tube1 = (uint8_t)(u16Data / 100 % 10);//百位
		sDisplayData.tube2 = (uint8_t)(u16Data % 100 / 10);//十位
	  sDisplayData.tube3 = (uint8_t)(u16Data % 10);//个位
	}
	else if (u16Data > 99)//三位数
	{
		sDisplayData.tube0 = TUBE_DISPLAY_NULL;//不显示
		sDisplayData.tube1 = (uint8_t)(u16Data / 100);//百位
		sDisplayData.tube2 = (uint8_t)(u16Data / 10 % 10);//十位
	    sDisplayData.tube3 = (uint8_t)(u16Data % 10);//个位	   
	}
	else if (u16Data > 9)//两位数
	{
		sDisplayData.tube0 = TUBE_DISPLAY_NULL;//不显示
	  sDisplayData.tube1 = TUBE_DISPLAY_NULL;//不显示
		sDisplayData.tube2 = (uint8_t)(u16Data / 10);//十位
	  sDisplayData.tube3 = (uint8_t)(u16Data % 10);//个位
	}
	else//一位数
	{
		sDisplayData.tube0 = TUBE_DISPLAY_NULL;//不显示
	  sDisplayData.tube1 = TUBE_DISPLAY_NULL;//不显示
		sDisplayData.tube2 = TUBE_DISPLAY_NULL;//不显示
	  sDisplayData.tube3 = (uint8_t)u16Data;//个位
	}
	TM1637_TubeDisplay(sDisplayData);
}

4.3.8 TM1637.h文件配置

以上代码为.c文件配置,下面是.h文件配置:

cs 复制代码
#ifndef _TM1637_H_
#define _TM1637_H_

#include <stdbool.h>

typedef struct 
{
	uint8_t tube0;
	uint8_t tube1;
	uint8_t tube2;
	uint8_t tube3;
}TM1637Tube_ts;

void TM1637_Init(void);
void TM1637_Start(void);
void  TM1637_Stop(void);
uint8_t TM1637_ReceiveAck(void);
void TM1637_Write_Byte(uint8_t data);
void TM1637_WriteCmd(uint8_t u8Cmd);
void TM1637_WriteData(uint8_t u8Addr, uint8_t u8Data);
void TM1637_TubeDisplay(TM1637Tube_ts sData);
void TM1637_SetBrightness(uint8_t u8Brt);
void TM1637_Switch(bool bState);
void Display_Init(void);
void Display_TubeDataProcess(uint16_t u16Data);

#endif

4.4 主函数

cs 复制代码
#include "stm32f10x.h"              
#include "Delay.h"
#include "TM1637.h"


int main(void)
{	
	TM1637_Init();
    Display_Init();	
	
	while (1)
	{
		Display_TubeDataProcess(1234);
	}
}

基于STM32通过SN74HC595驱动4位数码管详细解析-CSDN博客

相关推荐
最后一个bug39 分钟前
如何理解Lua 使用虚拟堆栈
linux·c语言·开发语言·嵌入式硬件·lua
一子二木生三火1 小时前
IO流(C++)
c语言·开发语言·数据结构·c++·青少年编程
时光の尘3 小时前
C语言菜鸟入门·关键字·void的用法
c语言·开发语言·c++·算法·c#·c·关键字
YuCaiH3 小时前
【STM32】MPU6050简介
笔记·stm32·单片机·嵌入式硬件
小林熬夜学编程3 小时前
【Linux系统编程】第四十九弹---日志系统构建指南:从基础结构到时间处理与Log类实现
linux·运维·服务器·c语言·开发语言·c++
DeepAlchemy3 小时前
ROSSERIAL与Arduino IDE交叉开发(UBUNTU环境,包含ESP32、arduino nano)
c++·单片机·ros·rosserial
折枝寄北3 小时前
C指针之舞——指针探秘之旅(2)
c语言·开发语言
陌小呆^O^3 小时前
关于C/C++Windows下连接MYSQL操作
c语言·c++·windows
BigShark8889 小时前
2025蓝桥杯(单片机)备赛--扩展外设之I2C的重要应用--PCF8591(八)
单片机·职场和发展·蓝桥杯