DS18B20 温度采集 + LCD1602 分页显示

一、项目概述

1.1 实验名称

DS18B20 温度传感器综合实验(LCD 分页显示 + 多阈值提示 + 串口打印 + LED 状态指示)

1.2 硬件平台

STM32F103(系统时钟 72MHz,标准库 StdPeriph)

1.3 硬件接线

表格

外设 STM32 引脚 说明
DS18B20 数据 DQ PA15(J14-P37) 单总线通讯
LCD1602 控制脚 PB0(RS)、PB1(RW)、PB2(E) 8 位 / 4 位接口可选
LCD1602 数据脚 PB8~PB15 D0~D7
LED1 自定义 LED 引脚(led.h) 200ms 周期闪烁
USART1 PA9(TX)、PA10(RX) 9600 波特串口输出温度

1.4 核心功能

  1. DS18B20 硬件上电自检,无传感器持续报错提示;
  2. 每 500ms 采集一次环境温度,串口实时打印正负温度;
  3. LED1 每 200ms 翻转,作为系统运行心跳指示灯;
  4. LCD1602 双页面自动切换(3 秒翻页一次):
    • 页面 0:实时温度数值显示
    • 页面 1:多区间温度温馨提示;
  5. 温度阈值分级提示:低温保暖、舒适、通风、高温防暑四级;
  6. 支持正负温度换算,DS18B20 单总线标准读写时序;
  7. SysTick 毫秒 / 微秒高精度延时,串口重定向 printf 输出。

1.5 运行时序参数

  • 系统基础循环:10ms
  • LED 闪烁周期:200ms(20 个 10ms 计数)
  • 温度采集周期:500ms(50 个 10ms 计数)
  • LCD 页面切换周期:3000ms(300 个 10ms 计数)

二、宏定义与全局参数说明

2.1 温度阈值宏(main.c)

c

运行

复制代码
#define TEMP_LOW     18.0f    // 低温分界,低于此值提示保暖
#define TEMP_COMORT  26.0f    // 舒适温度上限
#define TEMP_HIGH    32.0f    // 高温预警分界

温度分级逻辑:

  1. temp < 18.0℃:Keep Warm! 气温偏低,注意保暖
  2. 18.0 ≤ temp <26.0℃:Very Comfort 舒适温度区间
  3. 26.0 ≤ temp <32.0℃:Ventilate! 温度偏高,建议通风
  4. temp ≥32.0℃:Heat Stroke! 高温防暑预警

2.2 核心全局变量(main 主函数)

表格

变量名 类型 作用
tick_cnt uint16_t 10ms 基础计数器,驱动 LED、温度采集定时
temp_val float 存储 DS18B20 读取的浮点温度
lcd_buf16 char LCD 字符串缓存,存放温度 / 提示文本
page_flag uint8_t 页面标记:0 = 温度页,1 = 提示页
page_timer uint16_t 翻页计时器,满 300 清零切换页面

三、分模块代码详细说明

模块 1:main.c 主逻辑模块

3.1 外设初始化流程
  1. SysTick_Init(72):配置 72MHz 系统时钟延时分频,提供delay_ms/delay_us
  2. NVIC_PriorityGroupConfig:中断优先级分组 2;
  3. LED_Init():初始化 LED 指示灯 IO;
  4. USART1_Init(9600):串口 1 初始化,开启收发中断,重定向 printf;
  5. lcd1602_init():LCD1602 屏幕初始化,清屏、设置显示模式;
  6. DS18B20 硬件检测循环:反复复位读取传感器,异常则 LCD 报错 + 串口打印。
3.2 主循环定时逻辑(10ms 单次循环)
  1. 计数器自增、延时 10ms;
  2. LED 心跳:每 20 次循环(200ms)翻转 LED 电平;
  3. 翻页计时:page_timer 计数到 300(3s)切换页面标记,清屏;
  4. 温度采集:每 50 次循环(500ms)读取一次 DS18B20 温度;
  5. 串口打印:区分正负温度格式化输出;
  6. LCD 页面渲染:根据 page_flag 刷新对应页面文字。
3.3 LCD 分页显示逻辑
  • 页面 0(温度页):第一行固定标题Current Temp,第二行打印浮点温度;
  • 页面 1(提示页):第一行标题Warm Tip:,第二行根据温度阈值匹配提示字符串;
  • 页面切换时执行lcd1602_clear()清屏,避免文字重叠。
3.4 负温度处理逻辑

DS18B20 负数为补码存储,读取后取反 + 1 换算绝对值,输出负号。

模块 2:ds18b20.c 单总线驱动模块

DS18B20 标准单总线时序驱动,实现 IO 切换、复位、位读写、字节读写、温度转换。

2.1 底层 IO 切换函数
  1. DS18B20_IO_IN():DQ 引脚配置为上拉输入,用于读取传感器应答;
  2. DS18B20_IO_OUT():DQ 引脚配置推挽输出,主机拉低 / 拉高总线。
2.2 基础时序函数
  1. DS18B20_Reset():主机拉低总线 750us 复位传感器,释放等待应答;
  2. DS18B20_Check():等待传感器拉低总线存在脉冲,返回 0 = 检测到,1 = 无设备;
  3. DS18B20_Read_Bit():读取单总线 1bit,标准读写时序(拉低 2us,释放 12us 采样);
  4. DS18B20_Read_Byte():循环读取 8bit,组合为 1 字节(低位先读);
  5. DS18B20_Write_Byte():逐位写入字节,写 0 保持 60us 低电平,写 1 仅 2us 低电平。
2.3 功能接口函数
  1. DS18B20_Start():跳过 ROM 指令 (0xCC)+ 启动温度转换 (0x44);
  2. DS18B20_Init():开启 PA15 时钟、关闭 JTAG 调试、配置输出模式,复位并检测传感器;
  3. DS18B20_GetTemperture():完整温度读取流程:
    • 启动转换 → 复位 → 跳过 ROM → 读取暂存器 (0xBE)
    • 读取低字节 LSB、高字节 MSB,拼接 16 位原始数据
    • 判断符号位,正负温度换算(分辨率 0.0625℃/LSB)
    • 返回浮点温度值。

模块 3:usart.c 串口驱动模块

3.1 功能说明
  1. PA9 复用推挽 TX,PA10 浮空 RX;
  2. 9600 波特 8N1 串口,开启收发中断;
  3. fputc重定向标准输出,支持printf()打印;
  4. 中断回调函数:接收数据自动回显(串口回环测试)。
3.2 关键函数
  • USART1_Init(u32 bound):串口时钟、GPIO、波特率、中断 NVIC 初始化;
  • USART1_IRQHandler():接收中断服务函数,回显收到字节;
  • fputc():底层字符发送,printf 依赖此函数输出。

模块 4:SysTick.c 高精度延时模块

基于内核 SysTick 定时器,AHB/8 分频,提供微秒、毫秒阻塞延时。

4.1 全局分频变量
  • fac_us:1 微秒所需计数;
  • fac_ms:1 毫秒所需计数。
4.2 核心接口
  1. SysTick_Init(u8 SYSCLK):根据系统时钟初始化分频系数;
  2. delay_us(u32 nus):微秒级阻塞延时;
  3. delay_ms(u16 nms):毫秒级阻塞延时(72MHz 最大支持 1864ms 单次延时)。

模块 5:lcd1602.c LCD1602 驱动模块

支持 8 位并行 / 4 位半并行两种接口,通过宏LCD1602_4OR8_DATA_INTERFACE切换。

5.1 IO 资源
  • 控制口:PB0 (RS)、PB1 (RW)、PB2 (E);
  • 数据口:PB8~PB15(D0~D7)。
5.2 底层函数
  1. lcd1602_gpio_init():初始化 LCD 所有 IO 为推挽输出;
  2. lcd_set_dataport():将 8 位数据写入 PB8~PB15;
  3. lcd1602_write_cmd():写指令函数(分 4/8 位重载);
  4. lcd1602_write_data():写显示数据函数(分 4/8 位重载)。
5.3 应用接口
  1. lcd1602_init():LCD 上电初始化:功能设置、显示开启、自动右移、清屏;
  2. lcd1602_clear():发送 0x01 清屏指令;
  3. lcd1602_show_string(x,y,str):指定坐标 (x:0~15,y:0/1) 显示字符串,自动换行截断。

四、数据流程说明

  1. 上电初始化全部外设,循环检测 DS18B20 硬件;
  2. 每 500ms 调用DS18B20_GetTemperture()获取浮点温度;
  3. 温度值同步两路输出:
    • USART1 串口格式化打印;
    • LCD1602 双页面定时轮播显示;
  4. LED 持续 200ms 闪烁证明程序正常运行,无卡死;
  5. 3 秒自动切换 LCD 页面,温度区间自动匹配提示文字。

五、异常处理机制

  1. DS18B20 缺失:while 死循环,LCD 固定显示报错文字,串口持续打印故障提示;
  2. 负温度兼容:补码自动换算,串口输出带负号;
  3. LCD 坐标越界保护:lcd1602_show_string判断 x/y 范围,超出直接退出不操作屏幕;
  4. SysTick 延时溢出限制:delay_ms单次最大 1864ms,本项目使用 10ms 短延时无溢出风险。

六、编译与配置说明

  1. 编译环境:Keil MDK-ARM,STM32F10x_StdPeriph_Lib 标准库;
  2. 必须头文件依赖:stdio.h(printf/sprintf)、string.h(memset/strcpy)、math.h(fabs 浮点绝对值);
  3. 宏配置项:
    • DS18B20 端口宏:DS18B20_PORTDS18B20_PINDS18B20_PORT_RCC
    • LCD 接口模式:LCD1602_4OR8_DATA_INTERFACE(0=8 位,1=4 位);
    • LCD 控制 / 数据端口宏:LCD_CTRL_PORTLCD_DATA_PORT、对应引脚宏;
  4. JTAG 关闭:GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE)释放 PA15 作为 DS18B20 总线。

七、实验现象对照文档

  1. 上电无 DS18B20:LCD 两行显示DS18B20 ERROR / Check Sensor!,串口持续打印故障;
  2. 传感器正常识别:串口打印DS18B20检测成功!,LED1 200ms 周期性闪烁;
  3. LCD 页面 0(0~3s):第一行 Current Temp,第二行实时温度如25.62 C
  4. LCD 页面 1(3~6s):第一行 Warm Tip:,第二行根据温度显示对应提示语句;
  5. 串口每 500ms 输出一行温度,负数自动显示负号;
  6. 每 3 秒 LCD 清屏切换页面,循环往复。

八、核心代码

cpp 复制代码
//main.c


#include "lcd1602.h"



// IO初始化
void lcd1602_gpio_init(void)
{
    GPIO_InitTypeDef gpio_cfg;
    RCC_APB2PeriphClockCmd(LCD_CTRL_RCC, ENABLE);

    // 控制脚 PB0 PB1 PB2 推挽输出
    gpio_cfg.GPIO_Pin = LCD_E_PIN | LCD_RS_PIN | LCD_RW_PIN;
    gpio_cfg.GPIO_Mode = GPIO_Mode_Out_PP;
    gpio_cfg.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(LCD_CTRL_PORT, &gpio_cfg);

    // 数据脚 PB8~PB15 推挽输出
    gpio_cfg.GPIO_Pin = LCD_DATA_MASK;
    gpio_cfg.GPIO_Mode = GPIO_Mode_Out_PP;
    gpio_cfg.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(LCD_DATA_PORT, &gpio_cfg);

    LCD_RS_L();
    LCD_RW_L();
    LCD_E_L();
}

// 将8位数据输出到PB8~PB15(D0→PB8,D7→PB15)
static void lcd_set_dataport(uint8_t val)
{
    // 先清除PB8~PB15
    LCD_DATA_PORT->ODR &= ~(0xFF << 8);
    // val低8位移到PB8~PB15输出
    LCD_DATA_PORT->ODR |= ((uint16_t)val << 8);
}

// 写命令
#if (LCD1602_4OR8_DATA_INTERFACE == 0)
void lcd1602_write_cmd(uint8_t cmd)
{
    LCD_RS_L();
    LCD_RW_L();
    LCD_E_L();
    lcd_set_dataport(cmd);
    delay_ms(1);
    LCD_E_H();
    delay_ms(1);
    LCD_E_L();
}
#else
void lcd1602_write_cmd(uint8_t cmd)
{
    LCD_RS_L();
    LCD_RW_L();
    LCD_E_L();
    lcd_set_dataport(cmd >> 4);
    delay_ms(1);
    LCD_E_H();
    delay_ms(1);
    LCD_E_L();

    lcd_set_dataport(cmd & 0x0F);
    delay_ms(1);
    LCD_E_H();
    delay_ms(1);
    LCD_E_L();
}
#endif

// 写数据
#if (LCD1602_4OR8_DATA_INTERFACE == 0)
void lcd1602_write_data(uint8_t dat)
{
    LCD_RS_H();
    LCD_RW_L();
    LCD_E_L();
    lcd_set_dataport(dat);
    delay_ms(1);
    LCD_E_H();
    delay_ms(1);
    LCD_E_L();
}
#else
void lcd1602_write_data(uint8_t dat)
{
    LCD_RS_H();
    LCD_RW_L();
    LCD_E_L();
    lcd_set_dataport(dat >> 4);
    delay_ms(1);
    LCD_E_H();
    delay_ms(1);
    LCD_E_L();

    lcd_set_dataport(dat & 0x0F);
    delay_ms(1);
    LCD_E_H();
    delay_ms(1);
    LCD_E_L();
}
#endif

// LCD初始化
#if (LCD1602_4OR8_DATA_INTERFACE == 0)
void lcd1602_init(void)
{
    lcd1602_gpio_init();
    delay_ms(20);
    lcd1602_write_cmd(0x38);
    lcd1602_write_cmd(0x0C);
    lcd1602_write_cmd(0x06);
    lcd1602_write_cmd(0x01);
}
#else
void lcd1602_init(void)
{
    lcd1602_gpio_init();
    delay_ms(20);
    lcd1602_write_cmd(0x28);
    lcd1602_write_cmd(0x0C);
    lcd1602_write_cmd(0x06);
    lcd1602_write_cmd(0x01);
}
#endif

// 清屏
void lcd1602_clear(void)
{
    lcd1602_write_cmd(0x01);
}

// 显示字符串 x:0~15 y:0/1
void lcd1602_show_string(uint8_t x, uint8_t y, uint8_t *str)
{
    uint8_t i = 0;
    if(y > 1 || x > 15) return;

    if(y == 0)
    {
        while(*str != '\0')
        {
            if(i < 16 - x)
                lcd1602_write_cmd(0x80 + i + x);
            else
                lcd1602_write_cmd(0x80 + 0x40 + i + x - 16);
            lcd1602_write_data(*str);
            str++;
            i++;
        }
    }
    else
    {
        while(*str != '\0')
        {
            if(i < 16 - x)
                lcd1602_write_cmd(0x80 + 0x40 + i + x);
            else
                lcd1602_write_cmd(0x80 + i + x - 16);
            lcd1602_write_data(*str);
            str++;
            i++;
        }
    }
}
cpp 复制代码
//ds18b20.c



#include "ds18b20.h"
#include "SysTick.h"

/*******************************************************************************
* 函 数 名         : DS18B20_IO_IN
* 函数功能		   : DS18B20_IO输入配置	   
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void DS18B20_IO_IN(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	
	GPIO_InitStructure.GPIO_Pin=DS18B20_PIN;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_Init(DS18B20_PORT,&GPIO_InitStructure);
}

/*******************************************************************************
* 函 数 名         : DS18B20_IO_OUT
* 函数功能		   : DS18B20_IO输出配置	   
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void DS18B20_IO_OUT(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	
	GPIO_InitStructure.GPIO_Pin=DS18B20_PIN;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_Init(DS18B20_PORT,&GPIO_InitStructure);
}

/*******************************************************************************
* 函 数 名         : DS18B20_Reset
* 函数功能		   : 复位DS18B20  
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void DS18B20_Reset(void)	   
{                 
	DS18B20_IO_OUT(); //SET PG11 OUTPUT
	DS18B20_DQ_OUT=0; //拉低DQ
	delay_us(750);    //拉低750us
	DS18B20_DQ_OUT=1; //DQ=1 
	delay_us(15);     //15US
}

/*******************************************************************************
* 函 数 名         : DS18B20_Check
* 函数功能		   : 检测DS18B20是否存在
* 输    入         : 无
* 输    出         : 1:未检测到DS18B20的存在,0:存在
*******************************************************************************/
u8 DS18B20_Check(void) 	   
{   
	u8 retry=0;
	DS18B20_IO_IN();//SET PG11 INPUT	 
    while (DS18B20_DQ_IN&&retry<200)
	{
		retry++;
		delay_us(1);
	};
	if(retry>=200)return 1;
	else retry=0;
    while (!DS18B20_DQ_IN&&retry<240)
	{
		retry++;
		delay_us(1);
	};
	if(retry>=240)return 1;	    
	return 0;
}

/*******************************************************************************
* 函 数 名         : DS18B20_Read_Bit
* 函数功能		   : 从DS18B20读取一个位
* 输    入         : 无
* 输    出         : 1/0
*******************************************************************************/
u8 DS18B20_Read_Bit(void) 			 // read one bit
{
	u8 data;
	DS18B20_IO_OUT();//SET PG11 OUTPUT
	DS18B20_DQ_OUT=0; 
	delay_us(2);
	DS18B20_DQ_OUT=1; 
	DS18B20_IO_IN();//SET PG11 INPUT
	delay_us(12);
	if(DS18B20_DQ_IN)data=1;
	else data=0;	 
	delay_us(50);           
	return data;
}

/*******************************************************************************
* 函 数 名         : DS18B20_Read_Byte
* 函数功能		   : 从DS18B20读取一个字节
* 输    入         : 无
* 输    出         : 一个字节数据
*******************************************************************************/
u8 DS18B20_Read_Byte(void)    // read one byte
{        
    u8 i,j,dat;
    dat=0;
	for (i=1;i<=8;i++) 
	{
        j=DS18B20_Read_Bit();
        dat=(j<<7)|(dat>>1);
    }						    
    return dat;
}

/*******************************************************************************
* 函 数 名         : DS18B20_Write_Byte
* 函数功能		   : 写一个字节到DS18B20
* 输    入         : dat:要写入的字节
* 输    出         : 无
*******************************************************************************/
void DS18B20_Write_Byte(u8 dat)     
{             
	u8 j;
    u8 testb;
	DS18B20_IO_OUT();//SET PG11 OUTPUT;
    for (j=1;j<=8;j++) 
	{
        testb=dat&0x01;
        dat=dat>>1;
        if (testb) 
        {
            DS18B20_DQ_OUT=0;// Write 1
            delay_us(2);                            
            DS18B20_DQ_OUT=1;
            delay_us(60);             
        }
        else 
        {
            DS18B20_DQ_OUT=0;// Write 0
            delay_us(60);             
            DS18B20_DQ_OUT=1;
            delay_us(2);                          
        }
    }
}

/*******************************************************************************
* 函 数 名         : DS18B20_Start
* 函数功能		   : 开始温度转换
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void DS18B20_Start(void)// ds1820 start convert
{   						               
    DS18B20_Reset();	   
	DS18B20_Check();	 
    DS18B20_Write_Byte(0xcc);// skip rom
    DS18B20_Write_Byte(0x44);// convert
} 

/*******************************************************************************
* 函 数 名         : DS18B20_Init
* 函数功能		   : 初始化DS18B20的IO口 DQ 同时检测DS的存在
* 输    入         : 无
* 输    出         : 1:不存在,0:存在
*******************************************************************************/   	 
u8 DS18B20_Init(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(DS18B20_PORT_RCC,ENABLE);
	
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE);
	
	GPIO_InitStructure.GPIO_Pin=DS18B20_PIN;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_Init(DS18B20_PORT,&GPIO_InitStructure);
 
 	DS18B20_Reset();
	return DS18B20_Check();
}  

/*******************************************************************************
* 函 数 名         : DS18B20_GetTemperture
* 函数功能		   : 从ds18b20得到温度值
* 输    入         : 无
* 输    出         : 温度数据
*******************************************************************************/ 
float DS18B20_GetTemperture(void)
{
    u16 temp;
	u8 a,b;
	float value;
    DS18B20_Start();                    // ds1820 start convert
    DS18B20_Reset();
    DS18B20_Check();	 
    DS18B20_Write_Byte(0xcc);// skip rom
    DS18B20_Write_Byte(0xbe);// convert	    
    a=DS18B20_Read_Byte(); // LSB   
    b=DS18B20_Read_Byte(); // MSB   
	temp=b;
	temp=(temp<<8)+a;
    if((temp&0xf800)==0xf800)
	{
		temp=(~temp)+1;
		value=temp*(-0.0625);
	}
	else
	{
		value=temp*0.0625;	
	}
	return value;    
}





#ifndef _ds18b20_H
#define _ds18b20_H

#include "system.h"


/*  DS18B20时钟端口、引脚定义 */
#define DS18B20_PORT 			GPIOA  
#define DS18B20_PIN 			(GPIO_Pin_15)
#define DS18B20_PORT_RCC		RCC_APB2Periph_GPIOA


////IO操作函数											   
#define	DS18B20_DQ_OUT PAout(15) //数据端口	
#define	DS18B20_DQ_IN  PAin(15)  //数据端口	 
   	
u8 DS18B20_Init(void);			//初始化DS18B20
float DS18B20_GetTemperture(void);	//获取温度
void DS18B20_Start(void);		//开始温度转换
void DS18B20_Write_Byte(u8 dat);//写入一个字节
u8 DS18B20_Read_Byte(void);		//读出一个字节
u8 DS18B20_Read_Bit(void);		//读出一个位
u8 DS18B20_Check(void);			//检测是否存在DS18B20
void DS18B20_Reset(void);			//复位DS18B20   




#endif
cpp 复制代码
//



#include "lcd1602.h"



// IO初始化
void lcd1602_gpio_init(void)
{
    GPIO_InitTypeDef gpio_cfg;
    RCC_APB2PeriphClockCmd(LCD_CTRL_RCC, ENABLE);

    // 控制脚 PB0 PB1 PB2 推挽输出
    gpio_cfg.GPIO_Pin = LCD_E_PIN | LCD_RS_PIN | LCD_RW_PIN;
    gpio_cfg.GPIO_Mode = GPIO_Mode_Out_PP;
    gpio_cfg.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(LCD_CTRL_PORT, &gpio_cfg);

    // 数据脚 PB8~PB15 推挽输出
    gpio_cfg.GPIO_Pin = LCD_DATA_MASK;
    gpio_cfg.GPIO_Mode = GPIO_Mode_Out_PP;
    gpio_cfg.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(LCD_DATA_PORT, &gpio_cfg);

    LCD_RS_L();
    LCD_RW_L();
    LCD_E_L();
}

// 将8位数据输出到PB8~PB15(D0→PB8,D7→PB15)
static void lcd_set_dataport(uint8_t val)
{
    // 先清除PB8~PB15
    LCD_DATA_PORT->ODR &= ~(0xFF << 8);
    // val低8位移到PB8~PB15输出
    LCD_DATA_PORT->ODR |= ((uint16_t)val << 8);
}

// 写命令
#if (LCD1602_4OR8_DATA_INTERFACE == 0)
void lcd1602_write_cmd(uint8_t cmd)
{
    LCD_RS_L();
    LCD_RW_L();
    LCD_E_L();
    lcd_set_dataport(cmd);
    delay_ms(1);
    LCD_E_H();
    delay_ms(1);
    LCD_E_L();
}
#else
void lcd1602_write_cmd(uint8_t cmd)
{
    LCD_RS_L();
    LCD_RW_L();
    LCD_E_L();
    lcd_set_dataport(cmd >> 4);
    delay_ms(1);
    LCD_E_H();
    delay_ms(1);
    LCD_E_L();

    lcd_set_dataport(cmd & 0x0F);
    delay_ms(1);
    LCD_E_H();
    delay_ms(1);
    LCD_E_L();
}
#endif

// 写数据
#if (LCD1602_4OR8_DATA_INTERFACE == 0)
void lcd1602_write_data(uint8_t dat)
{
    LCD_RS_H();
    LCD_RW_L();
    LCD_E_L();
    lcd_set_dataport(dat);
    delay_ms(1);
    LCD_E_H();
    delay_ms(1);
    LCD_E_L();
}
#else
void lcd1602_write_data(uint8_t dat)
{
    LCD_RS_H();
    LCD_RW_L();
    LCD_E_L();
    lcd_set_dataport(dat >> 4);
    delay_ms(1);
    LCD_E_H();
    delay_ms(1);
    LCD_E_L();

    lcd_set_dataport(dat & 0x0F);
    delay_ms(1);
    LCD_E_H();
    delay_ms(1);
    LCD_E_L();
}
#endif

// LCD初始化
#if (LCD1602_4OR8_DATA_INTERFACE == 0)
void lcd1602_init(void)
{
    lcd1602_gpio_init();
    delay_ms(20);
    lcd1602_write_cmd(0x38);
    lcd1602_write_cmd(0x0C);
    lcd1602_write_cmd(0x06);
    lcd1602_write_cmd(0x01);
}
#else
void lcd1602_init(void)
{
    lcd1602_gpio_init();
    delay_ms(20);
    lcd1602_write_cmd(0x28);
    lcd1602_write_cmd(0x0C);
    lcd1602_write_cmd(0x06);
    lcd1602_write_cmd(0x01);
}
#endif

// 清屏
void lcd1602_clear(void)
{
    lcd1602_write_cmd(0x01);
}

// 显示字符串 x:0~15 y:0/1
void lcd1602_show_string(uint8_t x, uint8_t y, uint8_t *str)
{
    uint8_t i = 0;
    if(y > 1 || x > 15) return;

    if(y == 0)
    {
        while(*str != '\0')
        {
            if(i < 16 - x)
                lcd1602_write_cmd(0x80 + i + x);
            else
                lcd1602_write_cmd(0x80 + 0x40 + i + x - 16);
            lcd1602_write_data(*str);
            str++;
            i++;
        }
    }
    else
    {
        while(*str != '\0')
        {
            if(i < 16 - x)
                lcd1602_write_cmd(0x80 + 0x40 + i + x);
            else
                lcd1602_write_cmd(0x80 + i + x - 16);
            lcd1602_write_data(*str);
            str++;
            i++;
        }
    }
}




#ifndef _LCD1602_H
#define _LCD1602_H

#include "stm32f10x.h"
#include "SysTick.h"

// 0=8位并口  1=4位并口
#define LCD1602_4OR8_DATA_INTERFACE 0

// 控制引脚映射
#define LCD_RS_PIN     GPIO_Pin_1
#define LCD_RW_PIN     GPIO_Pin_2
#define LCD_E_PIN      GPIO_Pin_0
#define LCD_CTRL_PORT  GPIOB
#define LCD_CTRL_RCC   RCC_APB2Periph_GPIOB

// 数据引脚 PB8~PB15 对应D0~D7
#define LCD_DATA_MASK  GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15
#define LCD_DATA_PORT  GPIOB
#define LCD_DATA_RCC   RCC_APB2Periph_GPIOB

// 控制电平宏
#define LCD_RS_H()     GPIO_SetBits(LCD_CTRL_PORT, LCD_RS_PIN)
#define LCD_RS_L()     GPIO_ResetBits(LCD_CTRL_PORT, LCD_RS_PIN)
#define LCD_RW_H()     GPIO_SetBits(LCD_CTRL_PORT, LCD_RW_PIN)
#define LCD_RW_L()     GPIO_ResetBits(LCD_CTRL_PORT, LCD_RW_PIN)
#define LCD_E_H()      GPIO_SetBits(LCD_CTRL_PORT, LCD_E_PIN)
#define LCD_E_L()      GPIO_ResetBits(LCD_CTRL_PORT, LCD_E_PIN)

// 函数声明
void lcd1602_gpio_init(void);
void lcd1602_write_cmd(uint8_t cmd);
void lcd1602_write_data(uint8_t dat);
void lcd1602_init(void);
void lcd1602_clear(void);
void lcd1602_show_string(uint8_t x, uint8_t y, uint8_t *str);


#endif
cpp 复制代码
//SysTick.c


#include "SysTick.h"

static u8  fac_us=0;							//us延时倍乘数			   
static u16 fac_ms=0;							//ms延时倍乘数


//初始化延迟函数
//SYSTICK的时钟固定为AHB时钟的1/8
//SYSCLK:系统时钟频率
void SysTick_Init(u8 SYSCLK)
{
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); 
	fac_us=SYSCLK/8;					
	fac_ms=(u16)fac_us*1000;				   
}								    


//延时nus
//nus为要延时的us数.		    								   
void delay_us(u32 nus)
{		
	u32 temp;	    	 
	SysTick->LOAD=nus*fac_us; 					//时间加载	  		 
	SysTick->VAL=0x00;        					//清空计数器
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;	//开始倒数	  
	do
	{
		temp=SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));		//等待时间到达   
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;	//关闭计数器
	SysTick->VAL =0X00;      					 //清空计数器	 
}

//延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对72M条件下,nms<=1864 
void delay_ms(u16 nms)
{	 		  	  
	u32 temp;		   
	SysTick->LOAD=(u32)nms*fac_ms;				//时间加载(SysTick->LOAD为24bit)
	SysTick->VAL =0x00;							//清空计数器
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;	//开始倒数  
	do
	{
		temp=SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));		//等待时间到达   
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;	//关闭计数器
	SysTick->VAL =0X00;       					//清空计数器	  	    
} 




#ifndef _SysTick_H
#define _SysTick_H

#include "system.h"

void SysTick_Init(u8 SYSCLK);
void delay_ms(u16 nms);
void delay_us(u32 nus);



#endif
相关推荐
piaoyiren1 天前
STM32 使用LCD1602简单显示字符
stm32·单片机·嵌入式硬件·lcd1602
小羊先生car1 天前
软件:STM32-F1系列-读取Flash(2026/7/14)
stm32·单片机·嵌入式硬件
weixin_464078071 天前
Altium Designer自定义变压器
嵌入式硬件
QQ5286211241 天前
芯片程序提取的具体流程和方法
stm32·单片机·嵌入式硬件·程序·pcb抄板
LingzhiPi1 天前
零知派ESP32-S3-智能小车控制系统(4.2)-红外双目跟随模块使用
单片机·嵌入式硬件
智者知已应修善业2 天前
【proteus交通灯故障检测的完善方案】2025-4-29
经验分享·笔记·单片机·嵌入式硬件·proteus
LingzhiPi2 天前
零知ESP32--RC522NFC考勤打卡系统
c++·单片机·嵌入式硬件
炸膛坦客2 天前
常见代码问题与解答:(二)因外部晶振频率改变导致 MCU 锁死的原因及处理方法
单片机·嵌入式硬件
ShiMetaPi2 天前
ShiMetaPi Pico-G1:百元级黑光全彩AI ISP开发板
人工智能·嵌入式硬件·pico·isp