• 51单片机


    1.#include   "STC8H.H" //包含此头文件后,不需要再包含"reg51.h"头文件;#include  "reg51.h"//包含此头文件;#include   "reg51.h" //包含此头文件;必须增加“引号”才可以,不然报错;

    2.使用ADC1进行8位ADC转换, 主动查询(polling)方式:

    使用ADC1进行8位ADC转换, 主动查询(polling)方式

    下面的例子, 使用主动查询的方式每隔0.1秒对P1.1口进行ADC转换, 精度8位, 将结果输出至串口

    main.c代码

    1. #include "fw_hal.h"
    2. void main(void)
    3. {
    4. uint8_t res;
    5. // 调整系统频率, 如果使用STC-ISP设定频率, 需要将这行注释掉
    6. SYS_SetClock();
    7. // 用于结果输出
    8. UART1_Config8bitUart(UART1_BaudSource_Timer2, HAL_State_ON, 115200);
    9. // 将 ADC1(GPIO P1.1) 设为高阻输入
    10. GPIO_P1_SetMode(GPIO_Pin_1, GPIO_Mode_Input_HIP);
    11. // 使用通道: ADC1
    12. ADC_SetChannel(0x01);
    13. // 设置ADC时钟 = SYSCLK / 2 / (1+1) = SYSCLK / 4
    14. ADC_SetClockPrescaler(0x01);
    15. // 设置结果左对齐, 只需要取值 ADC_RES
    16. ADC_SetResultAlignmentLeft();
    17. // 开启ADC电源
    18. ADC_SetPowerState(HAL_State_ON);
    19. while(1)
    20. {
    21. // 开始转换
    22. ADC_Start();
    23. // 等待两个系统时钟
    24. NOP();
    25. NOP();
    26. // 检查转换结果标志位是否置位
    27. while (!ADC_SamplingFinished());
    28. // 清除结果标志位
    29. ADC_ClearInterrupt();
    30. // 读取结果
    31. res = ADC_RES;
    32. // 通过串口1输出
    33. UART1_TxString("Result: ");
    34. UART1_TxHex(res);
    35. UART1_TxString("\r\n");
    36. // 等待100ms后再次进行转换
    37. SYS_Delay(100);
    38. }
    39. }

    3.使用ADC1进行10位/12位ADC转换, 中断(interrupt)方式

    下面的例子, 使用中断的方式对P1.1口进行ADC连续转换, 精度10位(或12位, MCU型号不同精度不同), 每隔0.1秒将结果输出至串口

    1. #include "fw_hal.h"
    2. // 16位变量用于记录转换结果
    3. uint16_t res;
    4. // 处理中断的方法, 使用宏定义保证Keil C51和SDCC的兼容性
    5. INTERRUPT(ADC_Routine, EXTI_VectADC)
    6. {
    7. // 先清除中断位
    8. ADC_ClearInterrupt();
    9. // 结果低8
    10. res = ADC_RESL;
    11. // 结果高8
    12. res |= (ADC_RES & 0x0F) << 8;
    13. // 再次启动, 使得ADC连续转换,
    14. ADC_Start();
    15. }
    16. void main(void)
    17. {
    18. // 设置系统频率
    19. SYS_SetClock();
    20. // 结果输出
    21. UART1_Config8bitUart(UART1_BaudSource_Timer2, HAL_State_ON, 115200);
    22. // 设置P11高阻输入模式
    23. GPIO_P1_SetMode(GPIO_Pin_1, GPIO_Mode_Input_HIP);
    24. // 使用通道: ADC1
    25. ADC_SetChannel(0x01);
    26. // ADC时钟 = SYSCLK / 2 / (1+15) = SYSCLK / 32
    27. ADC_SetClockPrescaler(0x0F);
    28. // 右对齐, 方便转换为双字节的结果
    29. ADC_SetResultAlignmentRight();
    30. // 开启全局中断和ADC中断
    31. EXTI_Global_SetIntState(HAL_State_ON);
    32. EXTI_ADC_SetIntState(HAL_State_ON);
    33. // 开启ADC电源
    34. ADC_SetPowerState(HAL_State_ON);
    35. // 开始ADC转换
    36. ADC_Start();
    37. while(1)
    38. {
    39. // 转换结果输出
    40. UART1_TxString("Result: ");
    41. UART1_TxHex(res >> 8);
    42. UART1_TxHex(res & 0xFF);
    43. UART1_TxString("\r\n");
    44. SYS_Delay(100);
    45. }
    46. }

    4.使用ADC1, ADC2双通道进行转换, 中断(interrupt)方式

    下面介绍一个更实用的例子, 中断形式进行多通道ADC转换, 可以用于无线小车遥控, 双声道音频采样等

    1. #include "fw_hal.h"
    2. // 用于记录当前采样的通道编号
    3. uint8_t pos;
    4. // 记录各通道的采样结果
    5. uint16_t res[2];
    6. // 中断处理方法
    7. INTERRUPT(ADC_Routine, EXTI_VectADC)
    8. {
    9. ADC_ClearInterrupt();
    10. // 记录采样结果
    11. res[pos] = ADC_RESL;
    12. res[pos] |= (ADC_RES & 0x0F) << 8;
    13. // 切换到下一个通道
    14. pos = (pos+1) & 0x1;
    15. if (pos == 0)
    16. {
    17. /**
    18. * 在采样频率较高时, 加上这两句能提高精度. 其机制是切换到开漏模式清除采样口上的残留电压
    19. GPIO_P1_SetMode(GPIO_Pin_1, GPIO_Mode_InOut_OD);
    20. GPIO_P1_SetMode(GPIO_Pin_1, GPIO_Mode_Input_HIP);
    21. */
    22. ADC_SetChannel(0x01);
    23. }
    24. else
    25. {
    26. /**
    27. * Uncomment these lines in high speed ADC
    28. GPIO_P1_SetMode(GPIO_Pin_2, GPIO_Mode_InOut_OD);
    29. GPIO_P1_SetMode(GPIO_Pin_2, GPIO_Mode_Input_HIP);
    30. */
    31. ADC_SetChannel(0x02);
    32. }
    33. ADC_Start();
    34. }
    35. // 下面的代码和前面的基本上是一样的, 就不详细注释了
    36. void main(void)
    37. {
    38. SYS_SetClock();
    39. // For debug print
    40. UART1_Config8bitUart(UART1_BaudSource_Timer2, HAL_State_ON, 115200);
    41. // Channel: ADC1
    42. ADC_SetChannel(0x01);
    43. // ADC Clock = SYSCLK / 2 / (1+15) = SYSCLK / 32
    44. ADC_SetClockPrescaler(0x0F);
    45. // Right alignment, high 2-bit in ADC_RES, low 8-bit in ADC_RESL
    46. ADC_SetResultAlignmentRight();
    47. // Enable interrupts
    48. EXTI_Global_SetIntState(HAL_State_ON);
    49. EXTI_ADC_SetIntState(HAL_State_ON);
    50. // Turn on ADC power
    51. ADC_SetPowerState(HAL_State_ON);
    52. // Set ADC1(P1.1), ADC2(P1.2) HIP
    53. GPIO_P1_SetMode(GPIO_Pin_1|GPIO_Pin_2, GPIO_Mode_Input_HIP);
    54. // Start ADC
    55. ADC_Start();
    56. while(1)
    57. {
    58. UART1_TxString("Result: ");
    59. UART1_TxHex(res[0] >> 8);
    60. UART1_TxHex(res[0] & 0xFF);
    61. UART1_TxChar(' ');
    62. UART1_TxHex(res[1] >> 8);
    63. UART1_TxHex(res[1] & 0xFF);
    64. UART1_TxString("\r\n");
    65. SYS_Delay(100);
    66. }
    67. }

    5.通用串行总线(Universal Serial Bus, 简称USB), 是当前使用最广泛的外设接口. 因为供电简单, 支持热插拔, 扩展端口简单, 传输方式多样化, 兼容性好, 支持的外设类型丰富, 基本成为PC标配.
    STC MCU从STC15开始支持USB ISP, 但是仅仅用于下载和调试, 可在代码中配置的USB外设功能直到STC8H8K64U才出现. USB标准规范包括USB1.0, USB1.1, USB2.0, USB3.0 以及2019年9月发布的 USB4TM, STC8H8K64U 支持的是 USB2.0 标准.

    USB的物理连接结构

    USB系统由一个USB Host, 一个或多个USB Hub, 一个或多个USB Node 组成. USB的物理连接是层次性的星型结构, 在系统中, 有且仅有一个USB主机, USB Hub实际上就是一个具有特殊功能的USB设备. 所有的USB设备都连接在USB Hub上, 同时Hub有责任为每个连接其上的USB Node 提供 +5V 和 500mA 的电源.

    USB总线采用的是树形结构、主从工作模式. USB主机根据各个设备的属性, 周期性访问各个设备. 而USB设备则是被动响应USB主机的访问请求, 这样避免了USB设备主动发送数据导致的总线冲突. 当然, 这样就导致没有USB主机的情况下 USB 设备之间无法通讯. USB OTG(USB On-The-Go)技术用于解决此问题, 拓展了USB在嵌入式设备, 如手机、PDA等方面的应用.

    USB系统的分层结构

    在系统设计和逻辑连接关系上USB总线系统具有明确的分层结构. USB设备与主机的驱动/应用程序通信, 是通过特定的USB端点来实现的.

    整个USB系统可以分为USB总线接口层, USB设备层, 功能层

    1. USB总线接口层
      USB总线接口层主要用于实现USB主机和USB设备之间的数据传输, 实际的数据流是在此层运行的. USB规范中 , USB总线接口NRZI编码(反向归零编码)来传输数据. 编码的过程是自动进行的, 由USB系统硬件完成.
    2. USB设备层
      USB设备层主要用于管理USB设备, 包括分配USB地址、读取设备描述报文等. 在这一层中, USB主机可以获取USB设备的各种属性或能力. 这部分的功能, USB主机通过驱动软件来完成, 而USB设备的固件层也需要编写对应的代码进行支持.
    3. 功能层
      功能层主要负责数据传输, 由USB设备的功能单元和对应的USB主机程序实现. 按照双方通信的类型, 可分为
      • 控制传输(Control Transfers). 主要用于传输少了的对时间和速率没有要求的数据, 一般用于USB主机读取或设置USB设备的配置信息, 或者其他的简单操作.
      • 中断传输(Interrupt Transfers). 主要用于传输少了对传输时间具有周期性要求的数据, 在鼠标、键盘等HID设备中经常使用.
      • 批量传输(Bulk Transfers). 用于传输大量的、对传输时间和速率没有严格要求的数据.
      • 实时传输(Isochronous Transfers). 用于传输大量的、且传输时间具有周期性、速率恒定的数据

    在USB HID设备的传输中主要使用的是控制传输.

    USB的命令(USB Device Request)

    USB规范定义了设备请求(USB Device Request), 以更好地完成USB主机对总线上所有USB设备的统一控制. 此设备请求由USB主机发往USB设备. USB命令包括USB标准命令, 类命令, 厂商命令. 这些命令的格式都是相同的, 如下表所示.

    偏移 字段 长度 数值 描述
    0 bmRequesType 1 位图 D7: 数据的传输方向: 0=主机->设备, 1=设备->主机
    D6-D5: 命令的类型, 0=标准命令 1=类命令 2=厂商命令 3=保留
    D4-D0: 接收对象 0=设备 1=接口 2=端点 3=其他  4…31=保留
    1 bRequest 1 命令的序号
    2 wValue 2 根据不同的命令, 含义也不同
    4 wIndex 2 索引或偏移 根据不同的命令, 含义不同, 主要用于传送索引或者偏移
    6 wLength 2 如果有数据阶段, 此字段为数据的字节数

    标准命令是每种USB设备都要支持的, 类命令则与USB设备所述类有关, 比如USB HID设备有HID类特有的命令, 如Get_Report、SetReport等.

    USB标准命令

    USB 1.1的规范中, 规定了11种USB标准命令, 用来完成各种目的. 根据不同的命令, 相应的字段含义也有所不同. 下表列出了11个USB标准命令的功能.

    命令 请求号 功能描述
    Get_Status 0x00 读取USB设备、接口或者端口的状态
    Clear_Feature 0x01 清除或禁止USB设备、接口或端点的某些特征
    Set_Feature 0x03 设置或使能USB设备、接口或端点的某些特征
    Set_Address 0x05 分配USB设备地址
    Get_Descriptor 0x06 读取描述报文
    Set_Descriptor 0x07 更新已有的描述报文或添加新的描述报文
    Get_Configuration 0x08 读取USB设备的当前配置值
    Set_Configuration 0x09 为USB设备选择一个合适的配置值
    Get_Interface 0x0A 获得设备接口当前工作的选择设置值
    Set_Interface 0x0B 激活USB设备的某个接口
    Synch_Frame 0x0C 设置并报告端点的同步帧号

    在USB规范中, 对于这些标准的USB命令, 所有的USB设备必须都支持, 并能够对命令进行响应. 如果不需要对命令进行操作, 也必须准备一个空的响应. 除了这些标准的USB命令, 对于不同的类, 也有类相关的USB命令. 比如人机接口类设备有Set_Report、Get_Report等命令, 集线器类设备有GetHubStatus、GetBusState等命令. 在开发相应类设备的时候, 也需要熟悉这些类本身特有的USB命令.

    下表是Get_Descriptor命令的结构

    偏移 字段 内容
    0 bmRequesType 值为10000000B, 设备到主机
    1 bRequest GET_DESCRIPTOR, 0x06
    2 wValue 描述报文的类型和描述报文的索引值
    4 wIndex 0或语言标识(LANGID)
    6 wLength 描述报文的长度
    • Get_Descriptor命令用来获取USB设备的各种描述报文, 包括设备描述报文、配置描述报文、接口描述报文、端点描述报文和字符串描述报文.
    • 需要获取的描述报文类型, 由wValue字段给出. wValue由两个字节组成, 高字节表示描述报文的类型, 低字节表示描述报文的索引值.
    • wIndex字段除去获取字符串描述报文之外, 其他情况下设置为0. 获得字符串描述报文的过程分为两步: 第一次发送命令后获得语言标识;第二次发送命令时, 将语言标识赋给wIndex字段, 需要获取的字符串描述报文的索引值赋给wValue字段, 即可获得所需要的字符串.
    • Get_Descriptor命令中的字段wLength, 表示描述报文的字节长度, 由USB主机指定. 当指定wLength比实际的描述报文长度小时, USB设备严格按照主机指定的字节长度返回描述报文信息;当wLength比实际的描述报文长度大时, USB设备值返回描述报文长度的信息. 比如在访问配置描述报文时, USB主机不清楚配置信息的总长, 可以将Get_Descriptor命令中的wLength设置为4, 得到配置描述报文中wTotalLength. 然后, 重新发送Get_Descriptor命令, 此时将wLength设置为wTotalLength的值, 从而获得整个配置描述报文信息.

    USB HID的类命令

    HID设备除了支持标准的USB命令外, 还支持6个HID特定的类命令, 如下表所示.

    命令 请求号 功能描述
    Get_Report 0x01 USB主机接收HID设备发来的报告
    Get_Idle 0x02 用于读取HID设备当前空闲速率
    Get_Protocol 0x03 用于读取HID设备的协议值
    Set_Report 0x09 USB主机向HID设备发送报告
    Set_Idle 0x0A 用于设置HID设备的空闲速率
    Set_Protocol 0x0B 用于设置HID设备的协议值

    HID的类命令, 其数据结构与USB的标准命令类似, 而且也是采用控制传输发送的. HID示例中主要用到了 Get_Report 和 Set_Report 两个命令

    Get_Report命令

    用于获取HID设备发送来的报告, 它主要在HID设备初始化和读取HID报告时使用. 此命令是所有HID设备都必须支持的, 其结构如下所示.

    偏移 字段 内容
    0 bmRequesType 值为10100001B, 设备到主机
    1 bRequest GET_REPORT, 0x01
    2 wValue 报告类型及报告ID
    4 wIndex 用于指明支持此命令的接口号码
    6 wLength 报告长度

    其中, wValue用来指明报告的类型. 它由两个字节组成, 低字节表示报告ID. 高字节值为1时, 表示Input报告;值为2时, 表示Output报告;值为3时, 表示Feature报告.

    Set_Report命令

    用于USB主机向HID设备发送报告数据, 它与Get_Report命令类似, 只是数据传输的方向不同. Set_Report命令并不是所有HID设备都必须支持的, 其结构如下所示.

    偏移 字段 内容
    0 bmRequesType 值为00100001B, 主机到设备
    1 bRequest SET_REPORT, 0x09
    2 wValue 报告类型及报告ID
    4 wIndex 用于指明支持此命令的接口号码
    6 wLength 报告长度

    USB 的描述报文

    USB 设备响应命令的内容就是描述报文, 为了方便USB主机对USB设备进行管理, USB-IF对USB设备的功能采用了分层结构, 包括设备层、配置层、接口层和端点层. 这四层的作用分别为

    1. 设备层. 说明USB设备的主要类型特征(如设备类别、接口、端点等属性), 保障设备枚举过程的正常进行
    2. 配置层. 选择不同的失败配置满足USB主机对设备功能的选择, 可选择复式的设备接口功能, 如图1展示的选择鼠标、键盘和游戏杆的复合功能
    3. 接口层. 将具体功能分类, 不同的功能对用不同的操作方式
    4. 端点层. 针对特定的设备功能, 选择不同的端点, 提供不同的数据管道, 与USB主机进行数据通讯

    为了描述USB设备的这些特征, USB规范定义了相应结构的描述报文, 包括设备描述报文、配置描述报文、接口描述报文、端点描述报文和字符串描述报文等. 下表给出了USB1.1下各种USB描述报文的类型值.

    类型 描述报文 描述报文值
    标准描述报文 设备描述报文(Device Descriptor) 0x01
    配置描述报文(Configuration Descriptor) 0x02
    字符串描述报文(String Descriptor) 0x03
    接口描述报文(Interface Descriptor) 0x04
    端点描述报文(Endpoint Descriptor) 0x05
    类描述报文 集线器类描述报文(hub descriptor) 0x29
    人机接口类描述报文(HID) 0x21
    厂商自定义 0xFF

    其他版本的USB规范, 还定义了其他类型的描述报文, 比如USB 2.0中的设备限定描述报文(Device_Qualifier), USB 3.2中的二进制设备对象存储描述报文(Binary Device Object Store, 简称BOS)等. 这些内容在博客中不会涉及, 可在USB-IF的官网下载相关的USB规范文档了解.

    USB主机会发送USB命令给USB设备, GET_DESCRIPTOR 是常用的获取描述报文命令. USB设备根据命令所要求的, 给出对应的描述报文. 本篇主要讲述描述报文的结构.

    不管哪种USB设备, 都必须提供标准描述报文, 用于告知主机设备本身的属性. 以下介绍的内容, 可以使用Bus Hound或USB逻辑分析仪等工具, 去抓取USB设备的识别和通信包.

    设备描述报文

    USB的设备描述报文用于表示USB设备的一般信息, 如制造商ID、产品序列号等. 一个USB设备有且只有一个设备描述报文, 它是USB主机所读取的第一个描述报文, 其结构如下所示.

    偏移 大小 描述
    0 bLength 1 数字 描述报文字节数长度(0x12)
    1 bDescriptor 1 常量 描述报文的类型(0x01)
    2 bcdUSB 2 BCD码 USB设备支持的协议版本号
    4 bDeviceClass 1 设备类代码
    5 bDeviceSubClass 1 子类 子类代码, 根据bDeviceClass来定
    6 bDevicePortocol 1 协议 协议码
    7 bMaxPacketSize0 1 数字 端点0的最大包长度
    8 idVendor 2 ID 厂商ID(由USB-IF赋值)
    10 idProduct 2 ID 产品ID(由厂商赋值)
    12 bcdDevice 2 BCD码 设备发行号(BCD码)
    14 iManufacturer 1 索引 厂商信息的字符串描述报文索引值
    15 iProduct 1 索引 产品信息的字符串描述报文索引值
    16 iSerialNumber 1 索引 设备序列号信息的字符串描述报文索引值
    17 bNumConfigurations 1 数字 配置描述报文数目

    设备描述报文结构中的

    • bMaxPacketSize0, 它用来告知U
  • 相关阅读:
    【环境配置笔记】基于clang15搭建liunx内核代码阅读环境
    YOLOv7改进:CBAM注意力机制
    绕不过的并发编程--synchronized原理
    蓝桥杯官网练习题(上三角方阵)
    Buffer Pool详解
    【趣味实践】自动补帧算法——RIFE的使用
    位图+布隆过滤器+海量数据问题(它们都是哈希的应用)
    javascript跨域传输数据的设置和兼容浏览函数代码
    DM8 数据库主备/读写分离集群增加节点
    上海亚商投顾:沪指探底回升 华为汽车概念股集体大涨
  • 原文地址:https://blog.csdn.net/tianpu2320959696/article/details/126594080