DS18B20 温度采集 + LCD1602 分页显示
一、项目概述1.1 实验名称DS18B20 温度传感器综合实验LCD 分页显示 多阈值提示 串口打印 LED 状态指示1.2 硬件平台STM32F103系统时钟 72MHz标准库 StdPeriph1.3 硬件接线表格外设STM32 引脚说明DS18B20 数据 DQPA15(J14-P37)单总线通讯LCD1602 控制脚PB0(RS)、PB1(RW)、PB2(E)8 位 / 4 位接口可选LCD1602 数据脚PB8~PB15D0~D7LED1自定义 LED 引脚led.h200ms 周期闪烁USART1PA9(TX)、PA10(RX)9600 波特串口输出温度1.4 核心功能DS18B20 硬件上电自检无传感器持续报错提示每 500ms 采集一次环境温度串口实时打印正负温度LED1 每 200ms 翻转作为系统运行心跳指示灯LCD1602 双页面自动切换3 秒翻页一次页面 0实时温度数值显示页面 1多区间温度温馨提示温度阈值分级提示低温保暖、舒适、通风、高温防暑四级支持正负温度换算DS18B20 单总线标准读写时序SysTick 毫秒 / 微秒高精度延时串口重定向 printf 输出。1.5 运行时序参数系统基础循环10msLED 闪烁周期200ms20 个 10ms 计数温度采集周期500ms50 个 10ms 计数LCD 页面切换周期3000ms300 个 10ms 计数二、宏定义与全局参数说明2.1 温度阈值宏main.cc运行#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_cntuint16_t10ms 基础计数器驱动 LED、温度采集定时temp_valfloat存储 DS18B20 读取的浮点温度lcd_buf[16]charLCD 字符串缓存存放温度 / 提示文本page_flaguint8_t页面标记0 温度页1 提示页page_timeruint16_t翻页计时器满 300 清零切换页面三、分模块代码详细说明模块 1main.c 主逻辑模块3.1 外设初始化流程SysTick_Init(72)配置 72MHz 系统时钟延时分频提供delay_ms/delay_usNVIC_PriorityGroupConfig中断优先级分组 2LED_Init()初始化 LED 指示灯 IOUSART1_Init(9600)串口 1 初始化开启收发中断重定向 printflcd1602_init()LCD1602 屏幕初始化清屏、设置显示模式DS18B20 硬件检测循环反复复位读取传感器异常则 LCD 报错 串口打印。3.2 主循环定时逻辑10ms 单次循环计数器自增、延时 10msLED 心跳每 20 次循环200ms翻转 LED 电平翻页计时page_timer 计数到 3003s切换页面标记清屏温度采集每 50 次循环500ms读取一次 DS18B20 温度串口打印区分正负温度格式化输出LCD 页面渲染根据 page_flag 刷新对应页面文字。3.3 LCD 分页显示逻辑页面 0温度页第一行固定标题Current Temp第二行打印浮点温度页面 1提示页第一行标题Warm Tip:第二行根据温度阈值匹配提示字符串页面切换时执行lcd1602_clear()清屏避免文字重叠。3.4 负温度处理逻辑DS18B20 负数为补码存储读取后取反 1 换算绝对值输出负号。模块 2ds18b20.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返回浮点温度值。模块 3usart.c 串口驱动模块3.1 功能说明PA9 复用推挽 TXPA10 浮空 RX9600 波特 8N1 串口开启收发中断fputc重定向标准输出支持printf()打印中断回调函数接收数据自动回显串口回环测试。3.2 关键函数USART1_Init(u32 bound)串口时钟、GPIO、波特率、中断 NVIC 初始化USART1_IRQHandler()接收中断服务函数回显收到字节fputc()底层字符发送printf 依赖此函数输出。模块 4SysTick.c 高精度延时模块基于内核 SysTick 定时器AHB/8 分频提供微秒、毫秒阻塞延时。4.1 全局分频变量fac_us1 微秒所需计数fac_ms1 毫秒所需计数。4.2 核心接口SysTick_Init(u8 SYSCLK)根据系统时钟初始化分频系数delay_us(u32 nus)微秒级阻塞延时delay_ms(u16 nms)毫秒级阻塞延时72MHz 最大支持 1864ms 单次延时。模块 5lcd1602.c LCD1602 驱动模块支持 8 位并行 / 4 位半并行两种接口通过宏LCD1602_4OR8_DATA_INTERFACE切换。5.1 IO 资源控制口PB0 (RS)、PB1 (RW)、PB2 (E)数据口PB8~PB15D0~D7。5.2 底层函数lcd1602_gpio_init()初始化 LCD 所有 IO 为推挽输出lcd_set_dataport()将 8 位数据写入 PB8~PB15lcd1602_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-ARMSTM32F10x_StdPeriph_Lib 标准库必须头文件依赖stdio.hprintf/sprintf、string.hmemset/strcpy、math.hfabs 浮点绝对值宏配置项DS18B20 端口宏DS18B20_PORT、DS18B20_PIN、DS18B20_PORT_RCCLCD 接口模式LCD1602_4OR8_DATA_INTERFACE08 位14 位LCD 控制 / 数据端口宏LCD_CTRL_PORT、LCD_DATA_PORT、对应引脚宏JTAG 关闭GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE)释放 PA15 作为 DS18B20 总线。七、实验现象对照文档上电无 DS18B20LCD 两行显示DS18B20 ERROR / Check Sensor!串口持续打印故障传感器正常识别串口打印DS18B20检测成功!LED1 200ms 周期性闪烁LCD 页面 00~3s第一行 Current Temp第二行实时温度如25.62 CLCD 页面 13~6s第一行 Warm Tip:第二行根据温度显示对应提示语句串口每 500ms 输出一行温度负数自动显示负号每 3 秒 LCD 清屏切换页面循环往复。八、核心代码//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~PB15D0→PB8D7→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; } } }//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_PinDS18B20_PIN; GPIO_InitStructure.GPIO_ModeGPIO_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_PinDS18B20_PIN; GPIO_InitStructure.GPIO_SpeedGPIO_Speed_50MHz; GPIO_InitStructure.GPIO_ModeGPIO_Mode_Out_PP; GPIO_Init(DS18B20_PORT,GPIO_InitStructure); } /******************************************************************************* * 函 数 名 : DS18B20_Reset * 函数功能 : 复位DS18B20 * 输 入 : 无 * 输 出 : 无 *******************************************************************************/ void DS18B20_Reset(void) { DS18B20_IO_OUT(); //SET PG11 OUTPUT DS18B20_DQ_OUT0; //拉低DQ delay_us(750); //拉低750us DS18B20_DQ_OUT1; //DQ1 delay_us(15); //15US } /******************************************************************************* * 函 数 名 : DS18B20_Check * 函数功能 : 检测DS18B20是否存在 * 输 入 : 无 * 输 出 : 1:未检测到DS18B20的存在0:存在 *******************************************************************************/ u8 DS18B20_Check(void) { u8 retry0; DS18B20_IO_IN();//SET PG11 INPUT while (DS18B20_DQ_INretry200) { retry; delay_us(1); }; if(retry200)return 1; else retry0; while (!DS18B20_DQ_INretry240) { retry; delay_us(1); }; if(retry240)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_OUT0; delay_us(2); DS18B20_DQ_OUT1; DS18B20_IO_IN();//SET PG11 INPUT delay_us(12); if(DS18B20_DQ_IN)data1; else data0; delay_us(50); return data; } /******************************************************************************* * 函 数 名 : DS18B20_Read_Byte * 函数功能 : 从DS18B20读取一个字节 * 输 入 : 无 * 输 出 : 一个字节数据 *******************************************************************************/ u8 DS18B20_Read_Byte(void) // read one byte { u8 i,j,dat; dat0; for (i1;i8;i) { jDS18B20_Read_Bit(); dat(j7)|(dat1); } return dat; } /******************************************************************************* * 函 数 名 : DS18B20_Write_Byte * 函数功能 : 写一个字节到DS18B20 * 输 入 : dat要写入的字节 * 输 出 : 无 *******************************************************************************/ void DS18B20_Write_Byte(u8 dat) { u8 j; u8 testb; DS18B20_IO_OUT();//SET PG11 OUTPUT; for (j1;j8;j) { testbdat0x01; datdat1; if (testb) { DS18B20_DQ_OUT0;// Write 1 delay_us(2); DS18B20_DQ_OUT1; delay_us(60); } else { DS18B20_DQ_OUT0;// Write 0 delay_us(60); DS18B20_DQ_OUT1; 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_PinDS18B20_PIN; GPIO_InitStructure.GPIO_SpeedGPIO_Speed_50MHz; GPIO_InitStructure.GPIO_ModeGPIO_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 aDS18B20_Read_Byte(); // LSB bDS18B20_Read_Byte(); // MSB tempb; temp(temp8)a; if((temp0xf800)0xf800) { temp(~temp)1; valuetemp*(-0.0625); } else { valuetemp*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// #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~PB15D0→PB8D7→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 // 08位并口 14位并口 #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//SysTick.c #include SysTick.h static u8 fac_us0; //us延时倍乘数 static u16 fac_ms0; //ms延时倍乘数 //初始化延迟函数 //SYSTICK的时钟固定为AHB时钟的1/8 //SYSCLK:系统时钟频率 void SysTick_Init(u8 SYSCLK) { SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); fac_usSYSCLK/8; fac_ms(u16)fac_us*1000; } //延时nus //nus为要延时的us数. void delay_us(u32 nus) { u32 temp; SysTick-LOADnus*fac_us; //时间加载 SysTick-VAL0x00; //清空计数器 SysTick-CTRL|SysTick_CTRL_ENABLE_Msk ; //开始倒数 do { tempSysTick-CTRL; }while((temp0x01)!(temp(116))); //等待时间到达 SysTick-CTRL~SysTick_CTRL_ENABLE_Msk; //关闭计数器 SysTick-VAL 0X00; //清空计数器 } //延时nms //注意nms的范围 //SysTick-LOAD为24位寄存器,所以,最大延时为: //nms0xffffff*8*1000/SYSCLK //SYSCLK单位为Hz,nms单位为ms //对72M条件下,nms1864 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 { tempSysTick-CTRL; }while((temp0x01)!(temp(116))); //等待时间到达 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

相关新闻

Ushahidi Platform API开发指南:构建自定义危机响应应用的终极教程

Ushahidi Platform API开发指南:构建自定义危机响应应用的终极教程

Ushahidi Platform API开发指南:构建自定义危机响应应用的终极教程 【免费下载链接】platform Ushahidi Platform API version 3 项目地址: https://gitcode.com/gh_mirrors/platform16/platform Ushahidi Platform API是一个强大的开源危机响应平台API&…

2026/10/3 2:49:34 阅读更多 →
10款提升效率的AI工具实测推荐

10款提升效率的AI工具实测推荐

1. 为什么你需要这10个AI工具?作为一名长期关注AI应用的从业者,我经常被问到:"哪些AI工具是真正实用而非噱头的?"经过半年多的实测筛选,我从上百个AI网站中精选出10个真正能提升工作效率的利器。这些工具的共…

2026/9/30 22:46:39 阅读更多 →
如何快速解决sandbox-sdk常见问题:从新手入门到高级应用的完整指南

如何快速解决sandbox-sdk常见问题:从新手入门到高级应用的完整指南

如何快速解决sandbox-sdk常见问题:从新手入门到高级应用的完整指南 【免费下载链接】sandbox-sdk Run sandboxed code environments on Cloudflares edge network 项目地址: https://gitcode.com/gh_mirrors/sa/sandbox-sdk Cloudflare Sandbox SDK 是一个强…

2026/10/7 19:38:16 阅读更多 →

最新新闻

并行编程——MPI 和OpenMPI 基本概念学习(一)

并行编程——MPI 和OpenMPI 基本概念学习(一)

文章大纲引言一、MPI 概述二、MPI基础数据类型1、使用MPI_Type_create_struct创建结构体类型数据类型1.1、在C中定义结构体并确定成员偏移量1.2、创建自定义 MPI 数据类型2、使用MPI_Type_create_subarray创建子数组类型数据类型2.1、确定子数组的形状和位置2.2、创建自定义 MP…

2026/10/14 1:53:15 阅读更多 →
syzkaller 依赖的 errchkjson 静态分析器:json.Marshal 错误处理与类型安全分类详解

syzkaller 依赖的 errchkjson 静态分析器:json.Marshal 错误处理与类型安全分类详解

测试操作系统网络安全 【免费下载链接】syzkaller syzkaller is an unsupervised coverage-guided kernel fuzzer 项目地址: https://gitcode.com/gh_mirrors/syz/syzkaller 点击查看 免费下载 本文围绕 syzkaller 仓库中 vendor 目录下的 errchkjson 文档展开&…

2026/10/14 1:53:15 阅读更多 →
Farm 懒编译(Lazy Compilation)实战指南:让大型项目开发编译从分钟级降到秒级

Farm 懒编译(Lazy Compilation)实战指南:让大型项目开发编译从分钟级降到秒级

前端构建构建工具开发工具 【免费下载链接】farm Extremely fast Vite-compatible web build tool written in Rust 项目地址: https://gitcode.com/gh_mirrors/fa/farm 点击查看 免费下载 Farm 是一款用 Rust 编写的 Vite 兼容 Web 构建工具,其「懒编译…

2026/10/14 1:53:15 阅读更多 →
TEngine 设计模式应用剖析:单例、工厂、观察者与状态机的工业化实现

TEngine 设计模式应用剖析:单例、工厂、观察者与状态机的工业化实现

游戏开发 【免费下载链接】TEngine Unity 商用级别开发框架,原生内置 AI 工作流支持,集成 HybridCLR 高性能热更、Obfuz 代码混淆加固、YooAssets 企业级资源管理方案,构建高效、安全、可扩展的工业化开发底座。 项目地址: https:…

2026/10/14 1:53:15 阅读更多 →
基于Vue和WebSocket的实时聊天系统实战:从轮询到长连接与踩坑记录

基于Vue和WebSocket的实时聊天系统实战:从轮询到长连接与踩坑记录

简介:这是一套基于WebSocket与Vue.js的在线聊天室系统设计Demo,面向前端开发学习者与全栈入门者,演示如何从零搭建支持实时双向通信的群聊与私聊场景。项目功能完整,涵盖私聊、群聊、消息已读与未读、未读消息提醒、聊天文字颜色区…

2026/10/14 1:53:15 阅读更多 →
新能源EPC项目SAP PS手册:从WBS到CJ88的落地指南

新能源EPC项目SAP PS手册:从WBS到CJ88的落地指南

简介:《新能源项目-2017年6月SAP项目SAP PS模块用户操作手册》是一份面向SAP项目实施顾问、新能源企业项目管理人员及信息化运维人员的操作指引,重点解决项目创建、WBS工作任务分解和标准网络复制等PS模块高频操作问题。文档共67页,以单个Wor…

2026/10/14 1:52:15 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →