一、项目概述
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 核心功能
- DS18B20 硬件上电自检,无传感器持续报错提示;
- 每 500ms 采集一次环境温度,串口实时打印正负温度;
- LED1 每 200ms 翻转,作为系统运行心跳指示灯;
- LCD1602 双页面自动切换(3 秒翻页一次):
- 页面 0:实时温度数值显示
- 页面 1:多区间温度温馨提示;
- 温度阈值分级提示:低温保暖、舒适、通风、高温防暑四级;
- 支持正负温度换算,DS18B20 单总线标准读写时序;
- 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 // 高温预警分界
温度分级逻辑:
temp < 18.0℃:Keep Warm! 气温偏低,注意保暖18.0 ≤ temp <26.0℃:Very Comfort 舒适温度区间26.0 ≤ temp <32.0℃:Ventilate! 温度偏高,建议通风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 外设初始化流程
SysTick_Init(72):配置 72MHz 系统时钟延时分频,提供delay_ms/delay_us;NVIC_PriorityGroupConfig:中断优先级分组 2;LED_Init():初始化 LED 指示灯 IO;USART1_Init(9600):串口 1 初始化,开启收发中断,重定向 printf;lcd1602_init():LCD1602 屏幕初始化,清屏、设置显示模式;- DS18B20 硬件检测循环:反复复位读取传感器,异常则 LCD 报错 + 串口打印。
3.2 主循环定时逻辑(10ms 单次循环)
- 计数器自增、延时 10ms;
- LED 心跳:每 20 次循环(200ms)翻转 LED 电平;
- 翻页计时:page_timer 计数到 300(3s)切换页面标记,清屏;
- 温度采集:每 50 次循环(500ms)读取一次 DS18B20 温度;
- 串口打印:区分正负温度格式化输出;
- 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 切换函数
DS18B20_IO_IN():DQ 引脚配置为上拉输入,用于读取传感器应答;DS18B20_IO_OUT():DQ 引脚配置推挽输出,主机拉低 / 拉高总线。
2.2 基础时序函数
DS18B20_Reset():主机拉低总线 750us 复位传感器,释放等待应答;DS18B20_Check():等待传感器拉低总线存在脉冲,返回 0 = 检测到,1 = 无设备;DS18B20_Read_Bit():读取单总线 1bit,标准读写时序(拉低 2us,释放 12us 采样);DS18B20_Read_Byte():循环读取 8bit,组合为 1 字节(低位先读);DS18B20_Write_Byte():逐位写入字节,写 0 保持 60us 低电平,写 1 仅 2us 低电平。
2.3 功能接口函数
DS18B20_Start():跳过 ROM 指令 (0xCC)+ 启动温度转换 (0x44);DS18B20_Init():开启 PA15 时钟、关闭 JTAG 调试、配置输出模式,复位并检测传感器;DS18B20_GetTemperture():完整温度读取流程:- 启动转换 → 复位 → 跳过 ROM → 读取暂存器 (0xBE)
- 读取低字节 LSB、高字节 MSB,拼接 16 位原始数据
- 判断符号位,正负温度换算(分辨率 0.0625℃/LSB)
- 返回浮点温度值。
模块 3:usart.c 串口驱动模块
3.1 功能说明
- PA9 复用推挽 TX,PA10 浮空 RX;
- 9600 波特 8N1 串口,开启收发中断;
fputc重定向标准输出,支持printf()打印;- 中断回调函数:接收数据自动回显(串口回环测试)。
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 核心接口
SysTick_Init(u8 SYSCLK):根据系统时钟初始化分频系数;delay_us(u32 nus):微秒级阻塞延时;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 底层函数
lcd1602_gpio_init():初始化 LCD 所有 IO 为推挽输出;lcd_set_dataport():将 8 位数据写入 PB8~PB15;lcd1602_write_cmd():写指令函数(分 4/8 位重载);lcd1602_write_data():写显示数据函数(分 4/8 位重载)。
5.3 应用接口
lcd1602_init():LCD 上电初始化:功能设置、显示开启、自动右移、清屏;lcd1602_clear():发送 0x01 清屏指令;lcd1602_show_string(x,y,str):指定坐标 (x:0~15,y:0/1) 显示字符串,自动换行截断。
四、数据流程说明
- 上电初始化全部外设,循环检测 DS18B20 硬件;
- 每 500ms 调用
DS18B20_GetTemperture()获取浮点温度; - 温度值同步两路输出:
- USART1 串口格式化打印;
- LCD1602 双页面定时轮播显示;
- LED 持续 200ms 闪烁证明程序正常运行,无卡死;
- 3 秒自动切换 LCD 页面,温度区间自动匹配提示文字。
五、异常处理机制
- DS18B20 缺失:while 死循环,LCD 固定显示报错文字,串口持续打印故障提示;
- 负温度兼容:补码自动换算,串口输出带负号;
- LCD 坐标越界保护:
lcd1602_show_string判断 x/y 范围,超出直接退出不操作屏幕; - SysTick 延时溢出限制:
delay_ms单次最大 1864ms,本项目使用 10ms 短延时无溢出风险。
六、编译与配置说明
- 编译环境:Keil MDK-ARM,STM32F10x_StdPeriph_Lib 标准库;
- 必须头文件依赖:
stdio.h(printf/sprintf)、string.h(memset/strcpy)、math.h(fabs 浮点绝对值); - 宏配置项:
- DS18B20 端口宏:
DS18B20_PORT、DS18B20_PIN、DS18B20_PORT_RCC; - LCD 接口模式:
LCD1602_4OR8_DATA_INTERFACE(0=8 位,1=4 位); - LCD 控制 / 数据端口宏:
LCD_CTRL_PORT、LCD_DATA_PORT、对应引脚宏;
- DS18B20 端口宏:
- JTAG 关闭:
GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE)释放 PA15 作为 DS18B20 总线。
七、实验现象对照文档
- 上电无 DS18B20:LCD 两行显示
DS18B20 ERROR / Check Sensor!,串口持续打印故障; - 传感器正常识别:串口打印
DS18B20检测成功!,LED1 200ms 周期性闪烁; - LCD 页面 0(0~3s):第一行 Current Temp,第二行实时温度如
25.62 C; - LCD 页面 1(3~6s):第一行 Warm Tip:,第二行根据温度显示对应提示语句;
- 串口每 500ms 输出一行温度,负数自动显示负号;
- 每 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