单片机学习

单片机

什么是单片机

单片机(Microcontroller)是一种小型计算机系统,通常由中央处理器(CPU)、内存、输入/输出端口和定时器等组成,用于控制嵌入式系统中的各种任务和功能。单片机通常用于嵌入式系统,这些系统包括但不限于家电、汽车电子、医疗设备、工业控制、电子玩具等。

单片机的主要特点:

  1. 集成度高:单片机集成了多个关键组件,包括处理器、存储器和输入/输出接口,使其在小尺寸内完成各种任务。

  2. 低成本:由于其小型和大规模生产,单片机通常价格相对较低。

  3. 低功耗:单片机通常被设计为低功耗设备,适合移动电子设备和电池供电的应用。

  4. 实时性:单片机通常能够实时响应输入,对外部环境做出相应的控制。

  5. 内置外设:单片机通常包含用于与外部设备通信的端口,如串口、SPI、I2C等,以便连接传感器、显示屏、执行器等外部设备。

使用基本逻辑。

配置内部寄存器(配置寄存器需要查阅单片机资料)
  • 通过配置寄存器的模式来控制单片机实现不同的内部操作。
配置IO口输出模式
执行需要的指令
  • 通过C代码执行单片机需要执行的指令,一般是通过调整单片机相关引脚高低电平实现相关的操作。注意查看模拟电路的原理图

单片机的最小系统

复位电路

在单片机中,复位电路是一个重要的电路组件,它用于将单片机恢复到其初始状态或者重新启动单片机。复位电路的作用是确保单片机在电源上电时或者在某些异常情况下能够可靠地初始化。

复位电路通常包括以下主要元素:

  1. 复位引脚(RESET Pin):这是单片机上的一个物理引脚,通常用于接收外部复位信号。当复位引脚接收到低电平信号(例如接地),单片机将进入复位状态。

  2. 复位电路的电源监测:这部分电路用于监测电源电压。如果电源电压在正常范围之外,复位电路可以触发复位操作,以确保单片机不会在低电压情况下运行。

  3. 复位触发条件:复位电路通常有多种触发条件,包括上电复位、手动复位(通过复位按钮或者RESET引脚),以及电源不稳定或出现电压下降时的复位。

  4. 复位信号的持续时间:复位电路可以配置以确定复位信号的持续时间,这有助于确保单片机在恢复正常操作之前有足够的时间完成初始化。

  5. 复位后的操作:复位电路可以配置为在复位后执行特定的操作,例如清除寄存器、设置特定的寄存器值或跳转到指定的地址。

  • 复位电路的设计对于单片机的可靠性和稳定性非常重要。它确保了单片机在电源或其他异常情况下能够可靠地初始化,从而避免不确定的行为或数据损坏。设计单片机应用程序时,程序员通常会关注复位电路的配置和操作,以确保系统的稳定性和可靠性。

晶振电路

晶振电路是一种用于产生稳定的时钟信号的电路,通常用于微处理器、微控制器和其他数字电子设备中,以同步其操作。晶振电路的核心部分是晶体振荡器,它基于压电效应产生高稳定性的频率。在电气上它可以等效成一个电容和一个电阻并联再串联一个电容的二端网络。电工学上这个网络有两个谐振点,以频率的高低分,其中较低的频率是串联谐振;较高的频率是并联谐振。由于晶体自身的特性致使这两个频率的距离相当的接近,在这个极窄的频率范围内,晶振等效为一个电感,所以只要晶振的两端并联上合适的电容它就会组成并联谐振电路。这个并联谐振电路加到一个负反馈电路中就可以构成正弦波振荡电路,由于晶振等效为电感的频率范围很窄,所以即使其他元件的参数变化很大,这个振荡器的频率也不会有很大的变化。

晶振电路包括以下关键组件:

  1. 晶振(Crystal Oscillator):晶振是一个小而精确的石英晶体,具有特定的谐振频率。晶振在电场作用下产生机械振动,然后将这种机械振动转换为电信号,生成稳定的时钟信号。

  2. 放大器(Amplifier):晶振电路通常包括一个放大器,用于放大晶振的输出信号以确保其能够提供足够的振荡幅度。

  3. 反馈电路(Feedback Circuit):反馈电路将放大器的输出信号馈送回晶振,以保持振荡的稳定性。这种反馈机制使得晶振电路可以自我调整以保持特定的频率。

  4. 输出接口:晶振电路的输出可以连接到其他电子设备的时钟输入,以提供精确的时钟信号。

晶振电路的主要特点包括:

  1. 高稳定性:晶振电路提供非常高的频率稳定性,这对于需要精确时序控制的应用非常重要。
  2. 精确频率:晶振的谐振频率通常非常精确,因此可以用来生成精确的时钟信号。
  3. 温度稳定性:晶振的频率变化受温度影响较小,因此在不同温度条件下仍然保持高度稳定。
  • 晶振电路通常用于数字系统中,如微处理器、微控制器、计算机和通信设备,以确保它们能够以精确的时间间隔执行操作。这对于计时和协调多个设备之间的操作非常重要。
单片机晶振电路原理和作用概述

晶振是电子元器件中很重要的一个元器件,用于各种电路中,产生振荡频率。单片机中也少不了晶振的存在,我们来看看单片机晶振电路的原理和作用。

单片机的内部时钟与外部时钟

  单片机有内部时钟方式和外部时钟方式两种:

  (1)单片机的XTAL1和XTAL2内部有一片内振荡器结构,但仍需要在XTAL1和XTAL2两端连接一个晶振和两个电容才能组成时钟电路,这种使用晶振配合产生信号的方法是内部时钟方式;

  (2)单片机还可以工作在外部时钟方式下,外部时钟方式较为简单,可直接向单片机XTAL1引脚输入时钟信号方波,而XTAL2管脚悬空。既然外部时钟方式相对内部较为方便,那为什么大多数单片机系统还是选择内部时钟方式呢?这是因为单片机的内部振荡器能与晶振、电容构成一个性能非常好的时钟信号源,而如果要产生这样的信号作为外部时钟信号输入到单片机中,则需要添加的器件远不止一个晶振和两个电容这么简单。时钟电路在单片机系统中很重要,它能控制着单片机工作的节奏,是必不可少的部分

一、有源晶振

有源晶振的精度则可以达到0.1ppm。精度越高,频率稳定性也更好。有源晶振在稳定性上要胜过无源晶振。有源晶振一般4个脚,一个电源,一个接地,一个信号输出端,一个NC(空脚)。有源晶振自身就能震动。

注: R1、C3需根据实际进行调整处理,电源必须是稳压的且电源引线尽量短,并尽量与系统中使用晶振信号的芯片共地。

二、无源晶振

无源晶振有2个引脚,需要借助于外部的时钟电路(接到主IC内部的震荡电路)才能产生振荡信号,自身无法振荡。两个外接电容能够微调晶振产生的时钟频率。

1、晶振负载电容选择

不同公司生产的同一标称频率的晶振,所需要的负载电容CL(Load capacitance)可能不同。通过公式计算电容容值


某公司晶振数据
晶振外部负载电容计算

为晶振容性负载,可通过查阅晶振数据手册获取

为引线电容,可通过查询芯片数据手册获取

为引脚电容,可通过查询芯片数据手册获取,在3-6pF之间

例子:假设晶振为晶振容性负载=0pF,引线电容=2.8pF,引脚电容=6pF,则晶振外部负载电容,因此晶振外围电容容值选择33pF。

3、晶振并联一个1M电阻(KHz晶振电路,并联电阻通常为10M欧,MHz晶振,并联电容通常为1M欧左右)

并联电阻的作用: 
1.降低晶体的Q值,Q值降低后晶体起振比较容易。

2.抑制EMI,EMI不过时,可减小阻值。

3.提供直流工作点。

4.使门电路工作于线性区,并联1M电阻增加了电路中的负性阻抗(-R),即提升了增益,缩短了晶振起振时间,达到了晶振起振更容易之目的。

5.在低温环境下振荡电路阻抗也会发生变化,当阻抗增加到一定程度时,晶振就会发生起振困难或不起振现象给晶振并联1MΩ电阻,增加振荡电路稳定性。

注:并联电阻不能太小,串联电阻不能太大。否则,在温度较低的情况下不易起振。

4、串联电阻

串联电阻的作用:

1.降低晶体的激励功率,防止损坏。

2.限制振荡幅度。

3、给晶振同时串联一个100Ω的电阻,减少晶振的频率偏移程度。

三、晶振选型关键参数

1、基准频率:晶振在完全理想条件下的振荡频率。

2、工作电压:晶振的工作需要外界提供一定的电源电压,晶振输出的时钟信号上的噪声与电源噪声紧密相关,因此在晶振器件资料上,对电源的质量有一定的要求。

3、输出电平:信号电平种类有许多种,时钟信号的电平也多种多样。在选型中,应根据所需时钟电平的种类,选择对应的晶振。

4、工作温度范围:根据环境温度要求的不同,应选择对应的工作温度范围。

5、频率精度:对应不同的工作温度范围,可选择不同的频率精度。参数是晶振最重要的参数,包含了由于温度变化、电源电压波动、负载变化等因素引起的频率偏差。

6、老化度:在恒定的外界条件下测量晶振频率,频率精度与时间之间的关系。

7、启动时间:从上电到晶振输出频率的偏差达到规定的频率精度所需要的时间。

四、晶振分类

晶振的分类常用的晶振分为三类:标准封装晶体振荡器SPXO、压控式晶体振荡器VCXO、温度补偿式晶体振荡器TCXO。

(1)标准封装晶体振荡器SPXO

无温度补偿功能,也无电压控制功能,其频率特性完全取决于晶体以及外部振荡电路,频率精度最高可以达到士10ppm,是晶振中精度最差的一个种类。

主要参数

1、基准频率:多种频率可供选择。

2、工作电压:支持1.8V、2.5V、3.3V、5.0V。

3、输出电平:输出时钟信号电平为CMOS。

4、工作温度范围:SPXO不具备温度补偿功能,因此在PCB布局时,SPXO器件应远离发热源。

5、频率精度:频率精度已完全包括了温度变化、电源电压波动、老化等因素引起的频率偏差。

6、老化度:由于老化对晶振精度的影响已经包含在频率精度中。

7、启动时间:最大值为10ms。

8、占空比:在双边沿采样的时序计算中,应考虑占空比参数。

9、时钟抖动(Jitter):两个抖动参数:一个是RMS Jitter(12kHz~20MHz),典型值为0.5ps;另一个是RMS Jitter(全频段),典型值为2.5ps。

(2)压控式晶体振荡器VCXO

VCXO常用在锁相环(PLL,Phase-Locked Loop)电路中。

主要参数:

1、基准频率:从77.76MHz开始,最高可达200MHz,在这个区间中有多种频率可供选择。

2、输出电平:输出时钟信号的电平为LVPECL。

3、工作温度范围:根据环境温度要求的不同,应选择对应的工作温度范围。

4、频率--温度稳定度:±20ppm,表示在工作温度范围内,频率偏移的最大值。VCXO不具备温度补偿功能,因此在PCB布局时,VCXO器件应远离发热源。

5、电源电压:3.3V,这是VCXO的工作电压。

6、控制电压:调整范围为0.3~3V,除了电源电压外,VCXO还需要控制电压,以调节输出频率。

7、牵引范围(Pull Range):从中央电压开始,将调整电压向最小值或最大值的方向调整时,输出频率的变化范围。

8、绝对牵引范围(Absolute Pull Range):绝对牵引范围=牵引范围(由于温度变化、老化、电源波动、负载变化造成的偏移范围),在根据芯片所要求的频率偏移最大量选型VCXO时,应参考绝对牵引范围,而不是牵引范围。

9、频偏敏感度:在牵引范围一调整电压曲线图中,“Gain”曲线代表频偏敏感度。以ppmV为单位。

10、线性度:指“Gain”曲线的线性程度。该参数值越小越好。一般而言,VCXO的牵引范围越宽,线性度越差。

11、调制带宽:VCXO能支持的调整电压最大带宽。

12、时钟抖动:四个抖动参数,一是RMS Jitter(12kHz20MHz),典型值为0.3ps;二是RMS Jitter(50kHz80MHz),典型值为0.5ps;三是RMS Jitter(全带宽),典型值为3ps;四是Jitter.蜂-蜂值,典型值为20ps.

(3)温度补偿式晶体振荡器TCXO

TCXO是利用附加的温度补偿电路以减小环境温度对振荡频率影响的一种晶体振荡器。其特点是:频率精度远远高于SPXO和VCXO。
 

主要参数:

1、基准频率:最高可达27MHz,有多种频率可供选择。

2、输出电平:输出时钟信号电平为CMOS。

3、工作温度范围:根据环境温度要求的不同,应选择对应的工作温度范围。

4、电源电压:3.3V或5V。

5、控制电压:TCXO提供一个引脚作为控制电压的输入,以实现类似于VCXO利用控制电压调整输出频率的功能,当不使用该功能时,应将此引脚接地。

6、启动时间:最大值为2ms。

7、相位噪声:

8、频率精度:频率精度参数已完全包括了温度变化、电源电压波动、老化等因素引起的频率偏差。根据芯片对时钟精度的要求,对TCXO选型时,这是最重要的一项参数。

原文链接:https://blog.csdn.net/weixin_52856735/article/details/131041138

《单片机晶振电路为什么用22pf或30pf的电容》)

单片机如何识别晶振电路的时钟信号的?

由于单片机识别的是数字信号,而晶振产生的是振荡波形,要通过单片机内部时钟发生电路,将振荡信号整合成矩形波形信号。足以被单片机识别,作为单片机的时钟。

时钟系统

在单片机中,时钟系统是一个至关重要的组件,它提供了单片机正常运行和协调各种操作的时序基准。时钟系统用于同步处理器的操作、定时、串行通信、PWM 生成、中断触发等任务。以下是时钟系统在单片机中的一些关键方面:

  1. 主时钟源:单片机通常有一个主时钟源,用于驱动处理器和大多数外设。这个时钟源可以是晶振、外部时钟信号、内部振荡器等。时钟频率可以根据应用需求进行配置,通常以赫兹(Hz)为单位。

  2. 时钟分频器:时钟分频器允许单片机将主时钟源分频为较低的频率,以降低功耗或调整执行速度。分频器通常用于减小时钟频率,以适应不同的外设和任务。

  3. 多路复用时钟源:一些单片机支持多个时钟源,允许根据应用需求切换不同的时钟源。这对于多模式操作和节能模式非常有用。

  4. 定时器和计数器:单片机通常具有多个定时器和计数器,用于生成精确的时间间隔、测量脉冲宽度、生成 PWM 信号等。这些定时器和计数器受到时钟源的影响。

  5. 串行通信时钟:串行通信协议(如 UART、SPI、I2C)通常需要时钟信号,以协调数据的传输。时钟源也可以用于生成串行通信时钟。

  6. 中断触发时钟:时钟系统还用于触发中断,以及计算中断延迟和间隔。中断通常是单片机执行实时任务的关键机制。

  7. 低功耗模式:时钟系统通常可以用于进入低功耗模式,以减少功耗,延长电池寿命。

  8. 时钟故障检测:一些单片机具有时钟故障检测机制,用于检测和响应主时钟源的故障,以确保系统的可靠性。

晶振周期和机器周期

1. 晶振周期(Crystal Clock Cycle):
   - 晶振周期是指晶振振荡器(也称为晶体振荡器或时钟晶振)产生一个完整的振荡周期所需的时间。
   - 晶振周期通常是一个基本的时间单位,它决定了微控制器的时钟频率。例如,如果你的嵌入式系统使用12MHz晶振,那么每个晶振周期是1/12,000,000秒(或称为12MHz时钟周期)。
   - 晶振周期是硬件级别的概念,通常与外部晶振的特性和微控制器的时钟电路有关。

晶振时钟周期为1秒钟运行的周期
例如:C8051晶振周期为12MHZ,即1秒钟可以产生12000000次周期,以此可以计算一个周期是多长时间,可以精确控制程序运行时间。

2. 机器周期(Machine Cycle):
   - 机器周期是一个相对于微控制器的操作和时钟频率的时间单位。
   - 机器周期通常是指微控制器执行一个机器指令(如汇编语言指令)所需的时间。
   - 机器周期与微控制器的内部架构和时钟频率相关。不同的微控制器可能有不同的机器周期长度。
   - 机器周期通常用于衡量指令的执行时间,以便进行时间相关的编程和操作。

定时器的模式对机器周期的影响

在1T模式下,机器周期通常是1个晶振周期的长度。这是因为1T模式表示定时器工作在CPU的时钟周期(T)下,而大多数8051微控制器的CPU需要12个晶振周期(12T)来完成一个时钟周期。

因此,在1T模式下,每个机器周期通常等于一个晶振周期,而在12T模式下,每个机器周期等于12个晶振周期的长度。这意味着在1T模式下,机器周期更短,可以执行更多的指令操作,但也可能需要更高的晶振频率以满足一些时间要求。

时间计算

在1T模式下,每个计数周期需要12个晶振周期。因此,不需要再额外乘以12来计算中断时间。我之前的回答中出现的错误是我混淆了1T模式和12T模式。所以,在1T模式下,中断时间的计算是:

中断时间 = (65536 - 初始值) * 1 / 晶振频率

其中,1表示一个晶振周期。12T模式下1要改为12。

实例

对于8051微控制器,计时器计数从40到65536,晶振频率为12MHz,我们可以使用以下公式来计算所需的时间:

中断时间 = (65536 - 初始值) * 1 / 晶振频率

其中,中断时间表示从初始值(40)开始计数到65536所需的时间。在这个公式中,1表示一个晶振周期。

将这些值代入公式:

中断时间 = (65536 - 40) * 1 / 12,000,000 ≈ 5.45 毫秒

因此,从初始值40计数到65536需要大约5.45毫秒的时间,假设8051微控制器工作在1T模式下,晶振频率为12MHz。这个时间是从计时器开始计数到达65536时的时间。

定时器

单片机中的定时器是一个非常重要的外设,用于执行时间相关的任务,如延时、计时、PWM(脉冲宽度调制)等。下面是单片机中定时器的基本使用方法和原理:

1. 定时器的基本原理:

单片机中的定时器是一个计数器,它基于晶振或内部时钟源计算时间。通常,定时器由几个寄存器组成,包括计数寄存器、控制寄存器和溢出标志寄存器。定时器从零开始计数,每个时钟脉冲增加计数器的值。当计数器达到特定的值(通常是一个预设的值),溢出标志位被设置,同时触发一个中断,或者执行特定的操作(取决于编程)。

2. 定时器的使用方法:

以下是单片机中定时器的一般使用方法:

a. 配置定时器: 在使用定时器之前,您需要配置它,这通常包括以下内容:

  • 选择时钟源:选择一个时钟源,例如晶振频率或内部时钟。
  • 设置计数器的初始值:将计数器初始化为适当的值。
  • 配置溢出条件:设置计数器的溢出值,即当计数器达到这个值时触发溢出。
  • 启用定时器中断(可选):如果需要定时中断,需要启用中断使能位。
    • 配置定时器模式
    • 配置定时器定时方式
    • 配置定时器门控位
    • 配置定时器装载值(高8位和低8位)
    • 配置定时器中断溢出标志
    • 使能定时器的溢出中断允许位
    • 让定时器开始定时

b. 启动定时器: 一旦配置好定时器,启动它以开始计时。

c. 监控定时器状态: 在编程中,您需要定期检查定时器的状态,以确定它是否溢出。这可以通过读取溢出标志寄存器或检查中断标志位来实现。

d. 处理定时器中断(可选): 如果启用了定时器中断,您需要编写中断服务程序来处理中断。在中断服务程序中,通常会清除溢出标志并执行所需的操作,例如更新计时器的值、延时或执行其他任务。

e. 停止或重新配置定时器: 在定时器的任务完成后,您可以选择停止定时器,重新配置它,或者重启它以执行其他定时任务。

3. 应用示例:

  • 延时函数: 使用定时器来创建延时函数,以便在程序中等待一段时间。
  • 计时应用: 用于计算过程的执行时间,如测量反应时间或执行某个任务所需的时间。
    • 16位自动重装模式是一种用于定时器的工作模式,通常在嵌入式系统中使用。它具有以下特征和工作原理:

    • 16位模式:在这种模式下,定时器的计数器是一个16位的寄存器,可以存储16位的二进制数值。这允许计时器的计数范围从0到65535(2^16-1)。

    • 自动重装:自动重装模式意味着当定时器的计数器达到最大值(65535)时,它会自动重新加载初始值,而不需要手动干预。这使得定时器可以循环计数,以实现周期性的定时任务,而不需要在每次计数器溢出时手动重置计数器。

    • 应用:16位自动重装模式经常用于生成周期性的中断或产生精确的时间延迟。例如,嵌入式系统可以使用它来定期执行任务,测量时间间隔,或者生成精确的定时信号。

    • 配置:通常,您需要配置初始值来设置定时器的计数器从何处开始计数,以及中断触发的时间间隔。一旦配置完成,定时器将在每次计数器溢出时触发中断,然后自动重新加载初始值,从而实现连续的计数。

  • PWM生成: 定时器可以用于生成脉冲宽度调制信号,用于控制电机速度、LED亮度等。
    • 当我们使用16位的值来表示PWM的占空比时,这个16位值通常被分为两部分,一部分称为高字节(high byte),另一部分称为低字节(low byte)。这种分割的目的是为了更精细地控制PWM信号的占空比。让我用一个更具体的例子来解释:

    • 高字节(High Byte):高字节通常包含占空比值的高位部分。这一部分控制PWM信号的高电平持续时间的较高百分比。例如,如果高字节的值是100,那么这部分将控制PWM信号的高电平占整个周期的较高百分比。高字节的值范围通常是0-255。

    • 低字节(Low Byte):低字节通常包含占空比值的低位部分。这一部分控制PWM信号的高电平持续时间的较低百分比。如果低字节的值是50,它将控制高电平占整个周期的较低百分比。低字节的值也在0-255之间。

    • 将高字节设置为37,表示37.5%的高电平占空比的整数部分。
    • 将低字节设置为0x80,这是0.5的二进制表示(因为50%的二进制表示为0.5),表示37.5%高电平占空比的小数部分。

    PWM(脉冲宽度调制)信号的周期通常是根据定时器的周期来计算的。定时器提供了产生PWM信号的时序基准,其中包括周期和占空比的控制。

    具体来说:

    1. PWM周期:PWM信号的周期是由定时器的溢出周期来决定的。定时器每次计数溢出都会触发一个中断或硬件事件,这个溢出周期就是PWM信号的周期。你可以通过设置定时器的溢出周期来控制PWM信号的频率。

    2. PWM占空比:*PWM信号的占空比表示高电平在一个PWM周期内所占的时间比例。占空比通常是通过控制高电平持续的时间来实现,而这个时间是通过定时器的计数来计算的。通过适当的计数值,你可以设置占空比,从而控制高电平的持续时间。

    因此,定时器的周期和计数在生成PWM信号时起到关键作用。定时器提供了精确的时序控制,以确保PWM信号的周期和占空比与应用要求一致。这种方式使得PWM信号的生成非常可控和精确。

  • 周期性任务: 定时器可以周期性地触发中断,用于执行某些周期性任务。

 定时器运行逻辑

定时器的中断系统在嵌入式系统中是一种非常重要的功能,它允许你在特定时间间隔或定时器溢出事件发生时执行特定的代码。以下是定时器的中断系统的一般逻辑:

定时器设置:首先,你需要配置定时器的工作模式、时钟源、预分频因子和溢出周期。这些设置将影响定时器的计数速度和溢出间隔。

中断使能:你需要启用定时器中断。这通常通过设置定时器控制寄存器中的相应标志位来实现。

定时器计数:一旦定时器开始计数,它将按照预定的速度递增。当定时器的计数值达到预设的溢出值时,定时器将触发一个中断。

中断服务例程:当定时器触发中断时,CPU将跳转到中断服务例程(中断处理程序)。这是一个用户定义的函数,用于执行特定的操作或任务。

中断处理:在中断服务例程中,你可以执行各种操作,如更新PWM占空比、执行定期任务、采集传感器数据等。这些操作通常与定时器的特定应用相关。

中断完成:中断服务例程执行完后,控制返回到主程序,继续正常的程序执行。

当中断程序执行时,通常是这样的:

  1. 定时器计数器被冻结:当中断发生时,通常会暂停定时器的计数器。这意味着计数器将不再递增,直到中断服务程序执行完成。

  2. 中断服务程序执行:中断服务程序是一个特定的函数,它在中断触发时执行。在执行中断服务程序期间,计数器暂停,中断服务程序可以执行特定的任务。

  3. 定时器继续计数:一旦中断服务程序执行完成,系统会恢复定时器的计数,继续递增。这意味着定时器在中断服务程序执行期间不会一直执行。

重复定时器中断:定时器会继续计数,生成周期性的中断。这样,你可以在每个定时器溢出时执行相同或不同的操作。

总的来说,定时器中断系统允许你在预定的时间间隔内执行代码,这对于实现周期性任务、时间测量和时序控制非常有用。中断服务例程是用于响应定时器中断事件的自定义代码段,它可以根据应用需求执行不同的任务。中断系统的逻辑在不同的嵌入式系统和微控制器上可能会有细微的差异,但上述步骤通常是通用的。

实例运转逻辑原理

1. 延时函数的实现:

原理: 延时函数用于在程序中等待一段时间,通常通过定时器来实现。基本原理是使用定时器计数来测量经过的时间。

步骤:

  1. 配置一个定时器以使用所需的时钟源(例如,微秒或毫秒级别的时钟源)。
  2. 设置计数器的初始值,以及需要等待的延时时间。
  3. 启动定时器,并等待计数器达到设定的延时时间。
  4. 一旦计数器达到设定值,停止定时器,延时函数完成。

2. PWM生成:

原理: 脉冲宽度调制(PWM)是通过改变脉冲的占空比来控制输出信号的一种技术。在定时器的帮助下,PWM信号的周期和占空比可以精确控制。

步骤:

  1. 配置一个定时器,选择适当的时钟源。
  2. 设置定时器的周期(PWM周期)和占空比(PWM占空比)。
  3. 启动定时器,它将自动递增计数器。
  4. 在每个计数器溢出之前,检查计数器的值,当计数器小于PWM占空比时,输出高电平;否则输出低电平。
  5. 当计数器溢出时,重置计数器,重复步骤3和4。

3. 计时应用:

原理: 定时器用于测量某个过程的执行时间,通过记录开始时间和结束时间之间的计数值来计算时间差。

步骤:

  1. 配置一个定时器以使用适当的时钟源。
  2. 记录过程的开始时间,启动定时器。
  3. 执行需要计时的过程。
  4. 当过程完成时,记录结束时间,停止定时器。
  5. 计算开始时间和结束时间之间的计数值差,将其转换为实际时间。

范例程序

1、延时函数的示例程序:
#include <C8051Fxxx.h>

void Delay_ms(unsigned int ms) {
    int i, j;
    for (i = 0; i < ms; i++) {
        for (j = 0; j < 1234; j++);  // 根据实际情况调整延时
    }
}

void main(void) {
    // 初始化单片机配置

    while (1) {
        // 执行其他任务

        // 延时1秒
        Delay_ms(1000);
    }
}
----------------------------------------------------
2、PWM生成的示例程序:
#include <C8051Fxxx.h>

sbit PWM_OUT = P1^0;  // PWM输出引脚

void ConfigureTimerForPWM(void) {
    // 配置定时器,选择时钟源和周期
    // 设置PWM占空比
    // 启动定时器
}

void main(void) {
    // 初始化单片机配置
    ConfigureTimerForPWM();

    while (1) {
        // 执行其他任务

        // 假设需要产生50%占空比的PWM信号
        PWM_OUT = 1;  // 设置高电平
        // 延时以控制PWM占空比
        // 例如,延时一半的周期时间
        Delay_ms(PWM_PERIOD / 2);
        PWM_OUT = 0;  // 设置低电平
        // 延时一半的周期时间
        Delay_ms(PWM_PERIOD / 2);
    }
}
-------------------------------------------------------
3. 计时应用的示例程序:
#include <C8051Fxxx.h>

unsigned long startTime, endTime, elapsedTime;

void ConfigureTimerForTiming(void) {
    // 配置定时器,选择时钟源
    // 启动定时器
}

void main(void) {
    // 初始化单片机配置
    ConfigureTimerForTiming();

    while (1) {
        // 执行其他任务

        // 记录开始时间
        startTime = GetTimerValue();

        // 执行需要计时的过程
        // 例如,一段代码

        // 记录结束时间
        endTime = GetTimerValue();

        // 计算执行时间
        elapsedTime = endTime - startTime;
    }
}

主要作用和运用包括:

  1. 精确延时: 定时器可以用来生成精确的延时,以满足特定应用的时间需求。这对于需要定时执行任务、控制外部设备的操作或实现时序相关功能非常有用。

  2. 计时: 定时器可以用于测量经过的时间,例如,计算两个事件之间的时间间隔。这对于实现定时任务、测速、测距或时间相关的应用非常有用。

  3. PWM(脉宽调制): 定时器通常用于生成PWM信号,以控制电机、LED亮度、声音输出等。通过调整定时器的参数,可以实现不同占空比的PWM信号,从而控制输出设备的状态。

  4. 定时中断: 定时器可以配置为生成定时中断,以在特定时间间隔内触发中断服务例程。这对于实现时间敏感的任务、数据采集、周期性任务等非常有用。

  5. 计数器: 定时器可以用作计数器,用于计数输入脉冲、测量脉冲频率、实现脉冲宽度测量等应用。它可以用于实现频率计、计数事件次数等。

  6. 实时时钟: 定时器通常用于实现系统的实时时钟。通过配置定时器,可以跟踪系统运行时间、日期和时钟功能。

  7. 通信协议: 一些通信协议,如UART通信、SPI通信、I2C通信,可能需要定时器来生成正确的时序信号以进行通信。

  8. 蜂鸣器和声音控制: 定时器可以用于控制蜂鸣器或产生声音信号。通过配置不同的频率和占空比,可以产生不同音调的声音。

  9. 数据采样和数据传输: 定时器可以用于定期触发ADC(模数转换器)的采样操作,或用于控制数据的传输速率,以与其他设备进行数据通信。

中断系统

单片机的中断系统是嵌入式系统中的一个重要组成部分,它允许单片机在执行程序的同时对外部事件或条件产生的中断请求做出响应。中断系统允许单片机立即停止当前正在执行的指令,转而执行与中断相关的处理程序,然后再返回原来的任务。以下是单片机中断系统的一般概述:

  1. 中断源: 中断源可以是来自外部设备的信号(如按键按下、传感器触发等)、定时器溢出、通信模块(如UART、SPI、I2C)的数据准备就绪、系统异常(如除零异常)、或其他特定事件。中断源会触发中断请求。

  2. 中断请求(IRQ): 当中断源产生事件并满足特定条件时,它会产生一个中断请求(IRQ),通知单片机需要执行与中断相关的处理程序。

  3. 中断控制器: 中断控制器是硬件模块,用于管理不同中断源的优先级、中断使能/禁用、中断触发条件等。它协调中断请求的处理次序。

  4. 中断向量表: 中断向量表是一个包含中断服务例程地址的表格。当中断发生时,单片机会查找中断向量表,找到相应中断源的中断服务例程的地址,然后跳转到该地址开始执行中断处理。

  5. 中断服务例程: 中断服务例程是特定中断源的处理程序,它是一段程序代码,用于执行与中断相关的操作,处理中断源引发的事件。中断服务例程通常比较紧凑和高效,以尽快完成中断处理。

  6. 中断优先级: 不同中断源可以具有不同的优先级。中断控制器通常允许配置中断的优先级,以确保在多个中断请求同时发生时,能够按照优先级的顺序进行处理。

  7. 中断使能/禁用: 中断系统允许开发者选择性地启用或禁用特定中断源,以控制哪些中断可以触发中断请求。

  8. 中断嵌套: 一些单片机支持中断嵌套,允许在处理一个中断时允许更高优先级的中断中断当前中断,以确保关键任务及时得到响应。

  9. 中断标志: 中断标志通常用于指示特定中断源是否已经被处理。处理中断时,必须清除中断标志,以允许下一次中断请求被响应。

下降沿中断
  • 当信号从低电平(0)变为高电平(1)时,触发上升沿中断。这个信号变化边沿被称为上升沿。
  • 上升沿中断通常用于监测信号的变化,以便在信号上升到高电平时触发某种操作。这种操作可能包括检测按钮按下、检测传感器触发、开始计时等。
  • 在中断控制器中,你通常可以配置中断触发方式为上升沿触发,以指示只有在上升沿发生时中断才会被触发。
上升沿中断
  • 当信号从高电平(1)变为低电平(0)时,触发下降沿中断。这个信号变化边沿被称为下降沿。
  • 下降沿中断通常用于监测信号的变化,以便在信号下降到低电平时触发某种操作。这种操作可能包括检测按钮释放、检测传感器松开、停止计时等。
  • 在中断控制器中,你通常可以配置中断触发方式为下降沿触发,以指示只有在下降沿发生时中断才会被触发。
中断结构图

中断引脚编号

该编号为STC8H系列芯片,其他的请参考芯片手册

基本工作条件引脚

在一些单片机和集成电路中,有一些引脚被称为基本工作条件引脚(Basic Operating Condition Pins)。这些引脚用于配置和控制器的基本工作条件,例如时钟源选择、电源管理、复位控制等。这些引脚的名称和功能可能因单片机型号和制造商而异,但下面是一些常见的基本工作条件引脚和它们的功能:

  1. 时钟源引脚:时钟源引脚通常用于选择单片机的时钟源,例如外部晶振或内部时钟源。通过配置这些引脚,可以调整单片机的时钟频率和源,以适应特定应用的需求。

  2. 复位引脚:复位引脚用于将单片机恢复到其初始状态,通常在上电时或发生异常情况下触发。通过配置复位引脚,可以控制复位的方式和时机。

  3. 电源引脚:电源引脚用于提供电源电压给单片机,包括电源输入引脚和地引脚。通过连接正确的电源引脚,可以确保单片机正常工作。

  4. 低功耗控制引脚:一些单片机具有用于进入低功耗模式或唤醒单片机的引脚。通过配置这些引脚,可以管理单片机的电源消耗。

  5. 时序引脚:一些单片机需要特定的时序引脚来实现同步操作,例如时钟同步、数据同步等。通过配置这些引脚,可以确保正确的时序操作。

  6. 晶振引脚:如果单片机使用外部晶振作为时钟源,晶振引脚用于连接晶振到单片机。通过配置这些引脚,可以确保时钟源的稳定性。

输入输出引脚

输入/输出(I/O)引脚是用于与外部设备通信的连接点,允许单片机发送和接收数字或模拟信号。这些引脚在单片机上具有不同的功能,可以配置为输入或输出,以便满足特定应用的需求。以下是有关I/O引脚的一些重要信息:

  1. I/O引脚的数量:单片机的数量和类型取决于单片机的型号和制造商。一些单片机只有几个I/O引脚,而其他高性能的单片机可以具有大量的I/O引脚。

  2. 数字输入引脚:数字输入引脚用于接收外部数字信号,例如开关状态、传感器数据等。它们通常可以配置为高电平或低电平输入。

  3. 数字输出引脚:数字输出引脚用于向外部设备发送数字信号,例如控制LED、继电器、数码管等。它们可以配置为高电平或低电平输出。

  4. 模拟输入引脚:模拟输入引脚用于测量模拟信号,例如电压、温度、光线等。这些引脚通常与模拟数字转换器(ADC)结合使用。

  5. 中断引脚:一些I/O引脚可以配置为中断引脚,用于在发生特定事件时触发中断服务程序,以实现即时响应。

  6. 上下拉电阻:一些I/O引脚支持内部上拉或下拉电阻,用于保持输入引脚在特定电平上,以防止浮动状态。

  7. 串行通信引脚:一些I/O引脚可用于串行通信,例如UART、SPI、I2C等。它们允许单片机与其他设备进行串行数据交换。

  8. 定时器/计数器引脚:一些I/O引脚与内部定时器和计数器模块相关联,用于生成脉冲、测量时间间隔等。

  9. PWM引脚:PWM(脉冲宽度调制)引脚用于产生可变占空比的脉冲信号,通常用于电机控制、调光等应用。

  10. 电源引脚:电源引脚用于提供电源电压给外部设备。

  11. 复位引脚:复位引脚用于将单片机恢复到其初始状态,通常在上电时或发生异常情况下触发。

串口收发数据

波特率(Baud Rate)是指串行通信中传输数据的速率,通常以每秒传输的符号数或位数(即波特)来表示。波特率决定了每秒钟可以传输多少位二进制数据。这是串行通信中的一个重要参数,用于确保发送和接收设备之间的数据传输速率一致。

波特率通常以每秒的单位(bps,bits per second)来表示。一些常见的波特率包括:

  • 9600 bps:每秒传输9600位数据。
  • 115200 bps:每秒传输115,200位数据。

波特率的选择受到通信设备和通信媒体的限制。通常,发送和接收设备必须以相同的波特率进行通信,以确保数据能够正确传输。如果波特率不匹配,数据传输可能会出现错误。

在串行通信中,波特率决定了数据位的传输速率,以及数据的时间间隔。较高的波特率意味着更快的数据传输速度,但也需要更高的带宽和更短的数据位传输时间。因此,在选择波特率时,需要考虑通信设备的性能和通信媒体的能力,以确保稳定的数据传输。

控制引脚

控制引脚是单片机上的数字输出引脚,用于控制外部设备的状态或执行特定的操作。这些引脚通常可以配置为高电平(1)或低电平(0),以控制连接的设备的行为。控制引脚在嵌入式系统和单片机应用中非常常见,用于执行各种任务,如控制LED、继电器、电机、蜂鸣器等外部设备。以下是有关控制引脚的一些关键信息:

  1. 数字输出引脚:控制引脚通常是数字输出引脚,用于向外部设备发送数字信号。它们可以配置为高电平或低电平,具体取决于设备的需求。

  2. 开关设备:控制引脚经常用于控制开关设备,例如继电器,它们可以打开或关闭电路中的电流。这在自动化和远程控制应用中非常有用。

  3. 指示设备:控制引脚还用于控制指示设备,如LED。通过改变控制引脚的状态,可以使LED亮起或熄灭,以指示不同的状态。

  4. 电机控制:电机驱动电路通常使用控制引脚来实现电机的正转、反转和停止等操作。这对于机器人、小车和其他移动设备非常重要。

  5. 蜂鸣器控制:控制引脚可以用于触发蜂鸣器发出声音,用于警告、提醒或指示。

  6. 定时控制:控制引脚可以与计时器和计数器模块结合使用,用于生成精确的脉冲信号,如PWM信号,以进行定时控制。

  7. 复位控制:某些控制引脚用于控制单片机的复位操作,以将其恢复到初始状态。

配置单片机I/O口

实例:(STC8H3K64S4-LQFP48)

单片机中一个端口通常包含8位

  1. 数据表示:计算机和单片机通常使用二进制表示数据。使用8位端口允许以字节(8位)为单位处理数据,这样更符合计算机的数据表示方式。通过8位端口,你可以直接读取和写入8位的数据,而不必进行额外的位操作。

  2. 数据总线:8位端口通常用于连接到数据总线,允许同时传输8位数据,这对于许多应用非常重要。如果只有单一位端口,需要进行多次传输才能处理相同数量的数据,这会增加通信的复杂性和延迟。

  3. I/O设备:许多外部设备,如键盘、显示屏、传感器等,通常也使用8位数据进行通信。使用8位端口可以更轻松地与这些设备进行数据交换。

  4. 硬件设计:硬件设计中使用8位端口通常更为方便。8位端口的引脚通常会布局在一个接口上,使得PCB设计更紧凑,并且容易布线。

 单片机如何控制8位端口

单片机中的一个8位端口(通常称为8位GPIO口)可以控制8个引脚,这是因为每个引脚与端口的每一位相对应。每一位可以控制相应引脚的状态(高电平或低电平),并且可以通过适当的位操作来设置或清除引脚的状态。以下是一个简单的示例来说明如何使用一个8位端口来控制8个引脚:

假设你有一个8位端口 P0,其中每个位控制一个引脚,从 P0.0 到 P0.7。要控制这些引脚,你可以执行以下操作:

  1. 设置 P0 的某一位为 1(高电平)以点亮相应的引脚,或者设置为 0(低电平)以熄灭相应的引脚。

  2. 使用位操作(如按位与、按位或、按位取反)来操作 P0 寄存器中的特定位。

例如,以下是如何设置 P0 的第 3 个引脚(P0.2)为高电平:

P0 = P0 | (1 << 2);

这里 (1 << 2) 表示将二进制位 00000100 左移 2 位,得到 00001000,然后将其与 P0 寄存器中的相应位进行按位或操作,将 P0.2 设置为高电平。

单片机单个引脚有多个功能,如何保证不混乱?

  1. 引脚配置: 在编程中,你需要明确指定引脚的功能。这通常通过设置相应的寄存器来实现,例如 GPIO 寄存器。不同的功能会涉及不同的寄存器配置。确保你正确配置了引脚以满足应用需求。

  2. 引脚状态: 当引脚具有多个功能时,其状态(输入或输出)和电平(高或低)可能会对不同功能产生影响。确保引脚状态和电平与所选功能相匹配。

  3. 共享资源: 在一些情况下,多个引脚可能共享某些资源,如模拟信号输入。这些共享资源可能需要考虑,以避免资源竞争或冲突。

  4. 时序和优先级: 不同功能的引脚可能具有不同的时序要求。确保时序满足各个功能的要求。如果多个功能同时使用一个引脚,确保有适当的优先级设置。

  5. 文档和数据表: 在使用单片机时,详细阅读数据表和文档是至关重要的。数据表会提供有关引脚多功能的信息,包括功能列表、配置寄存器和注意事项。

引脚模式:

准双向口模式

在准双向口模式中,设备可以切换其数据引脚的功能,以在不同的时间点发送和接收数据。这种模式通常用于一对一或一对多的通信中,其中每个设备需要能够发送和接收数据,但不需要同时执行这两个操作。

准双向口模式通常比全双工模式更简单,因为在同一时刻只有一个方向的数据传输,但对于许多应用来说足够了。如果需要同时进行双向数据传输,可以考虑使用全双工模式,其中发送和接收数据可以同时进行,通常需要更多的引脚和更复杂的电路。

强推挽输出模式

强推挽输出模式" 是数字电子设备中用于配置输出引脚的一种工作模式,它允许输出引脚在需要时提供更高电流的能力,并且可以拉高(推挽)和拉低(挽)输出电平。这个模式通常用于控制外部设备、驱动负载或与其他数字电路进行通信。

  1. 高电流输出:在强推挽输出模式下,输出引脚能够提供较高的电流,以驱动要求较高电流的负载,例如电机、继电器或其他外部设备。

  2. 能够拉高和拉低:在强推挽输出模式下,输出引脚可以拉高(输出高电平)和拉低(输出低电平)。这使得输出引脚可以控制外部设备的电平状态。

  3. 与地和电源的连接:在强推挽输出模式下,输出引脚通常可以直接连接到电源电压或地,而不需要外部电阻。这使得它们在控制负载时更为方便。

  4. 广泛应用:强推挽输出模式常见于微控制器、单片机、FPGA 等数字电子设备中,用于控制外部设备或与其他数字电路进行通信。例如,它可以用于控制 LED 灯、电机驱动、继电器操作等。

  5. 高噪声抗干扰:由于强推挽输出提供更高电流能力,因此它通常对电路中的噪声和干扰更具抵抗力。

  • 需要注意的是,强推挽输出模式通常需要额外的电源电压来供电,以提供较高的电流。此外,使用强推挽输出模式时需要小心,确保输出引脚与负载和电源电压之间的连接正确,以防止过流或其他问题。强推挽输出模式在许多嵌入式系统和数字电路中都非常有用,因为它提供了更大的控制灵活性和电流输出能力。

开漏输出模式

开漏输出模式" 是数字电子设备中用于配置输出引脚的一种工作模式。在开漏输出模式下,输出引脚可以将信号拉低(输出低电平),但不能将信号主动拉高到高电平。为了将信号拉高,通常需要外部上拉电阻。

  1. 输出低电平:在开漏输出模式下,输出引脚可以将信号拉低,即输出低电平。这通常表示输出引脚与地(GND)连接,形成一个电气连接,使电流能够流过。

  2. 无法推高电平:与推挽输出模式不同,开漏输出模式下,输出引脚不能主动拉高电平。如果需要将信号拉高到高电平状态,通常需要外部上拉电阻,将输出引脚连接到电源电压。

  3. 适用于多设备共享总线:开漏输出模式常用于多个设备共享总线的情况,其中多个设备可以通过总线上的一个引脚来控制总线上的信号。这种模式允许多个设备共享总线,通过将引脚拉低来控制总线上的信号。

  4. 避免冲突:开漏输出模式有助于避免总线冲突,因为多个设备可以通过将引脚拉低来协同工作,而不会产生电平冲突。

  5. 用于开关和复用引脚:开漏输出模式通常用于开关或多功能引脚,其中引脚可以在不同的时间点扮演输入或输出角色。

  6. 需要外部上拉电阻:要使信号变为高电平,通常需要在输出引脚和电源电压之间连接一个外部上拉电阻。这个上拉电阻会将信号拉高,使其处于高电平状态。

  • 开漏输出模式在许多数字电子设备中有广泛应用,特别是在需要多设备共享总线的情况下。这种模式提供了灵活性,允许多个设备在不引起电平冲突的情况下协同工作。

高阻输入模式

"高阻输入模式" 是数字电子设备中用于配置输入引脚的一种工作模式。在高阻输入模式下,输入引脚具有非常高的电阻,以阻止电流流过引脚,从而使引脚处于一种电气上的开放状态。这意味着输入引脚对外部信号的干扰非常敏感,因为它们不会主动拉高或拉低电平,而是根据外部信号的状态响应。

  1. 高输入电阻:在高阻输入模式下,输入引脚通常具有非常高的电阻值,通常以兆欧姆(MΩ)计。这样的高电阻值阻止了电流的流动,使输入引脚对外部电路的电气影响最小。

  2. 电平不确定:由于高阻输入模式下的输入引脚对外部电平不主动进行拉高或拉低操作,因此它们通常处于一种未确定的状态。它们的电平状态可能会受到外部干扰的影响。

  3. 用于多设备共享总线:高阻输入模式通常用于多设备共享总线的情况,其中多个设备可以通过同一总线上的一个引脚来控制总线上的信号。通过使输入引脚处于高阻状态,可以避免电平冲突。

  4. 需要外部上下拉电阻:要在高阻输入模式下确保输入引脚的电平状态,通常需要外部上拉或下拉电阻。这些电阻可以用于将输入引脚拉高到高电平或拉低到低电平,以防止引脚的状态不确定。

  • 高阻输入模式允许多个设备在同一引脚上共享信号,同时减小对电路的电气干扰。然而,它需要额外的电路来确保引脚的电平状态。在需要共享信号的多设备系统中,高阻输入模式是一种常见的配置,以确保设备之间的协同工作。
采用不同输出模式的原因 
  1. 适应外部设备:不同的外部设备可能需要不同类型的电气接口。例如,一些设备可能需要推挽输出,而另一些可能需要开漏输出。通过设置引脚的输出模式,你可以适应连接的外部设备的需求。

  2. 电平适应:一些设备需要不同的电平,例如5V或3.3V。通过设置引脚的输出模式,你可以确保输出电平与目标设备的电源电压兼容,从而防止电平不匹配引发问题。

  3. 功耗控制:不同的输出模式可能具有不同的功耗特性。推挽输出通常需要更多的功耗,而开漏输出通常需要较少的功耗。通过选择适当的输出模式,你可以在需要时降低功耗,延长电池寿命或降低系统功耗。

  4. 信号调整和驱动能力:不同的输出模式可以提供不同的信号驱动能力。推挽输出通常提供更大的驱动能力,适用于驱动具有较大负载的设备。开漏输出通常适用于需要多个设备连接到同一线的情况。

  5. 电路保护:开漏输出模式通常用于实现外部电路的过电流保护。如果输出线路中的某些设备导致过载,开漏输出可以使引脚变为高阻态,从而保护电路免受损坏。

  6. 总线通信:一些通信协议,如 I2C 和 SPI,要求使用开漏输出模式,以实现共享总线和允许多个设备在同一总线上通信。

  7. 外部中断:开漏输出模式也用于配置外部中断引脚,以侦听外部事件并触发中断服务例程。

  8. 多功能引脚:一些引脚可能具有多种功能,如模拟输入、数字输入、数字输出等。通过设置不同的输出模式,你可以切换引脚的功能,以满足不同的应用需求。

单片机的内部架构

单片机的内部架构可以根据其型号和制造商而有所不同,但通常包括以下基本组件:

  1. 中央处理器(Central Processing Unit, CPU):CPU是单片机的大脑,执行指令并处理数据。它包括运算单元、控制单元和寄存器等。

  2. 存储器(Memory):存储器用于存储程序代码和数据。它通常包括闪存/EPROM(用于存储程序代码)和RAM(用于暂时存储数据)。

  3. 输入/输出端口(I/O Ports):I/O端口用于与外部设备进行通信,包括数字输入、数字输出和模拟输入端口。这些端口允许单片机与按钮、传感器、显示器、电机、通信设备等外部设备交互。

    单片机的I/O口(Input/Output端口)是用于与外部世界进行数据输入和输出的接口。这些I/O口可以配置为数字输入、数字输出或模拟输入,具体功能取决于单片机的类型和性能。下面是关于单片机I/O口的一些基本信息:

    • 数字输入:数字输入口用于接收外部数字信号。例如,按钮开关状态的读取、传感器信号的读取等。

    • 数字输出:数字输出口用于向外部设备发送数字信号。例如,驱动LED、继电器、数码管、蜂鸣器等。

    • 模拟输入:某些单片机具有模拟输入口,可以读取模拟信号(如电压)。这对于测量温度、光线强度等模拟信号非常有用。

    • 端口数量:单片机的I/O口数量取决于其型号和封装。一些单片机具有较少的I/O口,而一些更高性能的单片机则具有大量的I/O口。

    • 配置:I/O口可以配置为输入或输出,通常通过软件进行配置。配置时,你可以选择使其成为输入以接收信号,或者输出以发送信号。

    • 电平:数字I/O口通常可以配置为高电平或低电平,用于驱动外部设备。

    • 中断:许多单片机支持中断,允许在I/O口状态发生变化时立即执行中断服务程序。

    • 上下拉电阻:一些I/O口支持内部上下拉电阻,用于提供稳定的输入状态。

    • 速度:I/O口的切换速度(转换为输出或输入状态的速度)通常是一个关键参数。

  4. 时钟电路(Clock Circuit):时钟电路产生用于同步单片机操作的时钟信号。时钟频率可以影响单片机的运行速度。

  5. 中断控制器(Interrupt Controller):中断控制器负责处理来自外部或内部事件的中断请求。它允许单片机在需要时立即响应特定事件。

  6. 定时器和计数器(Timers and Counters):定时器和计数器用于生成定时延迟、频率计算和时间测量。它们可以用于各种应用,如脉冲宽度调制(PWM)控制、定时任务等。

  7. 串口通信接口(Serial Communication Interface):串口通信接口允许单片机与其他设备进行串行通信,如UART、SPI、I2C等。

  8. 模拟数字转换器(Analog-to-Digital Converter, ADC):ADC用于将模拟信号转换为数字信号,允许单片机测量模拟传感器的值。

  9. 复位电路(Reset Circuit):复位电路用于将单片机恢复到其初始状态,通常在上电时或发生异常情况下触发。

  10. 功率管理单元(Power Management Unit):功率管理单元用于监测和管理单片机的电源供应,以确保正常运行。

  11. 闪存/EPROM编程接口(Programming Interface):这些接口用于编程或重新编程单片机的闪存/EPROM,以加载新的程序代码。

  12. 内部总线(Internal Bus):内部总线用于在各个组件之间传递数据和指令。

单片机可控制的外设

  • 单片机可以控制各种外部设备,以执行不同的任务和应用。下面是一些常见的外部设备和组件,可以通过单片机进行控制:

  • LEDs(Light Emitting Diodes):LED是最常见的输出设备,用于指示状态、显示信息或作为指示灯。

  • LCDs(Liquid Crystal Displays):LCD显示屏用于显示文本、数字、图像等信息。

  • 数码管(Seven-Segment Displays):数码管通常用于显示数字,例如时钟、计数器等。

  • 蜂鸣器(Buzzer):蜂鸣器用于产生声音或警告信号。

  • 电机和马达(Motors):单片机可以控制各种电机,包括直流电机、步进电机和伺服电机,用于控制机器人、车辆、舵机等。

  • 传感器(Sensors):各种传感器用于检测温度、湿度、光线、压力、声音、运动、距离等,以及用于测量加速度、陀螺仪、磁场等。

  • 通信设备(Communication Devices):单片机可以通过串口、SPI、I2C等通信接口与其他设备通信,如WiFi模块、蓝牙模块、RFID读卡器等。

  • 存储设备(Storage Devices):单片机可以与各种存储设备交互,包括闪存、SD卡、EEPROM等,以读写数据。

  • 继电器(Relays):继电器用于控制高功率电路,例如家用电器、电机、灯光等。

  • 键盘和按键开关(Keypads and Push Buttons):单片机可以用于检测按键事件,例如键盘输入、按钮点击等。

  • 计时器和计数器(Timers and Counters):单片机内部的计时器和计数器可以用于精确计时和频率计算。

  • 执行器(Actuators):执行器用于控制机械部件,如阀门、门、舵机、气缸等。

  • 摄像头和图像传感器(Cameras and Image Sensors):用于拍摄图像、视频、视觉识别等。

  • GPS(Global Positioning System)模块:用于获取位置信息。

单片机中引脚常见的简称对应的用途

单片机中的引脚常见的简称和用途可以根据具体的单片机型号而有所不同,但以下是一些常见的引脚简称和用途:VCC:电源供应,通常连接到正电源。

GND:接地,通常连接到负电源或地。

GPIO:通用输入/输出引脚,用于连接外部设备或传感器。

RST:复位引脚,用于重启单片机。

CLK:时钟引脚,通常用于时序和同步操作。

  • 时钟引脚(通常简写为CLK)是许多集成电路(包括微控制器、微处理器、FPGA等)上的一种特殊引脚,用于提供时钟信号,以同步和协调电路的操作。时钟信号是电子系统中的基础,它确定了电路的操作速度和时序。以下是时钟引脚的工作原理:

  • 时钟信号生成:时钟信号通常是由一个时钟发生器电路产生的。这个电路可以是一个独立的晶振、晶体振荡器或由芯片内部的振荡电路产生。时钟发生器产生固定频率的方波信号,通常以高电平和低电平交替的方式。

  • 时钟频率:时钟频率是指时钟信号的周期,通常以赫兹(Hz)为单位表示。时钟频率决定了电路的操作速度。较高的时钟频率意味着电路可以以更高的速度执行操作,但也可能增加功耗和电磁干扰。

  • 同步:时钟信号在整个电路中传播,各个部分按照时钟的上升沿或下降沿进行操作。这种同步操作确保了电路中的各个元件在精确的时间点执行任务,从而防止数据错误和时序问题。

  • 时序控制:时钟信号还用于控制电路中不同元件的操作顺序。在时钟上升沿或下降沿,电路中的元件可以执行各自的操作,例如从寄存器读取数据、执行算术运算、存储数据等。

  • 稳定性:时钟信号的稳定性对于电子系统至关重要。不稳定的时钟信号可能导致数据错位和电路不正常工作。因此,时钟信号通常需要经过精密的设计和调谐以确保稳定性。

  • 频率调节:有时,电路需要调整时钟频率以适应不同的操作模式或要求。这可以通过调整时钟发生器的频率来实现,从而使电路在不同情况下能够以不同的速度运行。

实例
#include <STC89.H>

#define BUZZER_PIN P2_3

void delay_ms(unsigned int ms) {
    unsigned int i, j;
    for (i = 0; i < ms; i++) {
        for (j = 0; j < 110; j++);
    }
}

void buzzer_on() {
    BUZZER_PIN = 0; // 使引脚输出低电平
}

void buzzer_off() {
    BUZZER_PIN = 1; // 使引脚输出高电平
}

void play_tone(unsigned int frequency, unsigned int duration) {
    unsigned int i, cycles;
    cycles = 1000000 / (4 * frequency); // 计算周期数

    for (i = 0; i < duration; i++) {
        buzzer_on();
        delay_ms(cycles / 2);
        buzzer_off();
        delay_ms(cycles / 2);
    }
}

int main() {
    while (1) {
        // 播放不同频率的声音
        play_tone(1000, 500); // 频率为1000 Hz,持续时间为500ms
        delay_ms(1000); // 停顿1秒
        play_tone(2000, 300); // 频率为2000 Hz,持续时间为300ms
        delay_ms(1000); // 停顿1秒
        play_tone(3000, 200); // 频率为3000 Hz,持续时间为200ms
        delay_ms(1000); // 停顿1秒
    }
}

TX/RX:串口通信引脚,用于串行数据传输。

#include <avr/io.h>
#include <util/delay.h>

// 初始化串行通信参数
void initUART() {
    // 设置波特率(波特率寄存器的配置)
    UBRRH = (unsigned char)(baud>>8);
    UBRRL = (unsigned char)baud;

    // 启用发送和接收
    UCSRB = (1<<RXEN)|(1<<TXEN);

    // 设置数据格式:8位数据,无奇偶校验,1位停止位
    UCSRC = (1<<URSEL)|(3<<UCSZ0);
}

// 发送一个字符
void sendChar(unsigned char data) {
    // 等待发送缓冲区为空
    while (!(UCSRA & (1<<UDRE)));
    // 将数据加载到发送缓冲区
    UDR = data;
}

// 接收一个字符
unsigned char receiveChar() {
    // 等待接收缓冲区有数据
    while (!(UCSRA & (1<<RXC)));
    // 返回接收到的数据
    return UDR;
}

int main() {
    // 初始化串行通信
    initUART();
    
    while (1) {
        // 发送字符 'A'
        sendChar('A');
        
        // 接收字符并回显
        unsigned char received = receiveChar();
        sendChar(received);
        
        // 延迟一段时间
        _delay_ms(1000);
    }
    
    return 0;
}

TXD (Transmit Data)

这是发送数据的引脚。当设备需要将数据发送到另一个设备时,它会使用 TXD 引脚将数据传输到接收方。TXD 引脚通常与数据传输的起点相关。

RXD (Receive Data):这是接收数据的引脚。当设备需要接收从另一个设备传来的数据时,它会使用 RXD 引脚来接收数据。RXD 引脚通常与数据传输的终点相关。

A0, A1, A2, 等:模拟输入引脚,用于连接模拟传感器或测量模拟信号。

INT:中断引脚,用于外部中断触发。

  • INT(中断)引脚是微控制器(MCU)或其他集成电路上的一种特殊引脚,它允许外部设备或事件通知芯片发生了特定的中断条件,以便在主程序执行的过程中执行一个特定的中断服务程序。中断引脚的工作原理如下:

  • 中断源:外部设备或事件(例如按钮按下、传感器触发、通信数据到达等)产生一个中断条件。这个中断条件可能是由硬件或软件生成的,通常是由外部世界的某种事件触发的。

  • 中断控制器:微控制器或集成电路内部有一个中断控制器模块,它监视中断引脚的状态。中断控制器可以配置为响应不同的中断源,并为每个中断源分配一个优先级。

  • 中断触发条件:当中断源触发了中断条件时,它会改变INT引脚的电平状态,通常是从低电平到高电平(上升沿中断)或从高电平到低电平(下降沿中断)。

  • MCU响应:当INT引脚的电平状态发生变化时,微控制器的中断控制器检测到这一变化,并立即挂起正在执行的主程序。然后,控制器将跳转到预定义的中断服务程序(也称为中断处理程序)的入口点,开始执行中断服务程序。

  • 中断服务程序:中断服务程序是一个特殊的程序,用于处理特定的中断条件。它通常用于采取适当的措施,以响应中断源的事件,例如处理输入、保存当前状态、执行特定操作,然后返回主程序的执行。

  • 中断结束:一旦中断服务程序执行完毕,控制器将返回到主程序的执行点,恢复正常操作。这使得MCU能够响应外部事件,而无需不断地轮询事件状态,从而提高了系统的效率和响应速度。

实例
#include <avr/io.h>
#include <avr/interrupt.h>

// 定义一个全局变量用于在中断处理程序和主程序之间共享数据
volatile int count = 0;

// 中断服务程序
ISR(INT0_vect) {
    // 中断服务程序中的代码
    count++;
}

int main() {
    // 初始化引脚和中断配置
    DDRD &= ~(1 << DDD2);  // 将PD2引脚设为输入
    PORTD |= (1 << PORTD2);  // 启用PD2引脚的上拉电阻

    // 配置外部中断
    EIMSK |= (1 << INT0);  // 启用INT0中断
    EICRA |= (1 << ISC00);  // 设置INT0中断触发条件为上升沿

    // 启用全局中断
    sei();

    while (1) {
        // 主程序中的代码
    }

    return 0;
}

PWM:脉冲宽度调制引脚,用于产生PWM信号。

  • PWM(Pulse Width Modulation,脉冲宽度调制)是一种用于产生模拟信号的数字控制技术,通常用于控制电机速度、亮度调节、音频放大器的音量控制等应用。PWM引脚是一种特殊的引脚,用于生成PWM信号。下面是PWM引脚的工作原理:

  • 基本原理:PWM引脚工作的基本思想是通过调整脉冲信号的占空比来模拟出不同的电压或功率输出。占空比是脉冲高电平(ON时间)占整个脉冲周期(周期时间)的比例,通常以百分比表示。

  • 生成PWM信号:微控制器或其他数字电路中的PWM引脚产生一系列脉冲信号。这些信号通常由一个计数器或定时器控制,并且可以根据需要调整频率和占空比。PWM信号的频率决定了脉冲的周期,而占空比决定了高电平和低电平之间的时间比例。

  • 占空比调节:占空比是PWM信号的一个关键参数,它控制了输出的效果。较高的占空比意味着更多的高电平时间,较低的占空比意味着更多的低电平时间。通过调整占空比,可以实现不同的输出效果。

  • 应用:PWM信号可以应用于各种领域,例如控制电机的速度,通过调整电机的占空比来改变转速;控制LED的亮度,通过调整占空比来改变LED的亮度;音频放大器的音量控制,通过调整占空比来控制输出音量等。

  • 滤波:在某些应用中,PWM信号可能需要通过低通滤波器进行平滑处理,以获得模拟输出。滤波器将平均PWM信号的占空比,从而获得平均电压或功率输出。

  • PWM1N" 表示该引脚用于 PWM1(PWM通道1)的负极性输出。在 PWM 中,通常有正极性输出(通常称为PWMx)和负极性输出(通常称为PWMxN),它们可以用来控制各种应用,如电机控制、亮度控制、音频产生等。

  • PWM1N 引脚产生的 PWM 信号的特点通常是,当 PWM 信号的占空比低于 50% 时,PWM1 引脚为高电平,PWM1N 引脚为低电平;当 PWM 信号的占空比高于 50% 时,PWM1 引脚为低电平,PWM1N 引脚为高电平。这种相位反转的特性对于一些应用非常有用。

  • "PWM1P" 表示该引脚用于 PWM1(PWM通道1)的正极性输出。在 PWM 中,通常有正极性输出(通常称为 PWMx)和负极性输出(通常称为 PWMxN),它们可以用来控制各种应用,如电机控制、亮度控制、音频产生等。

  • PWM1P 引脚产生的 PWM 信号通常与 PWM1N 引脚相对应,具有相同的频率,但是相位反转。换句话说,当 PWM 信号的占空比低于 50% 时,PWM1P 引脚为低电平,当 PWM 信号的占空比高于 50% 时,PWM1P 引脚为高电平。

  • "PWMFLT" 在单片机中通常涉及 PWM 模块中的滤波器,用于改善 PWM 输出信号的质量和稳定性。

#include <avr/io.h>
#include <util/delay.h>

// 初始化 PWM 参数
void initPWM() {
    // 配置 PWM 输出引脚为输出
    DDRB |= (1 << DDB1); // 设置PB1引脚为输出

    // 配置 PWM 模式(例如,Fast PWM)
    TCCR1A |= (1 << COM1A1) | (1 << WGM10);

    // 配置 PWM 周期(例如,256 分频)
    TCCR1B |= (1 << CS10);

    // 设置占空比,占空比为50%
    OCR1A = 32767; // 50% 的占空比
}

int main() {
    initPWM(); // 初始化 PWM 参数

    while (1) {
        // 主程序中的代码

        // 延迟一段时间
        _delay_ms(1000);

        // 修改占空比以改变输出电平
        OCR1A = 16384; // 25% 的占空比
        _delay_ms(1000);

        OCR1A = 49152; // 75% 的占空比
        _delay_ms(1000);
    }

    return 0;
}

计时器:在单片机中,计时器(Timer)是一种重要的硬件模块,用于生成精确的时间延时、执行定时操作、测量时间间隔和生成定时中断。计时器通常包括一个计数寄存器和一些控制寄存器,可以配置和控制计时器的行为。以下是计时器在单片机中的一些常见用途和特征:

  1. 延时生成:计时器可用于生成精确的时间延时,用于控制执行特定任务的时间。

  2. 定时中断:计时器可以配置为在特定时间间隔内触发中断。这对于执行周期性任务、时间测量和事件定时非常有用。

  3. PWM 生成:许多单片机的计时器可用于生成脉冲宽度调制(PWM)信号,用于控制电机速度、LED亮度、音频输出等。

  4. 时间间隔测量:计时器可以用于测量两个事件之间的时间间隔,例如,测量脉冲的宽度或计算两个事件之间的时间差。

  5. 计数功能:计时器可以用作通用计数器,用于计数外部事件、脉冲或其他信号的频率。

  6. 输入捕获:一些计时器具有输入捕获功能,用于捕获外部事件的时间戳,例如,捕获脉冲的上升沿或下降沿的时间。

  7. 输出比较:计时器可以用于与某个预设值进行比较,并根据比较结果触发操作,例如,当计数器值等于某个特定值时触发中断或产生输出。

  8. 计时器分频器:许多计时器具有分频器,允许将主时钟源分频,以减小计时器的时钟频率,从而降低功耗或调整定时精度。

SCL/SDA:I2C通信引脚,用于双线串行通信。

MISO/MOSI:SPI通信引脚,用于串行外设接口通信。

SCK:SPI时钟引脚,用于同步SPI通信。

CS/SS:SPI片选引脚,用于选择特定SPI设备。

AREF:模拟参考电压,用于设定模拟输入的参考电压。

ADx:模拟输入引脚,用于连接模拟传感器或测量模拟信号。

VBAT:备用电池供电引脚,用于备用电池的连接。

XIN/XOUT:外部时钟输入/输出引脚,用于外部时钟源。

GCLK:通用时钟引脚,用于生成内部时钟信号。

ALE:地址锁存使能引脚,用于地址锁存的控制。

PSEN:程序存储器使能引脚,用于控制程序存储器的访问。

ADC_AGnd:是一个标识,通常用于表示模拟-数字转换器(ADC)电路中的模拟地(Analog Ground)。ADC_AGnd 是模拟电路中的一个重要参考点,通常是一个连接到ADC电路中的地线或地端。

  • ADC_AGnd通常与数字地(Digital Ground)分开,数字地是用于数字电路部分的地线。这种分离是为了减少模拟和数字电路之间的相互干扰,以确保ADC测量的准确性。模拟地通常更为稳定,受到较少的电磁干扰,而数字地可能会受到来自数字电路的噪声。

MOSI:表示引脚的SPI(串行外设接口)功能中的MOSI(主机输出从机输入)线。SPI用于串行通信,而MOSI通常用于主设备将数据发送到从设备。

SCLK:表示该引脚用于串行时钟(Serial Clock)功能,通常用于同步串行通信,例如SPI(串行外设接口)通信

RXD:可能表示该引脚用于串行数据接收(Receive Data)功能,通常用于与其他设备进行串行通信,例如UART或USART通信。

3V3 和 2V5 :是电路原理中通常用来表示电源电压的标记。它们表示电路中的两个不同电压供应点的电压值。

  • "3V3" 表示电路中的一个电源点,其电压为 3.3伏特(3.3V)。这通常用于供应数字逻辑电路、微控制器、传感器和其他数字电子元件。

  • "2V5" 表示电路中的另一个电源点,其电压为 2.5伏特(2.5V)。这个电压通常用于供应某些模拟电路、低功耗电子设备或一些特定的电子元件。

CMP:

"CMP+" 和 "CMP-" 通常表示比较器(Comparator)的正极性和负极性输入引脚。比较器是一种用于比较两个电压信号并输出相应结果的电子元件或电路。它通常用于检测两个信号的大小关系,以进行决策或控制其他电子设备的行为。

在单片机或嵌入式系统中,通常会有一个比较器模块,该模块可以具有一个或多个比较器。每个比较器通常有一个正极性(+)输入和一个负极性(-)输入,分别对应于 "CMP+" 和 "CMP-"。

  • "CMP+" 是比较器的正极性输入,接收一个参考电压或信号,用于与另一个输入信号进行比较。

  • "CMP-" 是比较器的负极性输入,接收待比较的输入信号。

比较器的工作原理是:当 "CMP+" 输入电压大于 "CMP-" 输入电压时,比较器输出一个高电平信号;当 "CMP+" 输入电压小于 "CMP-" 输入电压时,比较器输出一个低电平信号。这种比较结果可以用于触发中断、控制开关、检测电压阈值等应用。

MCU

"MCU" 是 "Microcontroller Unit" 的缩写,意为"微控制器单元"。在单片机(Microcontroller)领域,"MCU" 通常指的是一种嵌入式系统,它集成了处理器核、内存、输入/输出引脚、外设和控制逻辑,用于执行特定的计算和控制任务。

具体来说,MCU 包含以下关键组件:

  1. 处理器核(CPU):用于执行程序和处理数据的中央处理单元。

  2. 内存:包括闪存(Flash)或可编程只读存储器(PROM)、随机访问存储器(RAM)等,用于存储程序代码和数据。

  3. 输入/输出引脚:用于连接外部设备、传感器和执行器的引脚,通过这些引脚进行数据输入和输出。

  4. 外设:包括通信接口(如 UART、SPI、I2C)、定时器、PWM 控制器、模拟数字转换器(ADC)等,用于执行各种任务,如通信、定时、PWM 控制、传感器读取等。

  5. 控制逻辑:用于管理和协调 MCU 内部的各个组件,确保它们协同工作。

MCU 的主要应用领域包括嵌入式系统、自动化控制、传感器接口、物联网设备、电子嵌入式产品等。MCUs 通常设计用于执行特定任务,因此可以根据应用需求进行定制,以提供特定的功能和性能。

MCU 通常运行嵌入式软件,该软件被存储在闪存中,并通过 CPU 执行。开发人员使用编程语言如 C/C++ 来编写嵌入式软件,以实现 MCU 的控制和计算功能。

ADC

ADC 是 "Analog-to-Digital Converter" 的缩写,意为 "模数转换器"。在单片机领域,"ADC" 通常指的是一种电子模块或外设,用于将模拟信号(如电压或电流)转换为数字信号,以便单片机能够处理和分析这些信号。

单片机通常是数字设备,能够处理数字信号,但现实世界中的许多传感器和信号源产生的信号是模拟的。因此,ADC 的作用是将这些模拟信号转换为数字形式,以便单片机能够读取、处理和分析它们。

ADC 通常包括以下关键特性:

  1. 输入通道:ADC 可以有一个或多个输入通道,允许连接到不同的模拟信号源,例如温度传感器、光敏电阻、压力传感器等。

  2. 分辨率:分辨率表示 ADC 的精度,通常以位数(比特数)表示。较高的分辨率允许 ADC 更精确地测量模拟信号。

  3. 采样速率:采样速率表示 ADC 每秒可以进行多少次模拟信号采样和转换为数字信号。

  4. 参考电压:ADC 需要一个参考电压,用于确定模拟信号的幅值。通常,这是已知的固定电压值,ADC 使用它来进行转换。

  5. 触发模式:ADC 可以根据不同的触发条件进行转换,包括定时触发、外部触发和软件触发。

AD3/ADC11:这部分表示引脚的模拟输入功能。它可以用于连接模拟传感器或其他模拟信号源。 "AD3" 和 "ADC11" 是对同一引脚的不同命名方式,一个用于引脚的物理编号,另一个用于 ADC(模数转换器)通道的编号。

寄存器

在计算机科学和数字电子领域,寄存器(Register)是一种用于存储和处理数据的小型临时存储器,通常位于中央处理器(CPU)或其他数字电子设备内部。寄存器通常是非常快速的,其作用是存储临时数据、中间计算结果以及指令执行过程中需要的数据。

以下是寄存器的一些重要特性和作用:

  1. 数据存储:寄存器用于存储二进制数据,可以是整数、浮点数、地址等。

  2. 临时存储:寄存器通常用于临时存储数据,以供计算和操作。计算机程序中的许多运算和逻辑操作都依赖于寄存器中的数据。

  3. 数据传输:寄存器用于在不同的处理器功能单元之间传输数据。数据可以从内存传输到寄存器,从寄存器传输到算术逻辑单元(ALU)等。

  4. 地址计算:寄存器中的地址寄存器用于存储内存地址,用于访问存储器中的数据。

  5. 指令操作:一些寄存器用于存储和操作指令,例如指令寄存器(IR),它存储当前执行的机器指令。

  6. 条件和状态标志:一些特殊寄存器用于存储条件标志,例如零标志、进位标志、溢出标志等,这些标志用于控制程序流程和处理器状态。

  7. 快速存储器访问:寄存器通常是处理器中访问速度最快的存储器,因此用于存储频繁访问的数据,以提高执行速度。

  8. 寄存器文件:一组不同类型的寄存器通常被组织成寄存器文件,其中包括通用寄存器、特殊用途寄存器和条件码寄存器等。

上拉电阻

  • 定义:上拉电阻是连接到电路引脚的电阻,将该引脚的电平拉高(通常是连接到电源电压,例如 Vcc 或 3.3V)。这意味着当没有外部输入时,该引脚处于高电平状态。

  • 作用:上拉电阻用于确保一个引脚在没有外部输入时保持高电平。这对于输入引脚用于检测开关状态、按键输入或传感器触发等场景非常有用。当没有外部电平控制时,引脚会保持高电平,以防止未知状态。

下拉电阻

  • 定义:下拉电阻是连接到电路引脚的电阻,将该引脚的电平拉低(通常是连接到地或 GND)。这意味着当没有外部输入时,该引脚处于低电平状态。

  • 作用:下拉电阻用于确保一个引脚在没有外部输入时保持低电平。这对于输入引脚用于检测开关状态、按键输入或传感器触发等场景非常有用。当没有外部电平控制时,引脚会保持低电平,以防止未知状态。

  • 当IC的I/O端口,节点为高电平时,节点处和GND之间的阻抗很大,可以理解为无穷大,这个时候通过上拉电阻(如4.7K欧,10K欧电阻)接到VCC上,上拉电阻的分压几乎可以忽略不计;当I/O端口节点需要为低电平时,直接接GND就可以了,这个时候VCC与GND是通过刚才的上拉电阻(如4.7K欧,10K欧电阻)连接的,通过的电流很小,可以忽略不计。

  • 电平值的大小、高低是相对于地电平来说的,因此在看电平值的大小时要参考地的电平值来看。看看那些引脚是否接到地上,与自己是否连接外围器件没有关系,因为其实高电平还是低电平是相对于地平面来说的。

  • 在节点与+5V之间接10K欧或4.7K欧的上拉电阻,能够把这个节点的电位拉上来,往往这个节点要求应用单片机或其它控制器来控制它(及这个节点与I/O连接)为高电平或低电平。

  • 如果单纯的想要使这个节点成为高电平,并且输出阻抗非常大,则直接接电源也无妨,但是如果单片机要使这个节点拉低,即单片机内部使节点接地,这样5V电源和地之间就短路了。

  • 另外,当要求这个节点为高电平时,这个节点和地之间的阻抗一般非常大,如100K欧的阻抗,当上拉一个10K欧的电阻,这个点分得的电压为100K欧/(100K+10K)*5V=4.5V,这样也可以拉到高电平。而当要求这个节点为低电平时,只要把它和地连接就可以了,电源和地之间有一个10K偶的电阻,这样就不会短路了。

  • 当低电平时,电源和地之间有一个负载形成的回路,有时候这个节点会再串接一个电阻,因为电流流向阻抗低的地方,所以电流会通过与电源相连的电阻流向地,而不是流向这个与节点相连的电阻,因为这个节点连接的电阻阻抗高,所以低电平时这个点的电势就是低电平。

  • 可以这么认为,对于IC的I/O端口来说,IC内部通过控制高低电平相当于控制这个O/O口与其内部的GND或非常大的电阻相连,如100K欧。当I/O口为低电平0V时,在IC内部,是控制IC芯片O/O口的引脚在芯片内与GND连接;当I/O口为高电平时,如5V。

  • 这个时候I/O口引脚在芯片内是与非常大的电阻,如100K欧相连接的,有时在I/O节点处会再串接一个小电阻值的电阻,如68欧。因为电流流向阻抗低的地方,所以当芯片内部的I/O端口欧与GND相连为低电平时,电源与上拉电阻及芯片内部的GND形成环路进行流通。这时I/O口节点处的电流就会流向芯片内部的GND,因为节点处串接了一个小阻值的电阻。相对于GND来说是高阻,就是大一点点也是高阻,所以电流就不会流过这个串联的电阻。

  • 当用下拉电阻时(所谓的上拉和下拉都是针对高阻态而言的),当I/O口为高阻态时,通过上拉电阻能够让其保持在高电平状态;具体如上文所述:当I/O端口为高阻态时,用下拉电阻把这个口与GND相连接,高阻态电阻值很大,可以理解为断开。实就是和芯片内部的阻值很大的电阻相连接,下拉的时候拉到地上了,没有电流,电平值为0,除非是给这个引脚赋予一个高电平值它才能够起作用。

上拉和下拉电阻的作用概括

1、提高电压准位

当TTL电路驱动CMOS电路时,如果TTL电路输出的高电平低于CMOS电路的最低高电平,这时就需要在TTL的输出端接上拉电阻,以提高输出高电平的值;OC门电路必须加上拉电阻,以提高输出的高电平值。

2、加大输出引脚的驱动能力

有的单片机引脚上也常使用上拉电阻。

3、N/A引脚(没有连接的引脚)防静电、防干扰

在CMOS芯片上,为了防止静电造成损坏,不用的引脚不能悬空,一般接上拉电阻降低输入阻抗,提供泄荷通路。同时引脚悬空就比较容易接收外界的电磁干扰。

4、电阻匹配

抑制反射波干扰,长线传输中电阻不匹配容易引起反射波干扰,加上下拉电阻使电阻匹配,能有效的抑制反射波干扰。

5、预设空间状态/默认电位

在一些CMOS输入端接上拉或下拉电阻是为了预设默认电位。当不用这些引脚时,这些输入端下拉接低电平或上拉接高电平。在I2C等总线上空闲时的状态是由上下拉电阻获得的。

6、提高芯片输入信号的噪声容限

输入端如果是高阻状态,或高阻抗输入端处于悬空状态,此时需要加上拉或下拉电阻,以免受到随机电平的影响,进而影响电路工作。同样,如果输出端处于被动状态,需要加上拉或下拉电阻,如输出端仅仅是一个三极管的集电极,从而提高芯片输入信号的噪声容限,增强抗干扰能力。

在BJT晶体三极管的基极端,上拉电阻和下拉电阻也起着至关重要的作用。在三极管的电路应用中,串接在基极上的电阻起限制基级电流的作用,如下图中的R2所示,

如下图中的R5所示,上拉电阻使三极管基极的输入电平在默认情况下是高电平输入,当CPU有低电平信号输出时,外围电路响应,下拉电阻使晶体管的基极输入在默认情况下拉到低电平,如下图中的R6所示。

 时序:

单片机的时钟电路,单片机需要统一的时钟来进行时钟控制,它才能够有条不紊的工作,单片机的工作过程是cpu取一条指令进行译码,译码之后进行微操作,然后再取一条指令译码进行微操作,它就是这样一步一步地自动地由这个微操作,依照持续来完成相应规定的一个功能,这种微操作在时间上是有一个严格的次序的,这种微操作的时间次序我们就叫做时序。

单片机电路详解

常见元器件简称:

R:Resistor,电阻。

C:Capacitor,电容。

L:Inductor,电感。之所以不用I,是因为I和1太像了,容易搞混淆,就用电感的单位Lenz的L了。

D:Dideo,二极管。除了用的最多的肖特基二极管,其他的像LED灯、TVS管、稳压管这样的二极管类的元器件,通常也缩写为D。

B:Bead,磁珠。

T:Transistor,晶体管。包括三极管和MOS管。

U:Unit,芯片。芯片本身是Chip、IC之类的名字,用I不行,用C也和电容冲突了,干脆就用CPU的U吧。不过除了CPU,其他大大小小的芯片,在原理图也都用U带代替。

X:Crystal,晶体。晶体和晶振都用X。

J:Jack,连接器。Jack的原意是插座,用多了就把所有的连接器类的都缩写为J。例如Zif连接器、板对板连接器、插针、卡座、旋钮、按键等。

TP:Test Point,测试点。

原理图符号:
(以下来源嵌入式开发学习之硬件基础:学会看懂原理图符号_嵌入式原理图怎么看_一杯烟火的博客-CSDN博客

以下是各种组件的一些标准化基本原理图符:
电阻器
原理图上的电阻器通常由几条锯齿线表示,两个端子向外延伸。使用国际符号的原理图可以改为使用无特征的矩形,而不是曲线。


电位计和可变电阻器
可变电阻器和电位器各自用箭头朝向电阻器。可变电阻器仍然是一个双端子器件,因此箭头恰好位于中间对角线。电位计是三端子设备,因此箭头成为第三个端子。

 

电容器
有两种常用的电容符号。一个符号表示极化(通常是电解或钽)电容器(有机型,分正负极),另一个符号表示非极化电容器。通常,有两个端子,没有正负极。


带有一个弯曲板的符号表示电容器是有极性的。弯曲板代表电容器的阴极,其电压应低于正极引脚,加号也可以添加到极化电容符号的正极引脚。

电感器
电感器通常由一系列弯曲凸起或环形线圈表示。国际符号可以仅将电感器定义为填充矩形。

 

开关
开关以许多不同的形式存在。最基本的开关,单刀单掷(SPST),是两个端子,半连接线代表执行器(将端子连接在一起的部分)。


具有多个投掷的开关,如下面的SPDT和SP3T,为执行器增加了更多的可接触点。


电源
正如有很多选项可以为您的项目供电,有各种各样的电源电路符号可以帮助指定电源。

直流或交流电压源
大多数情况下,使用电子设备时,您将使用恒定电压源。我们可以使用这两个符号中的任何一个来定义源是提供直流电(DC)还是提供交流电(AC):


电池
电池,无论是圆柱形,碱性AA还是可充电锂聚合物,通常看起来像一对不成比例的平行线:

更多线对通常表示电池中有更多串联电池。此外,较长的线通常用于表示正端子,而较短的线连接到负端子。

电压节点
有时-特别是在复杂的原理图上-您可以为节点电压分配特殊符号。您可以将器件连接到这些单端符号,它将直接连接到5V,3.3V,VCC或GND(地)。正电压节点通常用向上的箭头表示,而接地节点通常包括一到三条扁平线(或者有时是一个向下的箭头或三角形)。

二极管

基本二极管通常用压在一条线上的三角形表示。二极管也是极化的,因此两个终端中的每一个都需要区分标识符。正极,阳极是进入三角形平坦边缘的终端。负极,阴极延伸出符号中的线(将其视为符号)。

有各种不同类型的二极管,每个二极管在标准二极管符号上都有特殊的 riff。发光二极管(LED)通过指向远处的几条线来增强二极管符号。从光产生能量的光电二极管(基本上是微小的太阳能电池),将箭头翻转并指向二极管。

其他特殊类型的二极管,如肖特基或齐纳二极管,都有自己的符号,符号的条形部分略有不同。

晶体管

晶体管,无论是 BJT 还是 MOSFET,都可以以两种配置存在:正掺杂或负掺杂。因此,对于这些类型的晶体管中的每一种,至少有两种方法来绘制它。

双极结晶体管(BJT)

BJT 是三端设备;它们有一个集电极(C),发射极(E)和一个基极(B)。有两种类型的 BJT 分别是 NPN 和 PNP,它们每种都有自己独特的符号。

集电极(C)和发射极(E)引脚彼此成直线,但发射极应始终有一个箭头。如果箭头指向内部,则为 PNP,如果箭头指向外,则为 NPN。或者看箭头,总是 P 指向 N 的

金属氧化物场效应晶体管(MOSFET)

与 BJT 一样,MOSFET 有三个端子,但这次它们被命名为源极(S),漏极(D)和栅极(G)。同样,该符号有两种不同的版本,具体取决于您是否有 N 沟道或 P 沟道 MOSFET。每种 MOSFET 类型都有许多常用符号:

符号中间的箭头定义 MOSFET 是 N 沟道还是 P 沟道。如果箭头指向意味着它是一个 n 沟道 MOSFET,如果它指出它是一个 p 沟道。

数字逻辑门

我们的标准逻辑功能 AND,OR,NOT 和 XOR - 都具有唯一的原理图符号:

 添加泡到输出否定的功能,则创建与非门,NORs 的,和 XNORs:

它们可能有两个以上的输入,但形状应该保持不变(好吧,可能更大),并且应该仍然只有一个输出。

集成电路

集成电路完成了如此独特的任务,而且数量众多,它们并没有真正获得独特的电路符号。通常,集成电路由矩形表示,其中引脚从侧面延伸出来。每个引脚都应标有数字和功能。

ATmega328 微控制器(通常在 Arduinos 上找到),ATSHA204 加密 IC 和 ATtiny45MCU 的原理图符号。如您所见,这些组件的大小和引脚数量差异很大。

由于 IC 具有这样的通用电路符号,因此名称,值和标签变得非常重要。每个 IC 应具有精确识别芯片名称的值。

独特的 IC:运算放大器,稳压器

一些更常见的集成电路确实获得了独特的电路符号。您通常会看到如下所示的运算放大器,总共 5 个端子:非反相输入(+),反相输入(-),输出和两个电源输入。

通常,在一个 IC 封装中内置两个运算放大器,只需要一个引脚用于电源,一个用于接地,这就是为什么右边只有三个引脚。

简单的稳压器通常是三端子元件,带有输入,输出和接地(或调节)引脚。这些通常采用矩形的形状,左侧(输入),右侧(输出)和底部(接地 / 调整)具有引脚。

晶体和谐振器

晶体或谐振器通常是微控制器电路的关键部分。它们有助于提供时钟信号。晶体符号通常有两个端子,而为晶体添加两个电容器的谐振器通常有三个端子。

接头和连接器

无论是提供电源还是发送信息,连接器都是大多数电路的要求。这些符号取决于连接器的外观,下面是一个示例:

电机,变压器,扬声器和继电器

我们将它们混为一谈,因为它们(大多数)都以某种方式使用线圈。变形金刚(不是眼睛以上的类型)通常涉及两个线圈,相互对接,有几条线将它们分开:

继电器通常将线圈与开关配对: 

扬声器和蜂鸣器通常采用与现实生活相似的形式:

 电机通常与终端周围多一点点缀涉及将环绕 “M”,有时是:

保险丝和 PTC

保险丝和 PTC - 通常用于限制大电流的设备 - 每个都有自己独特的符号:

PTC 符号实际上是热敏电阻的通用符号,是一个与温度相关的电阻

毫无疑问,这个列表中有许多电路符号,但上面的那些应该让你在原理图读数中有 90%的识字率。通常,符号应与其建模的现实组件共享相当大的数量。除符号外,原理图上的每个组件都应具有唯一的名称和值,这有助于识别它。

位号和值

值(Value)有助于准确定义组件的内容。对于电阻器,电容器和电感器等原理图,该值告诉我们它们有多少欧姆,法拉或亨利。对于其他组件,如集成电路,该值可能只是芯片的名称。晶体可能将其振荡频率列为其值。

位号(Default)通常是一个或两个字母和一个数字的组合。名称的字母部分表示组件的类型电阻器的 R,电容器的 C,集成电路的 U,等等。示意图上的每个组件名称应该是唯一的;例如,如果电路中有多个电阻,它们应命名为 R1,R2,R3 等。元件名称有助于我们参考原理图中的特定点。

名称的前缀非常标准化。对于某些组件,如电阻器,前缀只是组件的第一个字母。其他名称前缀不是那么直接;例如,电感器是 L(因为电流已经占据了 i)。这是一个常见组件及其名称前缀的快速表:

尽管这些是组件符号的 “标准化” 名称,但它们并未得到普遍遵循。例如,您可能会看到以 IC 为前缀而不是 U 的集成电路,或标有 XTAL 而不是 Y 的晶体。用你最好的判断来诊断哪一部分是哪一部分。符号通常应该传达足够的信息。

原理图阅读技巧

识别模块

真正广泛的原理图应该分成功能块。可能有一个部分用于电源输入和电压调节,或微控制器部分,或专门用于连接器的部分。尝试识别哪个部分是哪个部分,并遵循从输入到输出的电路流程。优秀的原理图工程师甚至可能像电子书一样放置电路,左侧输入,右侧输出。

如果原理图的抽屉非常好(就像为 RedBoard 设计此原理图的工程师),他们可能会将原理图的各个部分分成逻辑的标记块。

识别电压节点

电压节点是单端子原理图组件,我们可以将组件端子连接到它们,以便将它们分配到特定的电压电平。这些是网名的特殊应用,意味着连接到同名电压节点的所有终端都连接在一起。

类似的电压节点 - 如 GND,5V 和 3.3V - 都连接到它们的对应部分,即使它们之间没有电线。

接地电压节点特别有用,因为许多组件需要接地。

常用芯片引脚定义和网络名称的缩写

电源和地

VCC、VDD,电源。C=circuit,D=deivde。实际使用的时候一般不区分VCC和VDD,反正怎么用都是一个意思。

为了准确描述电源是给谁用的、电压有多少,在复杂一些的原理图上会标的更复杂一些。例如VCC_ZIGBEE_3_3V,意思就是给ZIGBEE供电的3.3V的电源。电压有时候会写3_3V,也可以是3V3。

之所以不写3.3,是因为某些画原理图或画PCB图的软件,无法识别到“.”点这个符号。同样的道理,写下划线“_”而不是中划线“-”,也是为了软件兼容性考虑。某些软件不识别中划线“-”。

GND,地。还有DGND,AGND。数字地和模拟地。不用解释了。有时候也叫VSS。

V**,专用的电源网络。例如VPP、VIO、VCORE、VBAT、VCHG等。

VPP=V Programme,编程电压,常用语Sim卡、单片机等需要特殊电压来烧写固件的元器件。

VIO=V IO口,对于内部有多个电压域的芯片,需要对每个电压域做单独的供电。最主要的就是IO口电压。可能芯片本身是3V供电的,但是IO口工作电压是1.8V。电压域的匹配在原理图中非常重要,例如两颗芯片,一个VIO电压是2.8V,一个是1.8V,两颗芯片就不能直接对连起来,需要做电压转换。

VCORE=V Core,系统内核电压。手机和平板类的多核ARM CPU,内核电压一般只有1V左右,需要通过PMU输出Vcore电压给内核供电。同理还有VGPU、VMEM(V memory)等。

VBAT=V Battery,电池电压。电池用的多少伏的,电池电压就是多少伏。

VCHG=V Charge,充电电压。

常见硬件原理图中的“英文缩写”大全详情
来源:
如何快速看懂常见硬件原理图的英文缩写-软件和硬件的区别

硬件原理图的英文缩写-常用控制接口

EN:Enable,使能。使芯片能够工作。要用的时候,就打开EN脚,不用的时候就关闭。有些芯片是高使能,有些是低使能,要看规格书才知道。
CS:Chip Select,片选。芯片的选择。通常用于发数据的时候选择哪个芯片接收。例如一根SPI总线可以挂载多个设备,DDR总线上也会挂载 多颗DDR内存芯片,此时就需要CS来控制把数据发给哪个设备。
RST:Reset,重启。有些时候简称为R或者全称RESET。也有些时候标注RST_N,表示Reset信号是拉低生效。
INT:Interrupt,中断。前面的文章提到过,中断的意思,就是你正睡觉的时候有人把你摇醒了,或者你正看电影的时候女朋友来了个电话。
PD:Power Down,断电。断电不一定非要把芯片的外部供电给断掉,如果芯片自带PD脚,直接拉一下PD脚,也相当于断电了。摄像头上会 用到这根线,因为一般的摄像头有3组供电,要控制三个电源直接断电,不如直接操作PD脚来的简单。(在USB Type-C接口中有一个Power
Delivery也叫PD,跟这个完全不一样,不要看错了。)
CLK:Clock,时钟。时钟线容易干扰别人也容易被别人干扰,Layout的时候需要保护好。对于数字传输总线的时钟,一般都标称为 xxx_xCLK,如SPI_CLK、SDIO_CLK、I2S_MCLK(MainClock)等。对于系统时钟,往往会用标注频率。如SYS_26M、32K等。标了数字而 不标CLK三个字,也是无所谓的,因为只有时钟才会这么标。
CTRL:control,控制。写CONTROL太长了,所以都简写为CTRL,或者有时候用CMD(Command)。
SW:Switch,开关。信号线开关、按键开关等都可以用SW。
PWM:PWM,这个已经很清晰了。
REF:Reference,参考。例如I_REF,V_REF等。参考电流、参考电压。
FB:Feedback。反馈。升压、降压电路上都会有反馈信号,意义和Reference是类似的,芯片根据外部采集来的电压高低,动态调整输出。外 部电压偏低了,就加大输出,外部电压偏高了,就减小输出。
A/D:Analog/Digital,模拟和数字的。如DBB=Digital Baseband,AGNG=Analog Ground。
D/DATA:数据。I2C上叫做SDA(Serial DATA),SPI上叫做SPI_DI、SPI_DO(Data In,Data Out),DDR数据线上叫做D0,D1,D32 等。
A/Address:地址线。用法同数据线。主要用在DDR等地址和数据分开的传输接口上。其他的接口,慢的像I2C、SPI,快的像MIPI、RJ45等, 都是地址和数据放在一组线上传输的,就没有地址线了

 

硬件原理图的英文缩写-常用方向的标识

TX/RX:Transmit,Receive。发送和接收。这个概念用在串口(UART)上是最多的,一根线负责发送,一根线负责接收。这里要特别注意, 一台设备的发送,对应另一台设备就是接收,TX要接到RX上去。如果TX接TX,两个都发送,就收不到数据了。 为了防止出错,可以标注为:UART1_MRST、UART1_MTSR。Master RX Slave TX的意思。Master就是主控芯片,Slave就是从设备。TX、 RX很容易标错的,尤其是原理图有几十页的情况下。
P/N:Positive、Negative。正和负。用于差分信号线。现在除了DDR和SDIO之外,其他很少有并行数据传输接口了。USB、LAN、MIPI的 LCD和Camera、SATA等等,高速数据总线几乎都变成了串行传输数据了。串行信号线速度很高,随便就上GHz,电压很低只有几百毫伏,因 此很容易被干扰,要做成差分信号,即用两根线传一个数据,一个传正的一个传负的。传到另外一边,数据相减,干扰信号被减掉,数据信号 负负得正被加倍。对于RESET_N这样的信号来讲,只起到重点标注的作用,表示这个RESET信号是拉低才生效的。大部分设备都是低有效的 RESET,偶尔会有一些设备拉高RESET。
L/R:Left、Right。通常用于音频线,区分左右。有些时候如喇叭的信号是通过差分来传输的,就是SPK_L_N、SPK_L_P这样的标识。如下 图,某2.1声道智能音箱音频输出(喇叭连接器端)。TAS5751是音频功放,HF是高频High frequency(2.1音响有专门的低频输出)。P和N
用 和-代替。

 

硬件原理图的英文缩写-常用设备缩写

BB:Baseband,基带处理器。十几年前的的手机芯片只有通信功能,没有这么强大的AP(跑系统的CPU),手机里的主芯片都叫做
Baseband基带芯片。后来手机性能强大了,还是有很多老工程师习惯把主芯片叫做BB,而不是叫CPU
P(GPIO):很多小芯片,例如单片机,接口通用化比较高,大部分都是GPIO口,做什么用都行,就不在管脚上标那么清楚了,直接用P1,
P2,P1_3这样的方式来标明。P多少就是第多少个GPIO。P1_3就是第1组的第3个GPIO。(不同组的GPIO可能电压域不一样)

 

BAT:Battery,电池。所有的电池电压都可以叫做VBAT。
CHG:Charge,充电。
CAM:Camera,摄像头。
LCD:显示器。
TP:Touch Panel,触摸屏。(注意不要和Test Point测试点搞混了)
DC:Direct Current,直流电。用在设备上通常用作外部直流输入接口,而不是指供电方式或者供电电压什么的,例如VCC_DC_IN的含义,就 是外部DC接口供电

I2C协议

单片机学习 13-I2C_EEPROM_单片机eeprom-CSDN博客

 【精选】I2C通信原理简介-CSDN博客

51单片机I2C通信原理详解(附代码详解)

  I2C总线的SDA和SCL是双向I/O线,必须通过上拉电阻接到正电源,当总线空闲时,2线都是“高”。所有连接在I2C总线上的器件引脚必须是开漏或集电极开路输出,即具有“线与”功能。

I2C总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。

I2C总线规定,当SCL为高电平时,SDA的电平必须保持稳定不变的状态,只有当SCL处于低电平时,才可以改变SDA的电平值,但起始信号和停止信号是特例。因此,当SCL处于高电平时,SDA的任何跳变都会被识别成为一个起始信号或停止信号。

在I2C总线的数据传输过程中,发送到SDA信号线上的数据以字节为单位,每个字节必须为8位,而且是高位(MSB)在前,低位(LSB)在后,每次发送数据的字节数量不受限制。但在这个数据传输过程中需要着重强调的是,当发送方发送完每一字节后,都必须等待接收方返回一个应答响应信号。

UART

SPI

 SPI(Serial Peripheral Interface,串行外围设备接口),是一种全双工通信协议,同步通信。其基本通信模式为

SPI通信接口为:

SPI_CS_N:片选信号,一般低电平有效。由主机(master)产生,选择与之通信的从机(slaver),低电平表示从机被选中。

SPI_SCLK:时钟信号,由主机产生,用于控制数据传输速率和时机(即:数据采样时间)

SPI_MOSI:SPI接口的TX通道,主机发送给从机的数据(Master Output Slaver Input)

SPI_MISO:SPI接口的RX通道,从机发送给主机的数据(Master Input Slaver Output)

SPI传输特点

1.SPI通信为主从模式。支持一主多从的通信方式,主机通过片选CS信号选中从机。其中需要注意的是:时钟信号CLK只能由主机产生

2.SPI为同步通信协议:SPI在传输数据的同时传输时钟信号。主机根据将要交换的数据产生时钟脉冲信号,时钟信号通过时钟极性(Clock Polarity)和时钟相位(Clock Phase)规定两个SPI设备在何时进行数据交换和数据采样,实现两个SPI设备的同步传输。

3.SPI为全双工通信协议:SPI同时拥有TX(MOSI)数据通道和RX(MISO)数据通道,为全双工通信。

SPI与I2C/UART的对比

1.硬件接口差异

IIC,拥有两根线,sda(双向端口)/scl(最大4Mbits/s).
SPI,拥有4根线,sclk/SDI(串行输入)/SDO(串行输出)/CS(片选:当接多个从设备时,需要用到该信号)
UART,拥有3根线,RX(接受数据)/TX(发送数据)/GND(地线)

2.协议差异

1.IIC和SPI均为先传输MSB,UART为LSB先传输
2.IIC的速度比SPI的速度更慢一些,协议更复杂一些,线也比标准的SPI少。
3.IIC通过地址选择从设备,SPI通过片选信号选择从设备
4.SPI和UART可以实现全双工通信,IIC为半双工通信(IIC只有一根数据线)
5.IIC需要上拉电阻,抗干扰的能力更弱。一般用于同一板卡上芯片之间的通信,较少用于远距离通信
6.UART需要固定的波特率,也就是说两位数据之间的间隔要相等。SPI无所谓,因为自己有时钟
7.UART为异步通信,一帧可以传送5/6/7/8位数据,SPI为同步通信可以一位一位的传送,IIC为同步通信传送8位连续数据

2.SPI传输模式

SPI协议规定SPI有四种传输模式,由CPOL(时钟极性:Clock Polarity)和CPHA(时钟相位:Clock Phase)决定

作用
CPOL决定空闲时SCLK电平
CPOL=0:空闲时SCLK为低电平
CPOL=1:空闲时SCLK为高电平
CPHA决定SPI采样数据是第几个时钟沿
CPHA=0:第一个时钟沿采样
CPHA=1:第二个时钟沿采样

 

因此四种模式为:

ModeCPOL/CPHA行为
Mode 0CPOL=0
CPHA=0
SCLK空闲时为低电平,上升沿采样数据,下降沿切换数据
Mode 1CPOL=0
CPHA=1
SCLK空闲时为低电平,下降沿采样数据,上升沿切换数据
Mode 2CPOL=1
CPHA=0
SCLK空闲时为高电平,下降沿采样数据,上升沿切换数据
Mode 3CPOL=1
CPHA=1
SCLK空闲时为高电平,上升沿采样数据,下降沿切换数据

其中模式0和模式3比较常用

二、SPI Verilog实现

1.设计时序

Mode 0设计时序:

模式0:sclk空闲时为低电平,数据在上升沿采样

以传输8bit数据为例,数据传输可以分为0-15共16个状态;

发送方向——MOSI(Master Output Slaver Input): 主

机发送数据,从机要在上升沿采集数据,为了保证从机进行采样时数据稳定,因此主机在时钟下降沿切换数据(sclk下降沿时,mosi切换),因此各个状态SCLK和MOSI的行为可总结为:

状态0:SCLK为0,MOSI保持不变

状态1:SCLK为1,MOSI进行数据切换

状态2:SCLK为0,MOSI保持不变

状态3:SCLK为1,MOSI进行数据切换

状态4:SCLK为0,MOSI保持不变

状态5:SCLK为1,MOSI进行数据切换

状态6:SCLK为0,MOSI保持不变

状态7:SCLK为1,MOSI进行数据切换

状态8:SCLK为0,MOSI保持不变

状态9:SCLK为1,MOSI进行数据切换

状态10:SCLK为0,MOSI保持不变

状态11:SCLK为1,MOSI进行数据切换

状态12:SCLK为0,MOSI保持不变

状态13:SCLK为1,MOSI进行数据切换

状态14:SCLK为0,MOSI保持不变

状态15:SCLK为1,MOSI进行数据切换,数据传输完毕

接收方向——MISO(Master Iutput Slaver Onput): 主机接收数据,为了保证主机进行采样时数据稳定,因此主机在时钟上降沿c采样(mode 0:数据在上升沿被采样),因此各个状态SCLK和MISO的行为可总结为: 状态0:SCLK为0,采样MISO

状态1:SCLK为1,不采样

状态2:SCLK为0,采样MISO

状态3:SCLK为1,不采样

状态4:SCLK为0,采样MISO

状态5:SCLK为1,不采样

状态6:SCLK为0,采样MISO

状态7:SCLK为1,不采样

状态8:SCLK为0,采样MISO

状态9:SCLK为1,不采样

状态10:SCLK为0,采样MISO

状态11:SCLK为1,不采样

状态12:SCLK为0,采样MISO

状态13:SCLK为1,不采样

状态14:SCLK为0,采样MISO

状态15:SCLK为1,不采样,数据传输完毕

2.设计框图

SPI_Controller设计框图

发送方向:

data_in[7:0]:主机需要向从机发送的8bit数据

data_vld:data_in有效指示

tx_en:主机向从机的发送使能

tx_done:主机发送数据完成标志

接收方向:

data_ou[7:0]:主机接受到的来自从机的8bit数据

rx_en:主机接收数据使能

rx_done:主机接收数据完成标志

SPI接口:

spi_cs_n:片选信号

spi_sclk:spi时钟信号

spi_mosi(master output slave input):主机输出数据,从机输入数据

spi_miso(master input slaver output):主机输入数据,从机输出数据

系统接口:

sys_clk:系统时钟

sys_rst_n:系统复位信号

3.verilog代码

1.设计过程中,使用shift_in/shift_ou指示发送和接收方向数据切换与采样时刻。

2.片选信号和时钟信号在状态机结束后立即处于默认值(片选拉高,时钟拉低)

蓝牙

WiFi

温度传感器

蜂鸣器

舵机

1、PWM信号的原理

PWM(Pulse Width Modulation)——通常翻译为脉冲宽度调制技术,通过对一系列脉冲信号的宽度进行调制,来等效地获得所需要波形(含形状和幅值)。PWM控制技术在逆变电路中应用最广,应用的逆变电路绝大部分是PWM型,PWM控制技术正是有赖于在逆变电路中的应用,才确定了它在电力电子技术中的重要地位。

例如下图,其实PWM就是占空比不同的方波,即按一定的频率不断输出高电平、低电平,同时,可以在一个周期内调整高电平的持续时间(占空比)。

说到底,其实是因为电脑只能输出0和1两种逻辑情况,所以采用这种形式来模拟输出。例如我想输出0.5,那就让电脑在一个周期内一半时间输出高电平1,一段时间内输出低电平0,则平均之后就是0.5。这就是等效电压的概念。再例如,ARDUINO高电平如果是5V,我想获得3.7V的电压,在PWM波的频率不变的情况下,可以令74%周期输出5V高电平,剩下26%周期时间输出低电平,加权平均后,只要频率够快,则可以等效为3.7V的电压。

不过ARDUINO中,提供了一个控制舵机的标准库,servo.h。可以令舵机控制的代码变得很傻瓜化。

2、360度舵机与180度舵机的控制

按惯例首先介绍下舵机。

舵机其实是伺服电机的一种别称。通常我们所说的舵机是指可以根据信号转动至指定角度的电机,例如180度舵机。因为这个特性,所以往往被用于很多遥控飞机、遥控给汽车、机器人等领域,用于控制它们的方向和位置。这也是它被称为舵机的原因。

还有一种舵机,称为360度舵机,它和一般舵机的区别是:如果给一个信号,普通舵机是转动至某个角度,而360度舵机则是按这个信号对应的速度进行匀速转动。看起来似乎和普通的电机差不多,但不同点在于,360度舵机由内部的电路控制,运转速度稳定。

那么舵机如何控制呢?

1、PWM信号的原理

PWM(Pulse Width Modulation)——通常翻译为脉冲宽度调制技术,通过对一系列脉冲信号的宽度进行调制,来等效地获得所需要波形(含形状和幅值)。PWM控制技术在逆变电路中应用最广,应用的逆变电路绝大部分是PWM型,PWM控制技术正是有赖于在逆变电路中的应用,才确定了它在电力电子技术中的重要地位。

例如下图,其实PWM就是占空比不同的方波,即按一定的频率不断输出高电平、低电平,同时,可以在一个周期内调整高电平的持续时间(占空比)。

说到底,其实是因为电脑只能输出0和1两种逻辑情况,所以采用这种形式来模拟输出。例如我想输出0.5,那就让电脑在一个周期内一半时间输出高电平1,一段时间内输出低电平0,则平均之后就是0.5。这就是等效电压的概念。再例如,ARDUINO高电平如果是5V,我想获得3.7V的电压,在PWM波的频率不变的情况下,可以令74%周期输出5V高电平,剩下26%周期时间输出低电平,加权平均后,只要频率够快,则可以等效为3.7V的电压。

不过ARDUINO中,提供了一个控制舵机的标准库,servo.h。可以令舵机控制的代码变得很傻瓜化。

2、360度舵机与180度舵机的控制

按惯例首先介绍下舵机。

舵机其实是伺服电机的一种别称。通常我们所说的舵机是指可以根据信号转动至指定角度的电机,例如180度舵机。因为这个特性,所以往往被用于很多遥控飞机、遥控给汽车、机器人等领域,用于控制它们的方向和位置。这也是它被称为舵机的原因。

还有一种舵机,称为360度舵机,它和一般舵机的区别是:如果给一个信号,普通舵机是转动至某个角度,而360度舵机则是按这个信号对应的速度进行匀速转动。看起来似乎和普通的电机差不多,但不同点在于,360度舵机由内部的电路控制,运转速度稳定。

那么舵机如何控制呢?

从图上可见,舵机有三根接线,通常红线接VCC(正极),黑色或棕色接GND(负极),黄色或者白色则是PWM信号控制线。我们将电源线接至电源(面包板)的正负极,信号线则接至ARDUINO NANO的D3接收PWM信号。

当D3输出一个PWM信号时,舵机就会根据信号转动指定角度或者以指定速度旋转(360度舵机)。通常,舵机需要一个20ms左右的脉冲,该脉冲信号中,高电平部分为0.5ms至2.5ms范围。当脉冲中高电平为1.5ms时,舵机将转动至90度位置,或者静止不动(360度舵机),当脉冲大于1.5ms时,舵机转向180度方向或者正转,小于1.5ms时,转向0度方向或者反转。

通常,我们可以调用servo.h来控制舵机。该库主要有以下几个控制函数。

控制舵机可以分为4步:

(1)声明使用servo.h;

(2)给舵机起个名字;

(3)定义舵机信号线连接ARDUINO哪个针脚;

(4)用write或者writeMicroseconds函数来设置PWM波的脉冲持续时间。

另外,还可以用read函数来读取舵机当前角度或者转速,用attached来判断舵机是否正确接线,用detach函数来分离舵机连接(这个命令暂时不是特别熟悉)。

接下来我们来接线试验一下,由于我只有360度舵机,因此来尝试下用可变电阻来控制舵机转速。

接线图如下,将舵机的电源线接在面包板的正负极上,信号线接D4;可变电阻的左右两个针脚分别接ARDUINO的3.3V和GND脚,中间脚接A3。

代码如下

总结:

舵机以总脉宽20ms的周期进行执行,脉宽决定舵机的旋转角度,因此需要用PWM来控制其旋转角度,最小为0度,最大为180读。当脉宽为2.5ms时达到最大旋转角度180度。角度都是相对的,,相对起始位置的角度,只要调整脉宽就可以确定舵机的位置。

LCD1602显示屏

时序


C8051单片机部分C代码理解

sfr

SFR(Special Function Register)是特殊功能寄存器的缩写,用于访问和控制单片机的各种特殊功能和外设。这些寄存器通常包括控制器的配置、I/O端口、定时器、中断控制等。

SFR是单片机中各功能部件对应的寄存器,用于存放相应功能部件的控制命令,状态或数据。它是单片机中最具有特殊的部分,现在所有系列功能的增加和扩展几乎都是通过增加特殊功能寄存器SFR来达到目的的。

标准C中并不存在sfr和sbit指令,属于Keil的拓展指令,reg52.h和reg5.h都是单片机的头文件(里面定义了IO口的地址)

SFR通常是硬件定义的内存位置,可以通过使用C语言来读取和写入这些寄存器的值来与单片机的硬件进行交互。使用SFR的方法会因不同的单片机型号和制造商而异,因此你需要查阅相关的单片机型号的文档以获取详细的信息。

sfr指令:用来直接描述硬件地址,可以理解为“一组IO口的起始地址(例如sfr P0 = 0x80 //P0就是一组IO口)”(一般只用P1,P2,P3的IO口,具体原因与硬件相关,这里不展开描述)

sbit指令:对应可寻址空间的一个位,可以理解为"一个IO口/针脚"的地址(例如sbit led1=P0^0 //这个意思就是找到P0这组IO口的第一个IO口,并把它命名为led1)

sfrt变量名=地址值。

sfr也是一种扩充数据类型,点用一个内存单元,值域为0~255。利用它可以访问51单片机内部的所有特殊功能寄存器。如用sfr P1 = 0×90这一句定P1为P1端口在片内的寄存器,在后面的语句中我们用以用P1 = 255(对P1端口的所有引脚置高电平)之类的语句来操作特殊功能寄存器。

sfr P1 = 0×90; //定义P1 I/O口,其地址90H

sfr关键定后面是一个要定义的名字,可任意选取,但要符合标识符的命名规则,名字最好有一定的含义如P1口可以用P1为名,这样程序会变的好读好多.等号后面必须是常数,不允许有带运算符的表达式,而且该常数必须在特殊功能寄存器的地址范围之内(80H-FFH),具体可查看附录中的相关表.

sfr是定义8位的特殊功能寄存器而sfr16则是用来定义16位特殊功能寄存器,如8052的T2定时器,可以定义为:

sfr16 T2 = 0xCC; //这里定义8052定时器2,地址为T2L=CCH,T2H=CDH

用sfr16定义16位特殊功能寄存器时,等号后面是它的低位地址,高位地址一定要位于物理低位地址之上.注意的是不能用于定时器0和1的定义.

sbit

在单片机编程中,sbit 是一种关键字,通常用于8051系列单片机的C语言编程。它用于定义单个特殊功能寄存器(SFR)中的位,并为这个位提供一个易于使用的名称。sbit 的作用是让程序员能够更方便地访问和控制特殊功能寄存器中的单个位,从而简化代码并提高可读性。

#include <8051.h> // 以8051单片机为例

sbit LED = P1^0; // 定义一个位,将P1端口的第0位命名为LED

void main() {
    LED = 1; // 设置LED为高电平,点亮LED
}

在上述示例中,sbit 用于定义一个位 LED,并将其关联到 P1 端口的第0位。这使得您可以使用 LED 变量来访问和控制 P1 端口的第0位,而不需要了解特殊功能寄存器的位掩码或详细的寄存器地址。这提高了代码的可维护性和可读性。

sbit 是8051系列单片机的C编译器提供的扩展功能,因此其语法和使用可能在不同的单片机系列中有所不同。如果您使用的是不同型号的单片机,请查阅相关的编程手册和文档以了解特定于该型号的 sbit 使用方法。

sbit 要在最外面定义,就是说必须定义成外部变量.sbit定义的是SFR(特殊功能寄存器)的bit。sbit更像是类型定义,不像是变量定义。sbit: 指示说明性说明。

sbit是对应可位寻址空间的一个位,可位寻址区:20H~2FH。一旦用了sbit xxx = REGE^6这样的定义,这个sbit量就确定地址了。sbit大部分是用在寄存器中的,方便对寄存器的某位进行操作的。

sbit可定义可位寻址对象.如访问特殊功能寄存器中的某位.其实这样应用是经常要用的如要访问P1口中的第2个引脚P1.1.我们可以照以下的方法去定义:

(1) sbit位变量名=位地址

sbit P1_1 = Ox91;

这样是把位的绝对地址赋给位变量.同sfr一样sbit的位地址必须位于80H-FFH之间.

(2) sbit位变量名=特殊功能寄存器名^位位置

sft P1 = 0×90;

sbit P1_1 = P1 ^ 1; //先定义一个特殊功能寄存器名再指定位变量名所在的位置,当可寻址位位于特殊功能寄存器中时可采用这种方法

(3) sbit位变量名=字节地址^位位置

sbit P1_1 = 0×90 ^ 1;

这种方法其实和2是一样的,只是把特殊功能寄存器的位址直接用常数表示.在C51存储器类型中提供有一个bdata的存储器类型,这个是指可位寻址的数据存储器,位于单片机的可位寻址区中,可以将要求可位录址的数据定义为bdata,如:

unsigned char bdata ib; //在可位录址区定义ucsigned char类型的变量ib

int bdata ab[2]; //在可位寻址区定义数组ab[2],这些也称为可寻址位对象

sbit ib7=ib^7 //用关键字sbit定义位变量来独立访问可寻址位对象的其中一位

sbit ab12=ab[1]^12;

操作符”^”后面的位位置的最大值取决于指定的基址类型,char0-7,int0-15,long0-31

sbit的用法有三种:

第一种方法:sbit 位变量名=地址值

第二种方法:sbit 位变量名=SFR名称^变量位地址值

第三种方法:sbit 位变量名=SFR地址值^变量位地址值

如定义PSW中的OV可以用以下三种方法:

sbit OV=0xd2 (1)说明:0xd2是OV的位地址值

sbit OV=PSW^2 (2)说明:其中PSW必须先用sfr定义好

sbit OV=0xD0^2 (3)说明:0xD0就是PSW的地址值

因此这里用sbit P1_0=P1^0;就是定义用符号P1_0来表示P1.0引脚,如果你愿意也可以起P10一类的名字,只要下面程序中也随之更改就行了。

bit

Bit用于定义位变量的名字,编译器会对其分配地址。位变量分配在内部RAM的20H~2FH单元相应的位区域,位地址范围是00~7FH,共128个;

bit和int char之类的差不多,只不过char=8位, bit=1位而已。都是变量,编译器在编译过程中分配地址。除非你指定,否则这个地址是随机的。这个地址是整个可寻址空间,RAM+FLASH+扩展空间。bit只有0和1两种值,意义有点像Windows下VC中的BOOL。

bit是编译器在的可寻址区分配的一个位变量,是不定的,不是绝对地址目标。

  所有可位寻址的位都可由sbit指定,这包括可位寻址区和SFR中的位。

sfr sbit bit的区别

bit : 编译时分配空间

 sbit 只能在外部定义全局变量。

sfr(特殊功能寄存器)的bit。SFR是系统指定的内存地址。

bit 动态分配的,有编译器来指定内存地址。

bit和sbit都是C51扩展的变量类型。

sbit 要在最外面定义,就是说必须定义成外部变量.sbit定义的是SFR(特殊功能寄存器)的bit

sbit更像是类型定义,不像是变量定义。

sbit: 指示说明性说明

bit 可以在外部或内部定义。

bit是编译器在的可寻址区分配的一个位变量,是不定的,不是绝对地址目标。

所有可位寻址的位都可由sbit指定,这包括可位寻址区和SFR中的位。

sbit是对应可位寻址空间的一个位,可位寻址区:20H~2FH。一旦用了sbi xxx = REGE^6这样的定义,这个sbit量就确定地址了。sbit大部分是用在寄存器中的,方便对寄存器的某位进行操作的。

sbit位寄存器是可位寻址的绝对地址目标,定义后编译器是不会改变位置的。

C代码(*(unsigned char volatile xdata *)0xfd06)的含义

完整的代码为:#define P6INTE (*(unsigned char volatile xdata *)0xfd06)

  1. #define P6INTE:这是C语言中的宏定义指令。它用于创建一个名为P6INTE的宏,这个宏将在整个代码中用作替代符号。

  2. (*(unsigned char volatile xdata *)0xfd06):这部分是宏的替代内容。它实际上是一个SFR的内存地址的定义。

    • unsigned char:表示数据类型,这是一个无符号字符,通常用于单字节的数据。
    • volatile:表示告诉编译器不要对这个变量进行优化,因为它可能在不同的时间被外部因素修改。
    • xdata:这是用于表示特殊数据存储器类型的关键字,通常用于外部RAM、外设寄存器等。
    • 0xfd06:这是特殊功能寄存器的内存地址。

硬件电路

进制

C语言中各进制表示方法

  1. 二进制表示法(Base 2):以0b0B为前缀,后面跟着二进制数字。例如,0b1010表示十进制中的10。前缀必须是0b或0B

  2. 八进制表示法(Base 8):以0为前缀,后面跟着八进制数字。例如,012表示十进制中的10。首位必须是0表示

  3. 十进制表示法(Base 10):直接使用数字来表示,没有前缀。例如,42表示十进制中的42。

  4. 十六进制表示法(Base 16):以0x0X为前缀,后面跟着十六进制数字。例如,0x2A表示十进制中的42。前缀必须是0x或0X

实例
#include <stdio.h>

int main() {
    int binaryNumber = 0b1010;   // 二进制表示法,等于十进制的10
    int octalNumber = 012;       // 八进制表示法,等于十进制的10
    int decimalNumber = 42;     // 十进制表示法
    int hexadecimalNumber = 0x2A; // 十六进制表示法,等于十进制的42

    printf("Binary: %d\n", binaryNumber);
    printf("Octal: %d\n", octalNumber);
    printf("Decimal: %d\n", decimalNumber);
    printf("Hexadecimal: %d\n", hexadecimalNumber);

    return 0;
}

二进制数制

二进制(Binary)是一种数制,它只包含两个数字,0和1。在计算机科学和数字电子技术中,二进制是最基本的数制,用于表示和处理数字信息。在这里,我将详细解释二进制的基础知识以及与之相关的运算。

  • 二进制是Binary,简写为B
  • 由0和1两个数字组成。

二进制数字系统:

  • 位(Bit): 位是二进制的最小单位,它只能是0或1。一个二进制位表示一个二进制数字。

  • 字节(Byte): 字节是由8个二进制位组成的单元。一个字节可以表示256个不同的值($2^8$)。

  • 二进制数: 二进制数是由二进制位构成的数字。例如,1011是一个二进制数,表示十进制数11。

  • 位权: 二进制中的每一位都有一个位权,它是2的幂。最低位(最右边的位)的位权是2^0,下一位的位权是2^1,然后是2^2,以此类推。

二进制运算:

  1. 二进制加法: 二进制加法与十进制加法类似,但只包含0和1。当相加的两个位都为0时,结果为0;当一个位为1,另一个位为0时,结果为1;当两个位都为1时,结果为10(以0和1相加,产生1,进位1)。

    例如:1011 + 1101 = 11000

  2. 二进制减法: 二进制减法也与十进制减法类似,但需要考虑借位。当减数大于被减数时,需要从更高位借位。例如,1101 - 1011,需要从第三位借位,结果为 0010

  3. 二进制乘法: 二进制乘法与十进制乘法类似,但只包含0和1。将每一位与另一个二进制数相乘,根据位权决定位置。进位需要考虑。

    例如:1011 * 10 = 10110

  4. 二进制除法: 二进制除法与十进制除法类似,但只包含0和1。在每一步中,尽量确定商中的每一位,然后减去相应的部分。进位需要考虑。

    例如:1101 / 10 = 110

  5. 与、或、异或运算: 二进制中还有位运算,如与(&)、或(|)、异或(^)。这些运算用于对二进制位进行逻辑操作,通常在计算机编程中广泛使用。

    • 与运算:两个位都为1时,结果为1。1010 & 1101 = 1000
    • 或运算:至少一个位为1时,结果为1。1010 | 1101 = 1111
    • 异或运算:两个位不同,结果为1。1010 ^ 1101 = 0111

补码表示:

补码的作用:补码是一种在计算机中用来表示和处理有符号整数的编码方式。它的作用是解决有符号整数的表示和运算问题,提供了一种简便的方式来表示正数和负数,并且允许进行基本的算术运算,如加法和减法。以下是补码的主要作用:

  1. 表示负数: 在计算机中,有符号整数通常使用补码来表示负数。补码编码使得负数的表示更加简便,而不需要额外的符号位。这有助于减少硬件复杂性。

  2. 一致的加法和减法: 使用补码编码后,加法和减法可以在相同的硬件逻辑下进行,不需要区分运算类型。这简化了算术运算的硬件设计。

  3. 简化溢出检查: 补码在进行算术运算时可以自然地处理溢出。在溢出时,溢出位(Carry)可以丢弃,从而简化了溢出检查和处理。

  4. 减法运算的简化: 在补码中,减法可以看作加法的反向操作。这简化了减法运算的实现。

  5. 简化逻辑运算: 使用补码时,逻辑运算(如与、或、非)可以直接应用于整数,而不需要额外的处理。这对于比特级操作非常有用。

  6. 方便的位级操作: 补码使得位级操作,如移位、按位与、按位或等,可以直接应用于整数。这对于位操作和位掩码非常有用。

  7. 最小值和最大值对称: 补码编码中,正数和负数的表示方式是对称的。最小负数的补码与最大正数的补码在位级上相似。这使得处理范围更广的整数变得更加一致。

补码的运算

在计算机中,负数通常使用二进制补码表示。补码表示方法有以下特点:

  • 正数的补码就是其二进制表示。
  • 负数的补码是其绝对值的原码按位取反再加1。
  • 补码加法和减法与正数运算相同,不需要特殊处理。

例如,要表示-3的补码,首先写出3的二进制表示(0000 0011),然后取反得到(1111 1100),最后加1得到-3的补码(1111 1101)。

八进制数制

  • 八进制是Octal,简写为O
  • 由0-7数字组成,为了区分与其他进制的数字区别,开头都是以0开始。

八进制数字系统:

  • 位(Octit): 八进制中的最小单位被称为"Octit",它只能是0到7之间的数字。一个八进制位表示一个八进制数字。

  • 字节(Byte): 字节是由三个八进制位组成的单元。一个字节可以表示512个不同的值($8^3$)。

  • 八进制数: 八进制数是由八进制位构成的数字。例如,63是一个八进制数,表示十进制数51。

  • 位权: 八进制中的每一位都有一个位权,它是8的幂。最低位(最右边的位)的位权是$8^0$,下一位的位权是$8^1$,然后是$8^2$,以此类推。

八进制运算:

  1. 八进制加法: 八进制加法与十进制加法类似,但只包含0到7。当相加的两个位都小于8时,结果是它们的和。如果和大于等于8,需要考虑进位。例如,$54_8 + 25_8 = 111_8$。

  2. 八进制减法: 八进制减法也与十进制减法类似。如果减数小于被减数,直接减。如果减数大于被减数,需要从更高位借位。例如,$65_8 - 32_8 = 33_8$。

  3. 八进制乘法: 八进制乘法与十进制乘法类似,但只包含0到7。将每一位与另一个八进制数相乘,根据位权决定位置。进位需要考虑。例如,$45_8 \times 12_8 = 546_8$。

  4. 八进制除法: 八进制除法与十进制除法类似,但只包含0到7。在每一步中,尽量确定商中的每一位,然后减去相应的部分。进位需要考虑。例如,$162_8 / 24_8 = 6_8$。

使用八进制:

在计算机科学中,八进制不常用,主要因为二进制更适合表示和处理计算机内部的数据。然而,在某些情况下,八进制仍然有用,例如在文件权限表示中。

十进制数制 

  • 十进制是Decimal,简写为D
  • 都是以0-9这九个数字组成。

十进制算法是我们平常生活中最常用的算法,用于进行数学运算和计数。它是一种基数为10的数字系统,包括0到9的十个数字。以下是关于十进制算法以及相关运算的详细解释:

十进制数字系统:

  • 数字集合: 十进制使用0到9的十个数字表示数值。这些数字是:0, 1, 2, 3, 4, 5, 6, 7, 8, 9。

  • 位权: 十进制中,每个数字位的位权是10的幂。最右边的位的位权是$10^0$,下一个位的位权是$10^1$,然后是$10^2$,以此类推。

  • 位置表示: 十进制中,每个数字的位置决定了其值的重要性。数字位从右往左依次表示个位、十位、百位、千位,以此类推。

十六进制数制

十六进制(Hexadecimal)是一种数制,它包含16个数字,包括0到9和A到F。在计算机科学和工程领域,十六进制用于表示和处理数字信息,特别是在表示二进制数据时更为常见。以下是十六进制的基础知识以及与之相关的运算:

  • 十六进制为Hexadecimal,简写为H
  • 表示方式为0x开头
  • 计数到F后,再增加1个,就进位。
  • 由0-9和A-F组成,英文字母A,B,C,D,E,F分别表示数字10~15。
    • 123456789ABCDEF
      123456789101112131415

十六进制数字系统:

  • 位(Hexit): 十六进制中的最小单位被称为 "Hexit",它包括0到9和A到F,共16个字符。一个Hexit表示一个十六进制数字。

  • 字节(Byte): 字节是由两个十六进制位组成的单元。一个字节可以表示256个不同的值($16^2$)。

  • 十六进制数: 十六进制数是由十六进制位构成的数字。例如,1F是一个十六进制数,表示十进制数31。

  • 位权: 十六进制中的每一位都有一个位权,它是16的幂。最低位(最右边的位)的位权是$16^0$,下一位的位权是$16^1$,然后是$16^2$,以此类推。

十六进制运算:

  1. 十六进制加法: 十六进制加法与十进制加法类似,但包括0到15的范围。当相加的两个位的和小于16时,结果是它们的和。如果和大于等于16,需要考虑进位。例如,$C5_{16} + 2A_{16} = EF_{16}$。

  2. 十六进制减法: 十六进制减法也与十进制减法类似。如果减数小于被减数,直接减。如果减数大于被减数,需要从更高位借位。例如,$7B_{16} - 3E_{16} = 3D_{16}$。

  3. 十六进制乘法: 十六进制乘法与十进制乘法类似,但包括0到15的范围。将每一位与另一个十六进制数相乘,根据位权决定位置。进位需要考虑。例如,$4D_{16} \times 2_{16} = 9A_{16}$。

  4. 十六进制除法: 十六进制除法与十进制除法类似。在每一步中,尽量确定商中的每一位,然后减去相应的部分。进位需要考虑。例如,$9A_{16} / 3_{16} = 34_{16}$。

使用十六进制:

在计算机科学和工程中,十六进制广泛用于以下领域:

  • 表示二进制数据: 十六进制更紧凑和可读,因此用于表示和处理二进制数据,如内存地址、机器指令等。

  • 颜色表示: 十六进制用于表示颜色值,如HTML颜色代码(例如,#FF0000 表示红色)。

  • 网络地址表示: IP地址和MAC地址常以十六进制形式显示。

  • 文件偏移和地址: 十六进制在文件系统和磁盘分区中用于表示文件偏移和分区地址。

  • 加密和哈希: 十六进制用于表示加密密钥和哈希值。

位权

位权(也称为权重或权值)是在不同数制(如十进制、二进制、八进制、十六进制等)中,用于确定一个数字位对整个数值的贡献大小的值。位权告诉你在一个数字中每个数字位的相对重要性,它是基数的幂,其中基数是该数制中的唯一数字数量。下面是位权的一些示例以及它们的工作原理:

  1. 十进制数制: 十进制的位权是10的幂。最右边的位的位权是10^0,下一个位的位权是10^1,然后是10^2,以此类推。例如,对于数字 3526,位权如下:

    • 6(个位):$6 \times 10^0 = 6$
    • 2(十位):$2 \times 10^1 = 20$
    • 5(百位):$5 \times 10^2 = 500$
    • 3(千位):$3 \times 10^3 = 3000$
  2. 二进制数制: 二进制的位权是2的幂。最右边的位的位权是2^0,下一个位的位权是2^1,然后是2^2,以此类推。例如,对于二进制数 1101,位权如下:

    • 1(最右边):$1 \times 2^0 = 1$
    • 0(下一个位):$0 \times 2^1 = 0$
    • 1(再下一个位):$1 \times 2^2 = 4$
    • 1(最左边):$1 \times 2^3 = 8$
  3. 八进制数制: 八进制的位权是8的幂。类似地,最右边的位的位权是8^0,下一个位的位权是8^1,然后是8^2,以此类推。

  4. 十六进制数制: 十六进制的位权是16的幂。最右边的位的位权是16^0,下一个位的位权是16^1,然后是16^2,以此类推。在十六进制中,额外的数字A到F分别代表十进制的10到15。

整数进制转换

菜鸟工具(进制转换工具)

4ecb81a3ee274f36a079eaf5316b3995.png

以下部分来源:超详细进制转换(二进制、八进制、十进制、十六进制)详解_爱躺平的威威的博客-CSDN博客

十进制转二进制

十进制转二进制的原理:十进制数除以2,余数为权位上的数,得到商继续除以2,直到商为0终止,然后反向取余数。

要将十进制数转换为八进制,你可以使用以下步骤:

  1. 将十进制数除以8,并记录商和余数。
  2. 将余数转换为八进制数(0-7)。
  3. 将商作为新的十进制数,重复步骤1,直到商变为0。
  4. 将所有的八进制位从右到左排列,得到最终的八进制表示。

让我们用一个示例来说明:

假设要将十进制数 94 转换为八进制:

  1. 94 除以 8 得到商 11,余数 6。

  2. 将余数 6 保持不变。

  3. 将商 11 除以 8 得到新的商 1,余数 3。

  4. 最终,商为1,余数为3。

  5. 将所有的八进制位从右到左排列:136。

所以,十进制数 94 转换为八进制后是 136。

具体实现:

例如(67)10  (1000011)2

将67除以2得商33,余数1。将商(33)作为第二次的被除数一次类推,直到商为0.

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5aiB5aiB5rKB5rKB,size_20,color_FFFFFF,t_70,g_se,x_16

十进制转八

原理:跟十转二原理一样,十进制数除以8,余数为权位上的数,得到商继续除以8,直到商为0终止,然后反向取余数。

具体实现:例758(十进制) 1366(八进制)     

bebb3053bdcb4c61a187a30e662be51a.png

十转十六进制

原理:跟十转二原理一样,十进制数除以16,余数为权位上的数,得到商继续除以16,直到商为0终止,然后反向取余数。

要将十进制数转换为十六进制,你可以使用以下步骤:

  1. 将十进制数除以16,并记录商和余数。
  2. 将余数转换为十六进制数(0-9对应0-9,10对应A,11对应B,12对应C,13对应D,14对应E,15对应F)。
  3. 将商作为新的十进制数,重复步骤1,直到商变为0。
  4. 将所有的十六进制位从右到左排列,得到最终的十六进制表示。

让我们用一个示例来说明:

假设要将十进制数 237 转换为十六进制:

  1. 237 除以 16 得到商 14,余数 13。这里,余数 13 对应十六进制的 D。

  2. 将商 14 除以 16 得到新的商 0,余数 14。这里,余数 14 对应十六进制的 E。

  3. 最终,商为0,不再有余数。

  4. 将所有的十六进制位从右到左排列:DE。

所以,十进制数 237 转换为十六进制后是 DE。

951(十进制)→   3B7(十六进制) 

步骤跟十进制转二进制一样。

19166f877b5b4345b8f86f6ca54d0597.png

二进制转十进制

方法:把二进制数按权展开、相加即得十进制数。 

要将二进制数转换为十进制,你可以按照以下步骤进行:

  1. 从二进制数的最右边(个位)开始,将每个位上的二进制数字转换为十进制数字。

  2. 乘以对应的权值(2的幂),从右向左,权值从0开始,依次增加1。

  3. 将这些乘积相加,得到十进制表示的数值。

以下是一个示例:

假设要将二进制数 101101 转换为十进制:

  1. 从最右边的位开始:

    • 1 (二进制) 对应 1 (十进制)。
    • 0 (二进制) 对应 0 (十进制)。
    • 1 (二进制) 对应 1 (十进制)。
    • 1 (二进制) 对应 1 (十进制)。
    • 0 (二进制) 对应 0 (十进制)。
    • 1 (二进制) 对应 1 (十进制)。
  2. 确定每个位的权值(2的幂):

    • 最右边的位权值:2^0 = 1。
    • 下一个位的权值:2^1 = 2。
    • 下一个位的权值:2^2 = 4。
    • 下一个位的权值:2^3 = 8。
    • 下一个位的权值:2^4 = 16。
    • 最左边的位权值:2^5 = 32。
  3. 计算每个位上的数字与权值的乘积,并相加:

    • 1 x 1 = 1。
    • 0 x 2 = 0。
    • 1 x 4 = 4。
    • 1 x 8 = 8。
    • 0 x 16 = 0。
    • 1 x 32 = 32。
  4. 将这些乘积相加:1 + 0 + 4 + 8 + 0 + 32 = 45。

所以,二进制数 101101 转换为十进制后是 45。

8479041c80d541a3bc9e547a4590d650.png

例题: 1001 0110B = ______ D

如下图所示,答案为150D

8762dc10af0c4b848711d102b4182fda.png

例题: 26Q = ______ D

如下图所示,答案为22D

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5aiB5aiB5rKB5rKB,size_20,color_FFFFFF,t_70,g_se,x_16

例题: 23daH = ______ D

如下图所示,答案为9178D

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5aiB5aiB5rKB5rKB,size_20,color_FFFFFF,t_70,g_se,x_16

二进制转八进制

方法:3位二进制数按权展开相加得到1位八进制数。(注意事项,3位二进制转成八进制是从右到左开始转换,不足时补0)。

要将二进制数转换为八进制,你可以将每三位的二进制数转换为一个八进制位。以下是详细的步骤:

  1. 从右到左,将每三位的二进制数分组。

  2. 将每个三位的二进制数转换为对应的八进制数。下面是三位二进制数到对应八进制数的转换表:

    • 000 (二进制) 对应 0 (八进制)。
    • 001 (二进制) 对应 1 (八进制)。
    • 010 (二进制) 对应 2 (八进制)。
    • 011 (二进制) 对应 3 (八进制)。
    • 100 (二进制) 对应 4 (八进制)。
    • 101 (二进制) 对应 5 (八进制)。
    • 110 (二进制) 对应 6 (八进制)。
    • 111 (二进制) 对应 7 (八进制)。
  3. 将这些八进制位连在一起,得到整个八进制数。

让我们用一个示例来说明:

假设要将二进制数 101101110 转换为八进制:

  1. 从右到左,将每三位的二进制数分组:

    • 101
    • 101
    • 110
  2. 将每个三位的二进制数转换为对应的八进制数:

    • 101 (二进制) 对应 5 (八进制)。
    • 101 (二进制) 对应 5 (八进制)。
    • 110 (二进制) 对应 6 (八进制)。
  3. 将这些八进制位连在一起,得到整个八进制数:

    • 556

所以,二进制数 101101110 转换为八进制后是 556。

6b1fba4509ae4b1d8ec3323f191ca973.png

二进制转十六进制

二进制转十六进制时, 4位二进制数按权展开相加得到1位十六进制数,不足四位补0。

要将二进制数转换为十六进制,你可以将每四位的二进制数转换为一个十六进制位。以下是详细的步骤:

  1. 从右到左,将每四位的二进制数分组。

  2. 将每个四位的二进制数转换为对应的十六进制数。下面是四位二进制数到对应十六进制数的转换表:

    • 0000 (二进制) 对应 0 (十六进制)。
    • 0001 (二进制) 对应 1 (十六进制)。
    • 0010 (二进制) 对应 2 (十六进制)。
    • 0011 (二进制) 对应 3 (十六进制)。
    • 0100 (二进制) 对应 4 (十六进制)。
    • 0101 (二进制) 对应 5 (十六进制)。
    • 0110 (二进制) 对应 6 (十六进制)。
    • 0111 (二进制) 对应 7 (十六进制)。
    • 1000 (二进制) 对应 8 (十六进制)。
    • 1001 (二进制) 对应 9 (十六进制)。
    • 1010 (二进制) 对应 A (十六进制)。
    • 1011 (二进制) 对应 B (十六进制)。
    • 1100 (二进制) 对应 C (十六进制)。
    • 1101 (二进制) 对应 D (十六进制)。
    • 1110 (二进制) 对应 E (十六进制)。
    • 1111 (二进制) 对应 F (十六进制)。
  3. 将这些十六进制位连在一起,得到整个十六进制数。

让我们用一个示例来说明:

假设要将二进制数 110110101 转换为十六进制:

  1. 从右到左,将每四位的二进制数分组:

    • 1101
    • 1010
    • 1
  2. 将每个四位的二进制数转换为对应的十六进制数:

    • 1101 (二进制) 对应 D (十六进制)。
    • 1010 (二进制) 对应 A (十六进制)。
    • 1 (二进制) 对应 1 (十六进制)。
  3. 将这些十六进制位连在一起,得到整个十六进制数:

    • D A 1

所以,二进制数 110110101 转换为十六进制后是 DA1。

16d85307d5d8420bb5977709b80187a2.png

dd68dfda74c6483081a01cc084bed3b6.png

八进制转二进制

要将八进制数转换为二进制,你可以将每个八进制位(0-7)转换为三位的二进制数。不足时在最左边补零。 以下是详细的步骤:

  1. 将每个八进制数字转换为对应的三位二进制数。下面是八进制数字到对应二进制数的转换:

    • 0 (八进制) 对应 000 (二进制)。
    • 1 (八进制) 对应 001 (二进制)。
    • 2 (八进制) 对应 010 (二进制)。
    • 3 (八进制) 对应 011 (二进制)。
    • 4 (八进制) 对应 100 (二进制)。
    • 5 (八进制) 对应 101 (二进制)。
    • 6 (八进制) 对应 110 (二进制)。
    • 7 (八进制) 对应 111 (二进制)。
  2. 将每个八进制位的三位二进制表示连在一起,得到整个八进制数的二进制表示。

让我们用一个示例来说明:

假设要将八进制数 345 转换为二进制:

  1. 从左到右,将每个八进制数字转换为三位二进制数:

    • 3 (八进制) 对应 011 (二进制)。
    • 4 (八进制) 对应 100 (二进制)。
    • 5 (八进制) 对应 101 (二进制)。
  2. 将这些三位二进制数连在一起,得到整个二进制数:

    • 011 100 101

所以,八进制数 345 转换为二进制后是 011100101。

图片实例226八进制转换为二进制

a66df8382b024a05a2bd677e3b69c55e.png

八进制转十进制

  1. 将八进制数的每个位上的数字提取出来,从最右边的位开始。

  2. 从最右边的位(个位)开始,每个位的权值依次为8^0、8^1、8^2,以此类推。

  3. 将每个位上的数字乘以对应的权值,然后将这些乘积相加。

  4. 结果就是十进制表示的数。

以下是一个示例:

假设要将八进制数 352 转换为十进制:

  1. 从最右边的位开始提取数字:

    • 个位:2
    • 十位:5
    • 百位:3
  2. 确定每个位的权值:

    • 个位的权值:8^0 = 1
    • 十位的权值:8^1 = 8
    • 百位的权值:8^2 = 64
  3. 计算每个位上的数字与权值的乘积,并相加:

    • 2 x 1 = 2
    • 5 x 8 = 40
    • 3 x 64 = 192
  4. 将这些乘积相加:2 + 40 + 192 = 234

所以,八进制数 352 转换为十进制后是 234。

八进制转十六进制

  1. 将八进制数转换为十进制: 首先,将八进制数的每个位上的数字转换为十进制。从最右边的位开始,每个位的权值分别是8^0、8^1、8^2,以此类推。将每个位上的数字与对应的权值相乘,然后将它们相加,得到十进制数。

  2. 将十进制数转换为十六进制: 一旦你有了十进制数,就可以将其转换为十六进制。这可以通过不断除以16并记录余数的方式进行。一直重复这个过程,直到商变为0。然后,从下往上读取所有的余数,即可得到十六进制表示。

假设要将八进制数 372 转换为十六进制:

  1. 将八进制转换为十进制:

    • 2(八进制的2) = 2(十进制)
    • 7(八进制的7) = 7(十进制)
    • 3(八进制的3) = 3(十进制)

    所以,八进制数 372 转换为十进制后是 237。

  2. 将十进制转换为十六进制:

    • 237 ÷ 16 = 商为 14,余数为 13。这里,余数 13 对应十六进制的 D。
    • 14 ÷ 16 = 商为 0,余数为 14。这里,余数 14 对应十六进制的 E。

    所以,十六进制表示为 DE。

因此,八进制数 372 转换为十六进制后是 DE。

152.76 O=_______H

将八进制转换成十六进制

 46a6350b759543caa433e3c3410f047c.png

十六进制转二进制

要将十六进制数转换为二进制,你可以将每个十六进制位(0-F)转换为四位的二进制数。以下是十六进制到二进制的转换规则:

  1. 0 在十六进制中对应 0000 在二进制中。
  2. 1 在十六进制中对应 0001 在二进制中。
  3. 2 在十六进制中对应 0010 在二进制中。
  4. 3 在十六进制中对应 0011 在二进制中。
  5. 4 在十六进制中对应 0100 在二进制中。
  6. 5 在十六进制中对应 0101 在二进制中。
  7. 6 在十六进制中对应 0110 在二进制中。
  8. 7 在十六进制中对应 0111 在二进制中。
  9. 8 在十六进制中对应 1000 在二进制中。
  10. 9 在十六进制中对应 1001 在二进制中.
  11. A 在十六进制中对应 1010 在二进制中.
  12. B 在十六进制中对应 1011 在二进制中.
  13. C 在十六进制中对应 1100 在二进制中.
  14. D 在十六进制中对应 1101 在二进制中.
  15. E 在十六进制中对应 1110 在二进制中.
  16. F 在十六进制中对应 1111 在二进制中.

现在,让我们来看一个示例。假设我们要将十六进制数 1A3 转换为二进制:

  1. 1 对应 0001.
  2. A 对应 1010.
  3. 3 对应 0011.

34db9fb943dd40e9b2da5c30f21595c5.png

十六进制转八进制

要将十六进制数转换为八进制,你可以先将十六进制数转换为二进制,然后再将二进制数转换为八进制。以下是详细的步骤:

步骤 1:将十六进制数转换为二进制。

  1. 将每个十六进制数字转换为四位的二进制数。下面是十六进制数字到对应二进制数的转换表:

    • 0 (十六进制) 对应 0000 (二进制)。
    • 1 (十六进制) 对应 0001 (二进制)。
    • 2 (十六进制) 对应 0010 (二进制)。
    • 3 (十六进制) 对应 0011 (二进制)。
    • 4 (十六进制) 对应 0100 (二进制)。
    • 5 (十六进制) 对应 0101 (二进制)。
    • 6 (十六进制) 对应 0110 (二进制)。
    • 7 (十六进制) 对应 0111 (二进制)。
    • 8 (十六进制) 对应 1000 (二进制)。
    • 9 (十六进制) 对应 1001 (二进制)。
    • A (十六进制) 对应 1010 (二进制)。
    • B (十六进制) 对应 1011 (二进制)。
    • C (十六进制) 对应 1100 (二进制)。
    • D (十六进制) 对应 1101 (二进制)。
    • E (十六进制) 对应 1110 (二进制)。
    • F (十六进制) 对应 1111 (二进制)。
  2. 将每个十六进制位的二进制表示连在一起,得到整个十六进制数的二进制表示。

步骤 2:将二进制数转换为八进制。

  1. 将二进制数从右向左每三位分组,如果最左边不满三位,可以在前面补零。

  2. 将每个三位的二进制组转换为对应的八进制数字。

    • 000 对应 0 (八进制)。
    • 001 对应 1 (八进制)。
    • 010 对应 2 (八进制)。
    • 011 对应 3 (八进制)。
    • 100 对应 4 (八进制)。
    • 101 对应 5 (八进制)。
    • 110 对应 6 (八进制)。
    • 111 对应 7 (八进制)。
  3. 将八进制组合在一起,得到最终的八进制数。

让我们用一个示例来说明:

假设要将十六进制数 1A3 转换为八进制:

步骤 1:将十六进制数转换为二进制。

  • 1 (十六进制) 对应 0001 (二进制)。
  • A (十六进制) 对应 1010 (二进制)。
  • 3 (十六进制) 对应 0011 (二进制)。

所以,十六进制数 1A3 转换为二进制是 000110100011。

步骤 2:将二进制数转换为八进制。

现在,将二进制数分成三位一组:

  • 000 110 100 011

然后,将每组的二进制转换为八进制:

  • 000 对应 0 (八进制)。
  • 110 对应 3 (八进制)。
  • 100 对应 4 (八进制)。
  • 011 对应 3 (八进制)。

最后,将这些八进制数组合在一起,得到最终的八进制数:

  • 0343

所以,十六进制数 1A3 转换为八进制后是 0343。

十六进制转十进制

要将十六进制数转换为十进制,你可以按照以下步骤进行:

  1. 从十六进制数的最右边(个位)开始,将每个位上的十六进制数字转换为十进制数字。

  2. 乘以对应的权值(16的幂),从右向左,权值从0开始,依次增加1。

  3. 将这些乘积相加,得到十进制表示的数值。

以下是一个示例:

假设要将十六进制数 1A3 转换为十进制:

  1. 从最右边的位开始:

    • 3 (十六进制) 对应 3 (十进制)。
    • A (十六进制) 对应 10 (十进制)。
    • 1 (十六进制) 对应 1 (十进制)。
  2. 确定每个位的权值(16的幂):

    • 3 对应 16^0 = 1。
    • A 对应 16^1 = 16。
    • 1 对应 16^2 = 256。
  3. 计算每个位上的数字与权值的乘积,并相加:

    • 3 x 1 = 3。
    • 10 x 16 = 160。
    • 1 x 256 = 256。
  4. 将这些乘积相加:3 + 160 + 256 = 419。

所以,十六进制数 1A3 转换为十进制后是 419。

小数进制转换

小数部分转换原理都是乘进制数取整数部分,再将整数部分顺序输出。

十进制转二进制

方法:十进制小数转换成R进制小数采用“乘二取整,顺序输出”

例题: 0.618D = ______ B(精确到小数点后3位)

如下所示,0.68乘以2,取整,然后再将小数乘以2,取整,直到达到题目要求精度。

得到结果:0.101B.

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5aiB5aiB5rKB5rKB,size_20,color_FFFFFF,t_70,g_se,x_16

 十进制转八进制

(1)原理:十进制小数转换成八进制小数采用 “乘8取整,顺序输出” 法。

(2)思路和十进制转二进制一样,参考如下例题:

例题: 10.68D = ______ Q(精确到小数点后3位)
解析:如下图所示,整数部分除以8取余数,直到无法整除。小数部分0.68乘以8,取整,然后再将小数乘以8,取整,直到达到题目要求精度。得到结果:12.534Q.

例如:十进制数10.68转换成八进制数,分为整数部分和小数部分求解
步骤:
(1)整数部分
10/8=1 -->2
1/8=0 -->1
倒序输出为12
(2)小数部分
0.68* 8=5.44 -->5
0.44* 8=3.52 -->3
0.52* 8=4.16 -->4
已经达到了题目要求的精度,即可结束
则小数部分为:0.68–>0.534
因此10.68D -->12.534Q

十进制转十六进制

(1)原理:十进制小数转换成十六进制小数采用 “乘16取整,顺序输出” 法。

要将十进制小数转换为十六进制,你可以使用以下步骤:

  1. 将十进制小数的整数部分和小数部分分开。

  2. 将整数部分转换为十六进制,可以使用和整数转换为十六进制的方法。

  3. 将小数部分转换为十六进制。这通常涉及到将小数部分乘以16的幂,然后将结果逐位转换为十六进制。一般情况下,你可能需要限制小数点后的位数,以避免无限循环。

  4. 将整数部分的十六进制表示和小数部分的十六进制表示以小数点分隔开。

让我们用一个示例来说明:

假设要将十进制小数 12.75 转换为十六进制:

  1. 整数部分是 12,将其转换为十六进制,得到 C。

  2. 小数部分是 0.75。首先,将 0.75 乘以 16,得到 12。然后,将 12 转换为十六进制,得到 C。

  3. 将整数部分和小数部分的十六进制表示以小数点分隔开:C.C。

例题: 25.68D = ______ H(精确到小数点后3位)

解析:如下图所示,整数部分除以16取余数,直到无法整除。小数部分0.68乘以16,取整,然后再将小数乘以16,取整,直到达到题目要求精度。得到结果:19.ae1H.
(1)整数部分
25/16=1 -->9
1/16=0 -->1
倒序输出为:19
(2)小数部分
0.68* 16=10.88 -->a(即十进制中的10)
0.88* 16=14.08 -->e
0.08* 16=1.28 -->1
已经达到了要求的精度,顺序输出为:ae1
则:25.68D -->19.ae1H

二进制转八进制

要将二进制小数转换为八进制,你可以使用以下步骤:

  1. 将二进制小数的整数部分和小数部分分开。

  2. 将整数部分转换为八进制,可以使用和整数转换为八进制的方法。

  3. 将小数部分转换为八进制。这涉及将每个二进制位乘以2的负幂(-1,-2,-3,等等),然后将结果相加。

  4. 将整数部分和小数部分的八进制表示连接起来。

让我们用一个示例来说明:

假设要将二进制小数 101.1101 转换为八进制:

  1. 整数部分是 101,将其转换为八进制,得到 5。

  2. 小数部分是 0.1101。将每个二进制位乘以2的负幂,然后将结果相加:

    • 1 x 2^(-1) = 0.5
    • 1 x 2^(-2) = 0.25
    • 0 x 2^(-3) = 0
    • 1 x 2^(-4) = 0.0625

    总和为 0.5 + 0.25 + 0 + 0.0625 = 0.8125。

  3. 整数部分 5 转换为八进制,保持不变。

  4. 小数部分 0.8125 转换为八进制,可以通过将其乘以8得到 6.5。然后,将 6.5 转换为八进制,得到 6.4。

  5. 将整数部分和小数部分的八进制表示连接起来:5.64。

所以,二进制小数 101.1101 转换为八进制后是 5.64。

二进制转十进制

 方法:把R进制数按权展开、相加即得十进制数。(具体操作如下)

2a86419a5e5743ec887500b3f846f52f.png

小数转换跟普通转换类似,小数点后幂次就变成了负的。 

二进制转十六进制

要将二进制小数转换为十六进制,你可以使用以下步骤:

  1. 将二进制小数的整数部分和小数部分分开。

  2. 将整数部分转换为十进制,可以使用和整数转换为十进制的方法。

  3. 将小数部分转换为十进制。这涉及将每个二进制位乘以2的负幂(-1,-2,-3,等等),然后将结果相加。

  4. 将整数部分和小数部分的十进制表示转换为十六进制。

  5. 将整数部分和小数部分的十六进制表示以小数点分隔开。

让我们用一个示例来说明:

假设要将二进制小数 1101.101 转换为十六进制:

  1. 整数部分是 1101,将其转换为十进制,得到 13。

  2. 小数部分是 0.101。将每个二进制位乘以2的负幂,然后将结果相加:

    • 1 x 2^(-1) = 0.5
    • 0 x 2^(-2) = 0
    • 1 x 2^(-3) = 0.125

    总和为 0.5 + 0 + 0.125 = 0.625。

  3. 整数部分 13 转换为十六进制,得到 D。

  4. 小数部分 0.625 转换为十六进制,可以通过将其乘以16得到 10。然后,将 10 转换为十六进制,得到 A。

  5. 将整数部分和小数部分的十六进制表示以小数点分隔开:D.A。

所以,二进制小数 1101.101 转换为十六进制后是 D.A。

八进制转二进制

要将八进制小数转换为二进制,你可以使用以下步骤:

  1. 将八进制小数的整数部分和小数部分分开。

  2. 将整数部分转换为二进制,可以使用和整数转换为二进制的方法。

  3. 将小数部分转换为二进制。这涉及将每个八进制位转换为对应的三位二进制数,然后将它们连接起来。

  4. 将整数部分和小数部分的二进制表示连接起来。

让我们用一个示例来说明:

假设要将八进制小数 5.64 转换为二进制:

  1. 整数部分是 5,将其转换为二进制,得到 101。

  2. 小数部分是 0.64。将每个八进制位转换为对应的三位二进制数:

    • 6 (八进制) 对应 110 (二进制)。
    • 4 (八进制) 对应 100 (二进制)。

    然后将它们连接起来,得到 110100。

  3. 将整数部分和小数部分的二进制表示连接起来:101.110100。

所以,八进制小数 5.64 转换为二进制后是 101.110100。

八进制转十进制

要将八进制小数转换为十进制,你可以使用以下步骤:

  1. 将八进制小数的整数部分和小数部分分开。

  2. 将整数部分转换为十进制,可以使用和整数转换为十进制的方法。

  3. 将小数部分转换为十进制。这涉及将每个八进制位转换为对应的十进制数,然后将它们相加。

  4. 将整数部分和小数部分的十进制表示相加。

让我们用一个示例来说明:

假设要将八进制小数 5.64 转换为十进制:

  1. 整数部分是 5,将其保持不变,仍为 5。

  2. 小数部分是 0.64。将每个八进制位转换为对应的十进制数:

    • 6 (八进制) 对应 6 (十进制)。
    • 4 (八进制) 对应 4 (十进制)。

    然后将它们相加,得到 6 + 4 = 10。

  3. 将整数部分和小数部分的十进制表示相加:5 + 10 = 15。

所以,八进制小数 5.64 转换为十进制后是 15。

八进制转十六进制

要将八进制小数转换为十六进制,你可以使用以下步骤:

  1. 将八进制小数的整数部分和小数部分分开。

  2. 将整数部分转换为十六进制,可以使用和整数转换为十六进制的方法。

  3. 将小数部分转换为十六进制。这涉及将每个八进制位转换为对应的十六进制数,然后将它们连接起来。

  4. 将整数部分和小数部分的十六进制表示相连接。

让我们用一个示例来说明:

假设要将八进制小数 5.64 转换为十六进制:

  1. 整数部分是 5,将其转换为十六进制,得到 5。

  2. 小数部分是 0.64。将每个八进制位转换为对应的十六进制数:

    • 6 (八进制) 对应 6 (十六进制)。
    • 4 (八进制) 对应 4 (十六进制)。

    然后将它们连接起来,得到 64。

  3. 将整数部分和小数部分的十六进制表示连接起来:5.64。

所以,八进制小数 5.64 转换为十六进制后仍然是 5.64。

十六进制转二进制

要将十六进制小数转换为二进制,你可以使用以下步骤:

  1. 将十六进制小数的整数部分和小数部分分开。

  2. 将整数部分转换为二进制,可以使用和整数转换为二进制的方法。

  3. 将小数部分转换为二进制。这通常涉及将每个十六进制位转换为对应的四位二进制数,然后将它们连接起来。

  4. 将整数部分和小数部分的二进制表示相连接。

让我们用一个示例来说明:

假设要将十六进制小数 3.A 转换为二进制:

  1. 整数部分是 3,将其转换为二进制,得到 0011。

  2. 小数部分是 A。将每个十六进制位转换为对应的四位二进制数:

    • A (十六进制) 对应 1010 (二进制)。

    然后将它们连接起来,得到 1010。

  3. 将整数部分和小数部分的二进制表示连接起来:0011.1010。

所以,十六进制小数 3.A 转换为二进制后是 0011.1010。

十六进制转八进制

要将十六进制小数转换为八进制,你可以使用以下步骤:

  1. 将十六进制小数的整数部分和小数部分分开。

  2. 将整数部分转换为八进制,可以使用和整数转换为八进制的方法。

  3. 将小数部分转换为八进制。这通常涉及将每个十六进制位转换为对应的三位八进制数,然后将它们连接起来。

  4. 将整数部分和小数部分的八进制表示连接起来。

让我们用一个示例来说明:

假设要将十六进制小数 3.A 转换为八进制:

  1. 整数部分是 3,将其转换为八进制,得到 3。

  2. 小数部分是 A。将每个十六进制位转换为对应的三位八进制数:

    • A (十六进制) 对应 012 (八进制)。

    然后将它们连接起来,得到 012。

  3. 将整数部分和小数部分的八进制表示连接起来:3.012。

所以,十六进制小数 3.A 转换为八进制后是 3.012。

十六进制转十进制

要将十六进制小数转换为十进制,你可以使用以下步骤:

  1. 将十六进制小数的整数部分和小数部分分开。

  2. 将整数部分转换为十进制,可以使用和整数转换为十进制的方法。

  3. 将小数部分转换为十进制。这通常涉及将每个十六进制位转换为对应的十进制数,然后将它们相加。

  4. 将整数部分和小数部分的十进制表示相加。

让我们用一个示例来说明:

假设要将十六进制小数 3.A 转换为十进制:

  1. 整数部分是 3,将其转换为十进制,得到 3。

  2. 小数部分是 A。将每个十六进制位转换为对应的十进制数:

    • A (十六进制) 对应 10 (十进制)。

    然后将它们相加,得到 10。

  3. 将整数部分和小数部分的十进制表示相加:3 + 10 = 13。

所以,十六进制小数 3.A 转换为十进制后是 13。

Keil的使用

大致流程

1、

创建新项目

一、基本创建文件到编译的使用

常用代码详解

#define MAIN_Fosc		11059200L	//定义主时钟
#include	"STC8H.H"  包含芯片

#define  uint16   unsigned int  
#define  uint8    unsigned char  
 
/****************************************************************
功能描述:延时函数
入口参数:uint16 x ,该值为1时,延时1ms
返回值:无
*****************************************************************/
void delay_ms(uint16 x) 
{  
	uint16 j,i;   
	for(j=0;j<x;j++)   
	{    
		for(i=0;i<1580;i++);   
	}  
}

 /**************************************************************************
功能描述:主函数
入口参数:无
返回值:无
 *************************************************************************/
int main(void)
{
	P0M1 &= 0xFD;	P0M0 &= 0xFD;	  //设置P0.1为准双向口
	//P0M1 &= 0xFD;	P0M0 |= 0x02;	  //设置P0.1为推挽输出
	//P0M1 |= 0x02;	P0M0 &= 0xFD;	  //设置P0.1为高阻输入
	//P0M1 |= 0x02;	P0M0 |= 0x02;	  //设置P0.1为开漏输出
	
  while(1)
  {
		P01=0;	      //控制P0.1端口输出低电平,点亮用户指示灯D3
		delay_ms(200);
		P01=1;	      //控制P0.1端口输出高电平,熄灭用户指示灯D3
		delay_ms(200);
	}
}

在这段代码中,#define MAIN_Fosc 11059200L 是一个宏定义(Macro Definition),用于给一个标识符分配一个特定的数值。具体来说,这行代码定义了一个名为 MAIN_Fosc 的宏,并将其值设置为 11059200L

11059200L 是一个整数常量,后面的 L 表示这是一个长整型(long integer)常量。这个值表示的是主时钟频率,通常以赫兹(Hz)为单位。在这个特定的代码中,主时钟频率被设置为 11,059,200 Hz,或者说 11.0592 MHz(兆赫兹)。这是一种常见的主时钟频率,特别是在一些嵌入式系统和单片机应用中,因为它是一个标准的晶振频率,方便了系统的时序和计时操作。

#define uint16 unsigned int: 这行代码将 uint16 定义为一个无符号整数(unsigned int)的别名。也就是说,每次你在代码中使用 uint16,它会被替换为 unsigned int。无符号整数是一种整数类型,通常用于表示正整数值。在大多数系统中,unsigned int 是16位整数。

#define uint8 unsigned char: 这行代码将 uint8 定义为一个无符号字符(unsigned char)的别名。无符号字符通常用于表示8位无符号整数值,范围在0到255之间。

函数 delay_ms 接受一个参数 x,它表示所需的延时时间,以毫秒为单位。

  1. 内层循环:for(i=0;i<1580;i++); 这个内层循环执行了1580次迭代,每次迭代不执行任何有意义的操作,只是用于浪费一定的时间。这个内层循环的目的是提供一个短时间的延时,具体的延时时间取决于循环的执行速度和次数。

  2. 外层循环:for(j=0;j<x;j++) 这个外层循环用于多次执行内层循环,从而延时的总时间为 x 毫秒。外层循环执行的次数由参数 x 决定,每次执行内层循环都会浪费一定的时间。

P0M1 &= 0xFD; P0M0 &= 0xFD;: 这两行代码是用来设置P0.1引脚为准双向口(Quasi-bidirectional),也就是一个特殊的工作模式,允许引脚在输入和输出之间切换。这是通过对P0M1和P0M0寄存器的位操作来实现的。

  • 注释中的其他几行代码是针对不同的工作模式设置,但是被注释掉了,没有被执行。这些工作模式包括推挽输出、高阻输入和开漏输出,不同的工作模式适用于不同的应用场景,以满足连接到P0.1引脚的外部设备的要求。
  • &= 是位与并赋值操作符,它用于将左边的操作数与右边的操作数进行位与操作,然后将结果赋值给左边的操作数。
  • P0.1 是引用单片机(或嵌入式系统)的端口 P0 上的第 1 个引脚。在这种表示法中,P0 是指端口 0,而 .1 表示这个端口上的第 1 个引脚。通常在嵌入式系统或单片机的开发中,引脚是根据它们的端口和引脚号进行编号。嵌入式系统通常有多个端口,每个端口上有多个引脚,这些引脚用于与外部设备通信、连接传感器、执行器等。引脚的编号通常从 0 开始,所以 P0.1 表示端口 0 上的第 1 个引脚。

GPIO.H文件
------------------------------------------------------
#ifndef	__GPIO_H
#define	__GPIO_H

#include	"config.h"

#define	GPIO_PullUp		0	//准双向口(弱上拉)
#define	GPIO_HighZ		1	//高阻输入
#define	GPIO_OUT_OD		2	//开漏输出
#define	GPIO_OUT_PP		3	//推挽输出(强上拉)

#define	GPIO_Pin_0		0x01	//IO引脚 Px.0
#define	GPIO_Pin_1		0x02	//IO引脚 Px.1
#define	GPIO_Pin_2		0x04	//IO引脚 Px.2
#define	GPIO_Pin_3		0x08	//IO引脚 Px.3
#define	GPIO_Pin_4		0x10	//IO引脚 Px.4
#define	GPIO_Pin_5		0x20	//IO引脚 Px.5
#define	GPIO_Pin_6		0x40	//IO引脚 Px.6
#define	GPIO_Pin_7		0x80	//IO引脚 Px.7
#define	GPIO_Pin_All	0xFF	//IO所有引脚
	
#define	GPIO_P0			0		
#define	GPIO_P1			1
#define	GPIO_P2			2
#define	GPIO_P3			3
#define	GPIO_P4			4
#define	GPIO_P5			5
#define	GPIO_P6			6
#define	GPIO_P7			7

typedef struct
{
	uint8	Mode;		//IO模式,  		GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
	uint8	Pin;		//要设置的端口	
} GPIO_InitTypeDef;

uint8	GPIO_Inilize(uint8 GPIO, GPIO_InitTypeDef *GPIOx);

#endif
------------------------------------------------------
Config.h文件

#ifndef		__CONFIG_H
#define		__CONFIG_H

/*********************************************************/
//#define MAIN_Fosc		22118400L	//定义主时钟
//#define MAIN_Fosc		12000000L	//定义主时钟
#define MAIN_Fosc		11059200L	//定义主时钟
//#define MAIN_Fosc		 5529600L	//定义主时钟
//#define MAIN_Fosc		24000000L	//定义主时钟

/*********************************************************/

#include	"STC8H.h"

/**************************************************************************/

#define Main_Fosc_KHZ	(MAIN_Fosc / 1000)

#define  uint16   unsigned int  
#define  uint8    unsigned char 

/***********************************************************/
#endif
------------------------------------------------------
GPIO.C文件
/*
功能说明
本文件为STC8xxx系列的端口初始化程序,用户几乎可以不修改这个程序.
*/

#include	"GPIO.h"

//========================================================================
// 函数: uint8	GPIO_Inilize(uint8 GPIO, GPIO_InitTypeDef *GPIOx)
// 描述: 初始化IO口.
// 参数: GPIOx: 结构参数,请参考gpio.h里的定义.
// 返回: 成功返回0, 空操作返回1,错误返回2.
//========================================================================
uint8	GPIO_Inilize(uint8 GPIO, GPIO_InitTypeDef *GPIOx)
{
	if(GPIO > GPIO_P7)				return 1;	//空操作
	if(GPIOx->Mode > GPIO_OUT_PP)	return 2;	//错误

	if(GPIO == GPIO_P0)
	{
		if(GPIOx->Mode == GPIO_PullUp)		P0M1 &= ~GPIOx->Pin,	P0M0 &= ~GPIOx->Pin;	 //上拉准双向口
		if(GPIOx->Mode == GPIO_HighZ)		  P0M1 |=  GPIOx->Pin,	P0M0 &= ~GPIOx->Pin;	 //浮空输入
		if(GPIOx->Mode == GPIO_OUT_OD)		P0M1 |=  GPIOx->Pin,	P0M0 |=  GPIOx->Pin;	 //开漏输出
		if(GPIOx->Mode == GPIO_OUT_PP)		P0M1 &= ~GPIOx->Pin,	P0M0 |=  GPIOx->Pin;	 //推挽输出
	}
	if(GPIO == GPIO_P1)
	{
		if(GPIOx->Mode == GPIO_PullUp)		P1M1 &= ~GPIOx->Pin,	P1M0 &= ~GPIOx->Pin;	 //上拉准双向口
		if(GPIOx->Mode == GPIO_HighZ)		  P1M1 |=  GPIOx->Pin,	P1M0 &= ~GPIOx->Pin;	 //浮空输入
		if(GPIOx->Mode == GPIO_OUT_OD)		P1M1 |=  GPIOx->Pin,	P1M0 |=  GPIOx->Pin;	 //开漏输出
		if(GPIOx->Mode == GPIO_OUT_PP)		P1M1 &= ~GPIOx->Pin,	P1M0 |=  GPIOx->Pin;	 //推挽输出
	}
	if(GPIO == GPIO_P2)
	{
		if(GPIOx->Mode == GPIO_PullUp)		P2M1 &= ~GPIOx->Pin,	P2M0 &= ~GPIOx->Pin;	 //上拉准双向口
		if(GPIOx->Mode == GPIO_HighZ)		  P2M1 |=  GPIOx->Pin,	P2M0 &= ~GPIOx->Pin;	 //浮空输入
		if(GPIOx->Mode == GPIO_OUT_OD)		P2M1 |=  GPIOx->Pin,	P2M0 |=  GPIOx->Pin;	 //开漏输出
		if(GPIOx->Mode == GPIO_OUT_PP)		P2M1 &= ~GPIOx->Pin,	P2M0 |=  GPIOx->Pin;	 //推挽输出
	}
	if(GPIO == GPIO_P3)
	{
		if(GPIOx->Mode == GPIO_PullUp)		P3M1 &= ~GPIOx->Pin,	P3M0 &= ~GPIOx->Pin;	 //上拉准双向口
		if(GPIOx->Mode == GPIO_HighZ)		  P3M1 |=  GPIOx->Pin,	P3M0 &= ~GPIOx->Pin;	 //浮空输入
		if(GPIOx->Mode == GPIO_OUT_OD)		P3M1 |=  GPIOx->Pin,	P3M0 |=  GPIOx->Pin;	 //开漏输出
		if(GPIOx->Mode == GPIO_OUT_PP)		P3M1 &= ~GPIOx->Pin,	P3M0 |=  GPIOx->Pin;	 //推挽输出
	}
	if(GPIO == GPIO_P4)
	{
		if(GPIOx->Mode == GPIO_PullUp)		P4M1 &= ~GPIOx->Pin,	P4M0 &= ~GPIOx->Pin;	 //上拉准双向口
		if(GPIOx->Mode == GPIO_HighZ)		  P4M1 |=  GPIOx->Pin,	P4M0 &= ~GPIOx->Pin;	 //浮空输入
		if(GPIOx->Mode == GPIO_OUT_OD)		P4M1 |=  GPIOx->Pin,	P4M0 |=  GPIOx->Pin;	 //开漏输出
		if(GPIOx->Mode == GPIO_OUT_PP)		P4M1 &= ~GPIOx->Pin,	P4M0 |=  GPIOx->Pin;	 //推挽输出
	}
	if(GPIO == GPIO_P5)
	{
		if(GPIOx->Mode == GPIO_PullUp)		P5M1 &= ~GPIOx->Pin,	P5M0 &= ~GPIOx->Pin;	 //上拉准双向口
		if(GPIOx->Mode == GPIO_HighZ)		  P5M1 |=  GPIOx->Pin,	P5M0 &= ~GPIOx->Pin;	 //浮空输入
		if(GPIOx->Mode == GPIO_OUT_OD)		P5M1 |=  GPIOx->Pin,	P5M0 |=  GPIOx->Pin;	 //开漏输出
		if(GPIOx->Mode == GPIO_OUT_PP)		P5M1 &= ~GPIOx->Pin,	P5M0 |=  GPIOx->Pin;	 //推挽输出
	}
	if(GPIO == GPIO_P6)
	{
		if(GPIOx->Mode == GPIO_PullUp)		P6M1 &= ~GPIOx->Pin,	P6M0 &= ~GPIOx->Pin;	 //上拉准双向口
		if(GPIOx->Mode == GPIO_HighZ)		  P6M1 |=  GPIOx->Pin,	P6M0 &= ~GPIOx->Pin;	 //浮空输入
		if(GPIOx->Mode == GPIO_OUT_OD)		P6M1 |=  GPIOx->Pin,	P6M0 |=  GPIOx->Pin;	 //开漏输出
		if(GPIOx->Mode == GPIO_OUT_PP)		P6M1 &= ~GPIOx->Pin,	P6M0 |=  GPIOx->Pin;	 //推挽输出
	}
	if(GPIO == GPIO_P7)
	{
		if(GPIOx->Mode == GPIO_PullUp)		P7M1 &= ~GPIOx->Pin,	P7M0 &= ~GPIOx->Pin;	 //上拉准双向口
		if(GPIOx->Mode == GPIO_HighZ)		  P7M1 |=  GPIOx->Pin,	P7M0 &= ~GPIOx->Pin;	 //浮空输入
		if(GPIOx->Mode == GPIO_OUT_OD)		P7M1 |=  GPIOx->Pin,	P7M0 |=  GPIOx->Pin;	 //开漏输出
		if(GPIOx->Mode == GPIO_OUT_PP)		P7M1 &= ~GPIOx->Pin,	P7M0 |=  GPIOx->Pin;	 //推挽输出
	}
	return 0;	//成功
}
------------------------------------------------------
Main.c文件
#include	"GPIO.h"

/**********************************************************************
功能描述:GPIO口初始化
入口参数:无
返回值:无
***********************************************************************/
void	GPIO_config(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
 
    //设置P0.1口工作模式
    GPIO_InitStructure.Mode=GPIO_PullUp;      //配置P0.1口为准双向口
	  //GPIO_InitStructure.Mode=GPIO_OUT_PP;    //配置P0.1口为推挽输出(强上拉)
	  //GPIO_InitStructure.GPIO_HighZ;          //配置P0.1口为高阻输入
	  //GPIO_InitStructure.Mode=GPIO_OUT_OD;    //配置P0.1口为开漏输出
    GPIO_InitStructure.Pin=GPIO_Pin_1;
    GPIO_Inilize(GPIO_P0,&GPIO_InitStructure);
 
} 
 
/**********************************************************************
功能描述:延时函数
入口参数:uint16 x ,该值为1时,延时1ms
返回值:无
***********************************************************************/
void delay_ms(uint16 x) 
{  
	uint16 j,i;   
	for(j=0;j<x;j++)   
	{    
		for(i=0;i<1580;i++);   
	}  
}

 /**************************************************************************
功能描述:主函数
入口参数:无
返回值:无
 *************************************************************************/
int main(void)
{
  GPIO_config();   //设置P0.1口为准双向口
	
  while(1)
  {
		P01=0;	      //控制P0.1端口输出低电平,点亮用户指示灯D3
		delay_ms(500);
		P01=1;	      //控制P0.1端口输出高电平,熄灭用户指示灯D3
		delay_ms(500);
	}
}

代码中共有5个文件

  • GPIO.H文件  配置寄存器作用和官方STC8H.h类似,手动简化他的作用
  • Config.h文件  配置主时钟和简化字符
  • GPIO.C文件  
  • Main.c文件
  • STC8H.h文件
  • uint8 表示函数的返回类型,这里是一个8位无符号整数(unsigned char)。

  • GPIO 是函数的第一个参数,用于指定要初始化的 GPIO 口。这个参数是一个8位无符号整数。

  • GPIO_InitTypeDef *GPIOx 是函数的第二个参数,它是一个指向结构体 GPIO_InitTypeDef 的指针,用于指定 GPIO 引脚的初始化参数。

接下来,我们将详细解释一些符号和代码段的含义:

  1. if(GPIO > GPIO_P7) return 1;

    • 这是一个条件语句,用于检查传递给函数的 GPIO 参数是否大于 GPIO_P7GPIO_P7 可能是一个预定义的常量,表示最大的 GPIO 口。如果 GPIO 大于 GPIO_P7,函数会返回 1,表示错误。
  2. if(GPIOx->Mode > GPIO_OUT_PP) return 2;

    • 这是另一个条件语句,用于检查传递给函数的 GPIOx 结构体中的 Mode 参数是否大于 GPIO_OUT_PPGPIO_OUT_PP 可能是另一个预定义的常量,表示 GPIO 的输出模式之一。如果 Mode 大于 GPIO_OUT_PP,函数会返回 2,表示错误。
  3. 下面的一系列 if 语句用于根据传递的 GPIO 参数和 Mode 参数来配置 GPIO 引脚的工作模式。例如,对于 GPIO_P0,根据不同的 Mode 值,它会执行不同的位操作来配置 P0M1P0M0 寄存器。这些寄存器通常用于控制 GPIO 引脚的工作模式。

  4. P0M1 &= ~GPIOx->Pin, P0M0 &= ~GPIOx->Pin;

    • &= 是按位与并赋值操作符,用于将 P0M1P0M0 寄存器中与 GPIOx->Pin 中对应位的值进行与操作,结果再赋值回这两个寄存器。 ~ 是按位取反操作符,它将 GPIOx->Pin 中的各位取反。因此,这行代码的目的是清除 P0M1P0M0 中与 GPIOx->Pin 对应的位,以配置 GPIO 引脚的工作模式。
  5. return 0;

    • 如果没有出现错误,函数将返回 0,表示初始化成功。
#流水灯代码-----------
#define MAIN_Fosc		11059200L	//定义主时钟
#include	"STC8H.H"

#define  uint16   unsigned int  
#define  uint8    unsigned char  

/*********************************************
引脚别名定义
**********************************************/			
sbit LED_D1=P0^3;		  //用户指示灯D1用IO口P03	
sbit LED_D2=P0^2;     //用户指示灯D2用IO口P02	
sbit LED_D3=P0^1;     //用户指示灯D3用IO口P01
sbit LED_D4=P0^0;     //用户指示灯D4用IO口P00	

/*************************************************************
功能描述:延时函数
入口参数:uint16 x ,该值为1时,延时1ms
返回值:无
**************************************************************/
void delay_ms(uint16 x) 
{  
	uint16 j,i;   
	for(j=0;j<x;j++)   
	{    
		for(i=0;i<1580;i++);   
	}  
}

/*************************************************************************
功能描述:流水灯
入口参数:无
返回值:无
 ************************************************************************/
void LED_Blink(void)
{
		LED_D1=0;	       //点亮用户指示灯D1
		LED_D2=1;        //熄灭用户指示灯D2
	  LED_D3=1;        //熄灭用户指示灯D3
	  LED_D4=1;        //熄灭用户指示灯D4
		delay_ms(300);
		LED_D1=1;	       //熄灭用户指示灯D1
		LED_D2=0;        //点亮用户指示灯D2
	  LED_D3=1;        //熄灭用户指示灯D3
	  LED_D4=1;        //熄灭用户指示灯D4
		delay_ms(300);
		LED_D1=1;	       //熄灭用户指示灯D1
		LED_D2=1;        //熄灭用户指示灯D2
	  LED_D3=0;        //点亮用户指示灯D3
	  LED_D4=1;        //熄灭用户指示灯D4
		delay_ms(300);
		LED_D1=1;	       //熄灭用户指示灯D1
		LED_D2=1;        //熄灭用户指示灯D2
	  LED_D3=1;        //熄灭用户指示灯D3
	  LED_D4=0;        //点亮用户指示灯D4
		delay_ms(300);
		LED_D1=1;	       //熄灭用户指示灯D1
		LED_D2=1;        //熄灭用户指示灯D2
	  LED_D3=1;        //熄灭用户指示灯D3
	  LED_D4=1;        //熄灭用户指示灯D4
		delay_ms(300);
}

 /**************************************************************************
功能描述:主函数
入口参数:无
返回值:无
 *************************************************************************/
int main(void)
{
	P0M1 &= 0xF0;	P0M0 &= 0xF0;	  //设置P0.3~P0.0为准双向口
	//P0M1 &= 0xF0;	P0M0 |= 0x0F;	//设置P0.3~P0.0为推挽输出
  //P0M1 |= 0x0F;	P0M0 &= 0xF0;	//设置P0.3~P0.0为高阻输入
  //P0M1 |= 0x0F;	P0M0 |= 0x0F;	//设置P0.3~P0.0为开漏输出
	
  while(1)
  {
		LED_Blink();	  //指示灯流水点亮
	}
}

 此流水灯程序很简单

先给相关LED灯定义引脚,便于控制


设定延时函数方便调用,此函数可以在该单片机中共用
设置单片机的流水程序,按顺序一个一个亮,定一个合理的延时,用于清楚的观察效果
设置相关引脚的输出模式,根据需要设置,保证驱动该引脚是一个输出高电平的状态。

按键检测代码
#define MAIN_Fosc		11059200L	//定义主时钟
#include	"STC8H.H"

#define  uint16   unsigned int  
#define  uint8    unsigned char  

/******************************************
引脚别名定义
*******************************************/
sbit KEY=P4^4; 	       //用户按键S5用IO口P44
sbit LED_D3=P0^1;      //用户指示灯D3用IO口P01	

/**************************************
功能描述:延时函数
入口参数:uint16 x ,该值为1时,延时1ms
返回值:无
***************************************/
void delay_ms(uint16 x) 
{  
	uint16 j,i;   
	for(j=0;j<x;j++)   
	{    
		for(i=0;i<1580;i++);   
	}  
}

/**************************************************************************
功能描述:主函数
入口参数:无
返回值:无
*************************************************************************/
int main(void)
{
	P0M1 &= 0xFD;	P0M0 &= 0xFD;	  //设置P0.1为准双向口 
	P4M1 &= 0xEF;	P4M0 &= 0xEF;	  //设置P4.4为准双向口
//	P4M1 |= 0x10;	P4M0 &= 0xEF;	  //设置P4.4为高阻输入
	
	P_SW2 |= 0x80;		            //将EAXFR位置1,以访问在XDATA区域的扩展SFR
	P4PU |= 0x10;                 //设置P4.4口有上拉电阻
	P_SW2 &= 0x7F;		            //将EAXFR位置0,恢复访问XRAM		
	
  while(1)
  {
		 if(KEY == 0)         //检测用户按键S5对应引脚P4.4是否是低电平 (按键按下,引脚为低电平)
	   {
	     delay_ms(10);      //软件延时10ms,如果延时后按键S5的电平依然没有变化,说明按键确实被有效操作,简称按键消抖
	     if(KEY== 0)	      //检测用户按键S5对应引脚P4.4是否依然是低电平
	     {
				 LED_D3=0;	             //点亮用户指示灯D3
		     while(KEY == 0)         //等待按键S5释放,即如果P4.4一直为低电平,会一直执行空命令
				 {
					 ;                     //条件KEY == 0成立,会执行这个空命令
				 }
         LED_D3=1;	             //按键S5释放,熄灭用户指示灯D3
	    }
	   }
	}
}

代码详解 

中断服务

#include  "delay.h"
#include	"led.h"
#include  "exint.h"	

/**************************************************************************
功能描述:主函数
入口参数:无
返回值:无
*************************************************************************/
int main(void)
{
	P0M1 &= 0xF0;	P0M0 &= 0xF0;	  //设置P0.3~P0.0为准双向口
	P3M1 &= 0xFB;	P3M0 &= 0xFB;	  //设置P3.2为准双向口
	P_SW2 |= 0x80;		            //将EAXFR位置1,以访问在XDATA区域的扩展SFR
	P3PU |= 0x04;                 //设置P3.2口有上拉电阻
	P_SW2 &= 0x7F;		            //将EAXFR位置0,恢复访问XRAM	
	
	INT0_Init();                  //外部中断0的初始化配置	
	EA = 1;		                    //允许总中断

  while (1)
	{
		;              //无任务,说明LED亮灭来自于中断
	}
}

  1. 引入头文件:

    • #include "delay.h":引入了一个名为 "delay.h" 的头文件,该头文件可能包含了延时函数的声明或定义。
    • #include "led.h":引入了一个名为 "led.h" 的头文件,该头文件可能包含了控制LED的函数声明或定义。
    • #include "exint.h":引入了一个名为 "exint.h" 的头文件,该头文件可能包含了外部中断初始化函数的声明或定义。
  2. 主函数(main函数):

    • main 函数是程序的入口点,它初始化一些引脚配置并启用外部中断,然后进入一个无限循环。
  3. 端口配置:

    • P0M1P0M0 寄存器被用于配置 P0 端口的工作模式。在这里,将 P0.3~P0.0 配置为准双向口(即可以作为输入或输出)。
    • P3M1P3M0 寄存器被用于配置 P3 端口的工作模式。在这里,将 P3.2 配置为准双向口。
    • P_SW2 寄存器被用于配置扩展SFR(Special Function Register)的访问。这里将 EAXFR 位置1,以访问扩展SFR。
    • P3PU 寄存器用于配置 P3.2 端口,使其具有上拉电阻。
    • 最后,将 P_SW2 寄存器中的 EAXFR 位置0,恢复对XRAM区域的访问。
  4. 外部中断初始化:

    • INT0_Init() 函数被调用,用于初始化外部中断0。这意味着程序预期在外部中断0引脚(通常为P3.2)上检测中断触发条件。
  5. 总中断允许:

    • EA = 1; 语句用于启用总中断。这意味着一旦启用,程序将能够响应所有中断请求。
  6. 主循环:

    • while (1) 创建了一个无限循环。在这个循环中,程序不执行具体的任务,只是空等。这是因为该程序的操作主要来自于外部中断触发。
  7. 外部中断操作:

    • 通过 INT0_Init() 初始化后,程序可能会在外部中断0触发时执行特定的操作。外部中断通常用于检测外部事件,例如按钮按下、传感器触发等,然后执行相应的处理。

 中断服务初始化

#include "led.h"
#include "exint.h"	

void INT2_Init(void)
{
   INTCLKO |= 0x10;   //使能INT2中断允许位
}

void INT2_Isr (void) interrupt 10		
{
	 led_toggle(LED_3);	      //翻转用户指示灯D3
}
  1. 引入头文件:

    • #include "led.h":引入了一个名为 "led.h" 的头文件,该头文件可能包含了控制LED的函数声明或定义。
    • #include "exint.h":引入了一个名为 "exint.h" 的头文件,该头文件可能包含了外部中断初始化函数的声明或定义。
  2. 外部中断2初始化(INT2_Init函数):

    • INT2_Init 函数用于初始化外部中断2。
    • INTCLKO |= 0x10; 设置了 INT2 中断允许位,允许外部中断2的触发。
  3. 外部中断服务程序(INT2_Isr函数):

    • INT2_Isr 函数是外部中断2的中断服务程序。它被声明为中断服务函数,可以在中断触发时被调用。
    • interrupt 10 声明表明这是一个中断服务函数,而且是第10个中断服务函数。中断服务函数的执行是由硬件自动触发的。
    • 在中断服务函数中,led_toggle(LED_3); 用于翻转用户指示灯 D3,即切换 D3 灯的状态。这意味着每次外部中断2触发时,D3 灯的状态都会改变,从亮变暗或从暗变亮。

PWM服务

使用原理

1、通过定时器控制中断

犯过的错误

配置IO口的错误

一、P0M1 = 0x70;    P0M0 = 0xF0;

二、P0M1 &= 0x70;    P0M0 &= 0xF0;

三、P0M1 &= 0xF0;
P0M0 &= 0xF0;
P0M1 &= 0x7F;
P0M0 |= 0x80;
三组的区别在于赋值是不同的

第一组直接配置相关寄存器为两组16进制对应的数

第二组是逻辑“与”在进行赋值,要考虑两组数据位运算之后的数值

第三组同第二组,多次位运算再赋值的目的在于只配置需要的寄存器而不改变不需要配置的寄存器

说明:此文本为个人单片机学习记录,长期更新,便于后续个人复习使用,部分资料收集来源于网络,如有侵权,请联系删除。

  • 4
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本目录下提供的是《手把手教你学51单片机(C语言版)》教材中所有例程与作业习题的源代码。 请将整个目录复制到电脑硬盘上使用,如直接在光盘路径下打开工程可能会导致软件报错。 运行目录下的Clean.bat可以清理程序编译过程中产生的中间文件和临时文件。 lesson2 (第2章): 点亮一个LED示例,所在章节2.4 lesson3 (第3章): LED闪烁示例,所在章节3.5 lesson4 (第4章): 4_1:用P0口控制LED的示例,所在章节4.8 4_2:用P0口控制实现流水灯效果,所在章节4.8 4_t4:作业题4,逐次右移的流水灯效果,所在章节4.9 4_t5:作业题5,左右往复移动的流水灯效果,所在章节4.9 lesson5 (第5章): 5_1:基于定时器查询模式的LED闪烁,所在章节5.2.3 5_2:静态点亮数码管的某几段,所在章节5.3.2 5_3:数码管静态显示一位字符,所在章节5.3.3 5_t3:作业题3,定时器查询模式实现左右移动的流水灯,所在章节5.4 5_t5:作业题5,变更lesson5_3的正计数为倒计数,所在章节5.4 lesson6 (第6章): 6_1:数码管动态显示原理示例(if...else if...语句示例),所在章节6.4 6_2:数码管动态显示原理示例(switch...case...语句示例),所在章节6.4 6_3:基于中断带消隐的数码管动态显示示例,所在章节6.5 6_t4:作业题4,在lesson6_3多位数字显示的基础上不显示高位的0,所在章节6.6 6_t5:作业题5,变更lesson6_3的正计数为倒计数,所在章节6.6 lesson7 (第7章): 7_1:基于数码管计时程序的静态变量演示,所在章节7.2 7_2:点亮LED点阵上的一个点,所在章节7.3 7_3:点亮LED点阵上的一行,所在章节7.3 7_4:点亮LED点阵上的全部点,所在章节7.3 7_5:LED点阵显示静态图形,所在章节7.4 7_6:LED点阵显示纵向移动的动画,所在章节7.5.1 7_6_h:LED点阵显示横向移动的动画(掉转板子方向的取巧方式),所在章节7.5.2 7_7:LED点阵显示横向移动的动画,所在章节7.5.2 7_t3:作业题3,lesson7_6的向上移动改为向下移动,所在章节7.6 7_t4:作业题4,lesson7_7的向左移动改为向右移动,所在章节7.6 7_t5:作业题5,基于LED点阵的9~0倒计数,所在章节7.6 7_t6:作业题6,独立LED、数码管、点阵LED同时全亮,所在章节7.6 lesson8 (第8章): 8_1:基于数码管计时程序的函数调用演示,所在章节8.2 8_2:按键基本原理演示例程,所在章节8.4.3 8_3:独立按键扫描原理演示例程,所在章节8.4.3 8_4:独立按键消抖原理演示例程,所在章节8.4.4 8_5:独立按键扫描并消抖的演示例程,所在章节8.4.4 8_6:矩阵按键扫描并消抖的演示例程,所在章节8.4.5 8_7:基于矩阵按键和数码管实现的简易加法计算器,所在章节8.5 8_t4:作业题4,变更lesson8_5的递增计数为递减计数,所在章节8.6 8_t5:作业题5,在lesson8_7基础上实现简易加减计算器,所在章节8.6 lesson9 (第9章): 9_1:步进电机驱动的基础示例,所在章节9.3.3 9_2:步进电机转动任意角度的示例,所在章节9.3.4 9_3:实用的步进电机驱动示例,所在章节9.3.5 9_4:按键控制步进电机转动的示例,所在章节9.3.6 9_5:蜂鸣器驱动的基础示例,所在章节9.4 9_6:蜂鸣器演奏简单乐谱——“两只老虎”,所在章节9.4 lesson10 (第10章): 10_1:基于数码管显示的数字秒表,所在章节10.1 10_2:基于PWM方式控制LED的亮度,所在章节10.2 10_3:基于PWM方式控制LED实现呼吸灯效果,所在章节10.2 10_4:交通信号灯示例,所在章节10.3 10_5:长短按键/连续按键功能实现示例,所在章节10.5 10_t3:作业题3,数码管计时与流水灯同时运行的示例,所在章节10.6 lesson11 (第11章): 11_1:普通IO口模拟实现串口通信的示例,所在章节11.4 11_2:单片机硬件UART查询方式实现串口通信的示例,所在章节11.5.3 11_3:单片机硬件UART中断方式实现串口通信的示例,所在章节11.5.3 11_4:UART串口通信及控制数码管显示的示例,所在章节11.6 11_t5:作业题5,UART串口控制流水灯流动和停止,所在章节11.7 11_t6:作业题6,UART串口控制蜂鸣器的开关,所在章节11.7 lesson12 (第12章): 12_1:指针作为函数参数的示例,所在章节12.1 12_2:指向变量的指针与变量关系的示例,所在章节12.2 12_3:指针、字符串、字符数组、ASCII码演示示例,所在章节12.3 12_4:1602液晶基本操作演示示例,所在章节12.4 lesson13 (第13章): 13_1:1602液晶显示两行字符串,并实现整屏的重复左移,所在章节13.2 13_2:多c文件示例,1602液晶显示两行字符串,并实现整屏的重复左移,所在章节13.3 13_3:整型数为操作数的简易+-*/计算器,所在章节13.4 13_4:基于帧模式的实用串口程序示例,所在章节13.5 13_t2:作业题2,1602液晶显示两行字符串,并实现整屏的重复右移,所在章节13.6 lesson14 (第14章): 14_1:寻址I2C总线上存在的和不存在的地址,将应答状态显示到液晶上,所在章节14.2 14_2:用单字节读写模式访问EEPROM,每次+1后写回,所在章节14.3.1 14_3:用多字节读写模式访问EEPROM,依次+1,+2,+3...后写回,所在章节14.3.2 14_4:用连续读与分页写模式访问EEPROM,依次+1,+2,+3...后写回,所在章节14.3.3 14_5:读取EEPROM中的数据显示到液晶,并可通过UART修改EEPROM中的数据,所在章节14.4 14_t3:作业题3,以lesson10_4为基础实现可通过UART设定时间的交通灯示例,所在章节14.5 14_t4:作业题4,基于液晶、按键、EEPROM的密码锁示例,所在章节14.5 lesson15 (第15章): 15_1:用单次读写模式访问DS1302,并将日期时间显示在液晶上,所在章节15.3.4 15_2:用突发读写模式访问DS1302,并将日期时间显示在液晶上,所在章节15.3.5 15_3:DS1302实现简易电子钟,通过按键校时,所在章节15.5 lesson16 (第16章): 16_1:接收NEC协议的红外编码并将用户码和键码显示到数码管上,所在章节16.3 16_2:控制DS18B20测量温度并将温度值显示到液晶上,所在章节16.4 16_t2:作业题2,用遥控器控制步进电机正反转,所在章节16.5 16_t4:作业题4,带温度显示的电子钟,所在章节16.5 lesson17 (第17章): 17_1:将模拟输入通道0、1、3的电压值显示到液晶上,所在章节17.4 17_2:由按键控制DA输出可调电压值,所在章节17.6 17_3:由按键控制DA输出可变化的波形,所在章节17.7 17_t3:作业题3,可调频率的信号发生器,所在章节17.8 lesson18 (第18章): 18_1:RS485基本示例,接收任意数据帧并添加回车换行后送回,所在章节18.1 18_2:基于RS485的Modbus通信示例,支持寄存器读写操作,所在章节18.3 18_t3:作业题3,基于Modbus协议实现电子钟校时,所在章节18.4 lesson19 (第19章): 19_1:多功能电子钟例程,所在章节19.4
这段Verilog代码实现了一个带有PWM功能的32分频器。让我为你逐行解释代码的作用: ```verilog module PWM_Divider ( input wire clk, input wire [1:0] pwm_duty_cycle, output wire clk_out ); ``` 这是一个模块定义,包括一个时钟信号 `clk`、一个2位的PWM占空比信号 `pwm_duty_cycle` 和一个输出时钟信号 `clk_out`。 ```verilog reg [4:0] counter; reg [1:0] pwm_counter; wire pwm_output; ``` 这里定义了两个寄存器 `counter` 和 `pwm_counter`,分别用于计数和PWM占空比计数。还有一个线网 `pwm_output`,用于控制输出时钟信号。 ```verilog assign pwm_output = (pwm_counter < pwm_duty_cycle); ``` 这行代码使用比较运算符 `<` 将 `pwm_counter` 和 `pwm_duty_cycle` 进行比较,得到一个PWM输出信号 `pwm_output`。当 `pwm_counter` 小于 `pwm_duty_cycle` 时,PWM输出为高电平,否则为低电平。 ```verilog always @(posedge clk) begin if (counter == 4'b11111) begin counter <= 0; if (pwm_counter == 2'b11) begin pwm_counter <= 0; end else begin pwm_counter <= pwm_counter + 1; end end else begin counter <= counter + 1; end end ``` 这段代码使用 `always @(posedge clk)` 表示在时钟上升沿触发的时候执行内部逻辑。当 `counter` 计数达到最大值时,将其重置为0,并根据 `pwm_counter` 的值来更新 `pwm_counter`。当 `pwm_counter` 达到最大值时,也将其重置为0。 ```verilog assign clk_out = pwm_output; ``` 这行代码将PWM输出信号 `pwm_output` 直接赋值给输出 `clk_out`。 综上所述,该代码实现了一个32分频器,并根据PWM占空比信号控制输出时钟信号的高低电平。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值