当前位置: 首页 > news >正文

基于STM32的智能天气时钟

1. GPIO

定义初始化结构体

GPIO_InitTypeDef GPIO_InitStruct;
  • 作用:定义一个名为GPIO_InitStruct的结构体变量,用于存储GPIO的配置参数。

  • 说明GPIO_InitTypeDef是库中定义的结构体类型,包含GPIO的所有可配置项。

2. 结构体默认初始化

GPIO_StructInit(&GPIO_InitStruct);
  • 作用:用默认值填充结构体,避免未初始化的随机值。

各参数详细含义:

参数说明
GPIO_ModeGPIO_Mode_AF复用功能模式,引脚用于UART、SPI、I2C等外设
GPIO_PuPdGPIO_PuPd_UP内部上拉电阻,引脚默认电平被拉高
GPIO_OTypeGPIO_OType_PP推挽输出,可输出高电平和低电平,驱动能力强
GPIO_SpeedGPIO_Speed_50MHz高速模式,适合高频信号传输

无论是使用标准库还是HAL库,其本质上都是进行读写寄存器的操作:

  1. 标准库 (SPL):寄存器视角的抽象

    • 设计哲学:“让我来帮你操作寄存器,但你仍然需要知道你在操作哪个外设的哪个功能。”

    • 示例USART_SendData(USART1, data);

    • 分析:这个函数很清晰,它帮你完成了向USART1的数据寄存器(DR)写入数据的操作。但你仍然需要知道是USART1,并且在此之后,你可能还需要手动检查状态寄存器(SR)的“发送完成”标志(TC)来判断数据是否发完。它抽象了“写寄存器”这个动作,但没有抽象“发送一帧数据”这个完整业务流程

  2. HAL库:业务逻辑视角的抽象

    • 设计哲学:“告诉我你想做什么(What),别管我怎么做(How)。我会管理整个流程和状态。”

    • 示例HAL_UART_Transmit(&huart1, &data, 1, 1000);

    • 分析:这个函数接受一个句柄(Handle),它封装了UART的所有状态和配置。你告诉它:“用huart1这个串口,发送1个字节的数据data,我给你1000ms的超时时间。” 在函数内部,它可能做了以下一系列寄存器操作:

      • 检查状态寄存器(SR)判断串口是否就绪。

      • 向数据寄存器(DR)写入数据。

      • 等待状态寄存器(SR)的TC标志置位,或者等待超时。

      • 如果超时,会设置错误代码。

    • 关键:HAL为你管理了状态机超时机制错误处理。这是它比标准库抽象层次更高的根本体现。

2. 串行通信原理与应用

通过逻辑分析仪分析串口发送数据时的特征:

channel1(TX)为电脑向单片机的串口发送数据,channl0(RX)为单片机接收电脑端发送的数据。这里向单片机发送字符“A”,其ASCII码为65,转换为二进制为1000 0001。

上面传输时,没有设置校验位,如果设置校验位,会在停止位(Stop bit)之前增加一个bit的奇偶校验位(计算传输的二进制数据中1的个数)。

常用模式:

1152000bps:表示每秒传输1152000比特位,那么在没有奇偶校验位的情况下,每秒最多可以传输                       1152000%(1+8+1)=115200个字节数据,注意这里需要加上起始位和停止位。

8N1:表示8位传输数据,没有校验位,1位停止位。

3. 如何编程

3.1 串口编程时遇到的问题

使用for()循环由电脑向单片机串口发送字符“hello world”,

代码如下:

这里的str[]=“hello world”。

a. USART_ClearFlag(USART1, USART_FLAG_TC);
这是确保发送可靠的关键一步。
USART_FLAG_TC 是 "Transmission Complete"(发送完成) 标志位。
* 这个标志位的含义是:发送移位寄存器中的数据已经全部移位发出了,并且数据寄存器(TDR)也是空的(即整个发送流程彻底完成)。
* 在发送新一个字节之前,先清除这个标志位,是为了避免之前可能遗留的已完成状态影响本次发送的等待判断。

b. USART_SendData(USART1, str[i]);
* 这是启动发送的命令。
* 函数将你要发送的字符(str[i])写入到USART1的发送数据寄存器(TDR)
* 一旦数据被写入TDR,USART外设就会自动开始发送过程(加入起始位、停止位等,并通过TX引脚一位一位地发送出去)。此时,TC标志位会被硬件自动清零。

c. while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
* 这是一个阻塞等待循环,直到当前字节发送完成。
USART_GetFlagStatus 函数用于检查 TC 标志位的状态。
RESET 通常等于0,表示标志位未被设置。
* 只要 TC 标志位为 RESET(即发送未完成),CPU就会一直在这里空循环等待。

如果不加上while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);那么单片机的串口可能只会接收到最后一个字符“d”。

这是因为CPU执行速度(纳秒级)远远快于串口发送速度(毫秒级)。

例如,在115200波特率下,发送1个字节(8位数据+1起始位+1停止位=10位)需要的时间大约是:
10 bits / 115200 bits-per-second ≈ 87 μs

而CPU执行一条指令可能只需要几十纳秒(0.000087 ms)。没有等待的话,CPU可以在87μs内执行成百上千条指令。

同时,上面的代码看起来是没有什么问题,但是如果使用逻辑分析仪观察时,就会发现两个字符发送的时间是有时间间隔的:

原因:在for()循环中每次都需要判断:i < strlen(str),而strlen(str)是一个函数,每次调用这个函数都需要一定的时间,这就造成了发送的相邻的两份字符之间存在时间间隔。

解决方法:

3.2 串口编程的方法

4. 按键

4.1按键代码编写

5. 中断

EXTI为外部\事件中断控制器,可以连接GPIO引脚;

外部中断是指当某一模块发生作用时(比如说按键按下),其响应是发送至CPU进行处理的;事件中断是指当某一模块发生作用时,其响应是由其他模块进行处理的。
NVIC为嵌套向量中断控制器,用于设置中断的优先级,因为会存在很多中断,如外部\事件中断(EXTI)、串口中断(UART)和定时器中断。

对于不同的中断源需要进行不同的设置(初始化),例如对于外部\事件(EXTI),可能需要设置GPIO口的哪一个引脚连接到外部中断控制器的引脚上;对串口中断,可能需要设置是发送数据之前发生中断,还是接收到数据之后产生中断;对于定时器中断,可能需要设置每隔一定的时间的时间产生中断。

特性Modbus协议串口通信 (UART)I2C协议SPI协议
本质应用层协议物理层/数据链路层协议硬件通信协议硬件通信协议
通信架构主从架构 (1主多从)对等架构 (点对点)主从架构 (1主多从)主从架构 (1主多从)
信号线依赖底层硬件 (如RS-485)TX (发送), RX (接收), GNDSDA (数据), SCL (时钟)SCLK (时钟), MOSI (主出从入), MISO (主入从出), SS/CS (片选)
通信方式半双工 (RS-485)全双工半双工全双工
同步方式异步异步同步 (由主设备提供SCL时钟)同步 (由主设备提供SCLK时钟)
拓扑结构总线型 (可挂载多个设备)点对点 (通常1对1)总线型 (可挂载多个设备)点对点星型 (每个从设备独立片选)
寻址方式软件地址 (从站地址)无地址硬件地址 (7位/10位地址)硬件片选 (通过CS引脚选择从设备)
速度慢 (依赖底层硬件,常用9600-115200bps)慢 (常用9600-115200bps)中速 (标准模式100kbps,快速模式400kbps)高速 (通常10+Mbps,甚至更高)
软件复杂度 (需实现协议栈) (配置好参数即可收发) (需实现字节级收发和协议) (主要是硬件移位寄存器操作)
硬件复杂度 (依赖现有硬件接口) (MCU基本都自带)非常低 (2根线,无需片选) (线多,每个从设备需独立片选)
主要应用场景工业控制、物联网 (PLC、传感器、电表)调试日志、GPS模块、老旧设备板内通信 (传感器、EEPROM、RTC)高速板内通信 (Flash、SD卡、显示屏、ADC)

5.1如何使用此驱动

要将I/O引脚用作外部中断源,请按以下步骤操作:

(#) 使用GPIO_Init()将I/O配置为输入模式

(#) 使用SYSCFG_EXTILineConfig()选择EXTI线路的输入源引脚

(#) 使用EXTI_Init()选择模式(中断/事件)并配置触发方式(上升沿/下降沿/双边沿)

(#) 使用NVIC_Init()配置映射到EXTI线路的NVIC中断请求通道

[..]

(@) 必须启用SYSCFG APB时钟才能通过RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE)获得对SYSCFG_EXTICRx寄存器的写访问权限

下面分步写代码:

1.初始化外设时钟:

2. 中断优先级组配置:

3.配置GPIO--初始化设置:

4.使用SYSCFG_EXTILineConfig()选择EXTI线路的输入源引脚:将GPIOC口的引脚4和5设置为中断模式

5.使用EXTI_Init()选择模式为中断,并配置触发方式上升沿:

6.使用NVIC_Init()配置映射到EXTI线路的NVIC中断请求通道:

5.2 中断服务函数

中断服务函数,也叫中断处理器,是一段特殊的、预先写好的函数。当微控制器的中断发生时,CPU会自动暂停当前正在执行的主程序,跳转到这个函数来运行。运行完毕后,CPU再返回主程序继续执行。

中断服务函数的特点

 特殊的函数名:编译器通过固定的函数名来识别ISR。

例如:

SysTick定时器中断服务函数:

按键中断服务函数:

6. 回调函数

函数本身是一个地址,我们可以把函数的地址赋值给另外一个变量,那么这个变量存放的就是函数的地址,所以这个变量就是指针变量,也就是函数指针。通过调用函数指针,实现对函数的间接访问。

定义了一个函数指针类型keyfunc,然后声明了三个该类型的静态全局函数指针变量(keyl_func_tkey2_func_tkey3_func_t)。

执行完上面的代码后:

key1_func = led1_toggle;

7. 定时器

定时器分为高级定时器、通用定时器和基本定时器。

定时器最基本的功能就是计数器,基本定时器结构如下:

系统时钟RCC通过预分频器PSC可以将系统时钟频率进行分频,计数器CNT进行累加。

通用定时器的结构如下:

通用定时器除了可以接受来自系统的时钟,还可以接收来自外部时钟TIMx_CH1,当接收来自外部时钟TIMx_CH1时,可以捕获引脚的波形。

捕获(Capture)功能是单片机定时器的一个硬件特性,它能够在外部引脚发生特定电平跳变时,自动记录下定时器的当前值

  • 核心目的精确测量外部信号的时间参数(脉宽、周期、频率)。

  • 核心优势硬件自动完成,速度快,精度高,不占用CPU资源,只有在需要读数时才通过中断通知CPU。

  • 核心流程设置触发条件(边沿)→ 硬件自动抓拍时间戳并存入CCRx → 产生中断 → CPU读取并处理

7.1 如何编程

8. I2C协议

上面是I2C协议的硬件实现,对应的是GPIO口的开漏输出,即P-MOS断开只有N-MOS可以使用,设备1和设备2均可以使SCL为高电平或低电平。

8.1程序编写

8.2 I2C时序

I2C有两根线,一根数据线SDA和一根时钟线SCL。

起始条件 (Start Condition) 和停止条件 (Stop Condition)

  • 起始条件 (S):当 SCL为高电平 时,SDA发生一个从高到低 的电平跳变。这个状态是唯一的,表示一次传输的开始。

  • 停止条件 (P):当 SCL为高电平 时,SDA发生一个从低到高 的电平跳变。这个状态表示一次传输的结束。

一个典型的I²C数据传输帧由以下部分组成:

  1. 起始条件 (S)

  2. 从机地址 (Slave Address):7位或10位地址,用于选择总线上的哪个从机。

  3. 读写位 (R/W#):1位,表示主机是想写数据到从机(0)还是从从机读数据(1)。

  4. 应答位 (ACK):从机回应地址匹配信号。

  5. 数据字节 (Data Byte):8位的数据,可以是命令、寄存器地址或实际数据。

  6. 应答位 (ACK):在每一个数据字节后都有,当有从机与从机地址匹配的上时,SDA会变成低电平,否则为高电平。

  7. ...(重复步骤5和6以传输更多数据)

  8. 停止条件 (P) 或 重复起始条件 (Sr)。重复起始条件在不释放总线的情况下开始一个新的通信序列。

传输数据时,数据线SDA和时钟线SCL的高低电平变化:

  • SCL为高电平期间,SDA线上的数据必须保持稳定(不变)。此时的数据位有效,接收方(主机或从机)会在这个时刻采样(读取)SDA的值。

  • SCL为低电平期间,SDA线上的电平才允许发生变化,为传输下一位数据做准备。

9 SPI协议

SPI vs. I2C 核心特性对比表

特性SPI (Serial Peripheral Interface)I2C (Inter-Integrated Circuit)
全称串行外设接口内部集成电路
线路数量4线制(SCLK, MOSI, MISO, SS)
(每增加一个从机,需增加一条SS线)
2线制(SDA, SCL)
(支持大量从机而无需增加线路)
通信方式全双工 (Full-Duplex)
(数据可同时收发)
半双工 (Half-Duplex)
(数据分时收发,同一时刻只能向一个方向传输)
拓扑结构主从式,点对点菊花链主从式,多主多从(所有设备都挂载到总线上)
从机选择方式硬件片选 (SS/CS)
主机通过专用引脚拉低来选中从机
软件地址寻址
主机在数据传输开始时发送一个从机地址来选中它
数据传输速率非常高(通常可达几十甚至上百MHz)中低速(标准模式100kHz,快速模式400kHz,高速模式3.4MHz)
协议复杂度非常简单
只有移位时钟,无格式要求。数据就是数据。
相对复杂
起始位、停止位、应答位(ACK/NACK) 等固定格式。
有无时钟同步(有独立的时钟线SCLK)同步(有独立的时钟线SCL)
流控与应答无硬件应答机制
主机无法知道从机是否成功接收数据。
有硬件应答机制
每个字节后,接收方必须发送一个ACK位,确保数据被确认接收。
方向控制通过独立的信号线(MOSI和MISO)实现通过协议和地址中的读写位控制方向
功耗相对较高(线路多,速度通常更快)相对较低(线路少,上拉电阻阻值可以很大)
专利与版权无版权,事实标准由NXP(原飞利浦)管理,但无需授权费
典型应用场景需要高速数据传输的场合:
• 存储器(Flash, EEPROM)
• SD卡
• 传感器(高精度ADC)
• 显示屏(OLED, TFT)
需要连接大量设备速度要求不高的场合:
• 传感器(温度、湿度)
• 实时时钟(RTC)
• I/O端口扩展器
• EEPROM

如何选择?

  • 选择 SPI 当

    • 你需要非常高的速度

    • 你连接的从设备不多,且主机的IO口引脚足够。

    • 你的从设备只支持SPI(如很多ADC和显示屏)。

    • 你不需要硬件确认数据是否接收。

  • 选择 I2C 当

    • 你的速度要求不高

    • 你需要连接很多设备(超过3-4个),但想节省主机的IO引脚。

    • 你的电路板空间紧张,希望布线简单(只有2根线)。

    • 数据可靠性很重要,你需要硬件应答机制。

简单总结:SPI追求速度和简单,I2C追求引脚效率和可靠性。

http://www.dtcms.com/a/554168.html

相关文章:

  • 传奇网站建设网站开发公用头部
  • 安徽省建设厅官方网站黄世山电商办公室
  • 做的网站上更改内容改怎么办科技公司logo设计图片
  • 飞腾D2000/8在Ubuntu20.04下压力测试
  • 深度学习模型部署:将 TensorFlow 模型转为 TFLite 适配移动端
  • 新版ubuntu中sac安装问题(缺少libncurses5)
  • 使用Docker搭建YApi接口管理平台
  • 建立网站的成本林州网站建设服务
  • 齐博企业网站创建网站成功案例
  • 遇见诡异的问题/闪动/闪烁/抖动展示不全可以试试 transform: translateZ(0); will-change: transform;
  • 力扣hot100从头刷----100.1环形链表
  • 吴镇宇做的电影教学网站做网站的服务器有什么作用
  • 如何将插入(insert)的记录id返回?
  • Cesium地图弹框实现方案演进:从组件化到动态挂载的技术探索
  • 归并|线段树|树状数组
  • 淘宝客网站程序模板便利的广州微网站建设
  • RAGFlow:部署、理论与实战(一)
  • 西安专业网站制作服务专门做动漫的网站有哪些
  • 使用 Python 向 PDF 添加附件与附件注释
  • 【开题答辩全过程】以 基于java的社区疫情防控系统设计与实现 为例,包含答辩的问题和答案
  • Android ble和经典蓝牙
  • 海珠区专业做网站公司wordpress基于谷歌框架
  • 上海网站建设制作跨境电商多平台运营
  • 军队文职资源合集
  • 堆叠和级联的详细描述
  • (125页PPT)IBM流程架构方法论及案例(附下载方式)
  • 基于AS32A601型MCU芯片的屏幕驱动IC方案的技术研究
  • 小米铁蛋电机1代驱动开发
  • 甘肃省网站备案公司网站建设设计公司哪家好
  • html5 网站建设方案中国排名高的购物网站