1.#include "STC8H.H" //包含此头文件后,不需要再包含"reg51.h"头文件;#include "reg51.h"//包含此头文件;#include "reg51.h" //包含此头文件;必须增加“引号”才可以,不然报错;
2.使用ADC1进行8位ADC转换, 主动查询(polling)方式:
下面的例子, 使用主动查询的方式每隔0.1秒对P1.1口进行ADC转换, 精度8位, 将结果输出至串口
main.c代码
- #include "fw_hal.h"
-
- void main(void)
- {
- uint8_t res;
- // 调整系统频率, 如果使用STC-ISP设定频率, 需要将这行注释掉
- SYS_SetClock();
- // 用于结果输出
- UART1_Config8bitUart(UART1_BaudSource_Timer2, HAL_State_ON, 115200);
- // 将 ADC1(GPIO P1.1) 设为高阻输入
- GPIO_P1_SetMode(GPIO_Pin_1, GPIO_Mode_Input_HIP);
- // 使用通道: ADC1
- ADC_SetChannel(0x01);
- // 设置ADC时钟 = SYSCLK / 2 / (1+1) = SYSCLK / 4
- ADC_SetClockPrescaler(0x01);
- // 设置结果左对齐, 只需要取值 ADC_RES
- ADC_SetResultAlignmentLeft();
- // 开启ADC电源
- ADC_SetPowerState(HAL_State_ON);
-
- while(1)
- {
- // 开始转换
- ADC_Start();
- // 等待两个系统时钟
- NOP();
- NOP();
- // 检查转换结果标志位是否置位
- while (!ADC_SamplingFinished());
- // 清除结果标志位
- ADC_ClearInterrupt();
- // 读取结果
- res = ADC_RES;
-
- // 通过串口1输出
- UART1_TxString("Result: ");
- UART1_TxHex(res);
- UART1_TxString("\r\n");
- // 等待100ms后再次进行转换
- SYS_Delay(100);
- }
- }
3.使用ADC1进行10位/12位ADC转换, 中断(interrupt)方式
下面的例子, 使用中断的方式对P1.1口进行ADC连续转换, 精度10位(或12位, MCU型号不同精度不同), 每隔0.1秒将结果输出至串口
- #include "fw_hal.h"
-
- // 16位变量用于记录转换结果
- uint16_t res;
-
- // 处理中断的方法, 使用宏定义保证Keil C51和SDCC的兼容性
- INTERRUPT(ADC_Routine, EXTI_VectADC)
- {
- // 先清除中断位
- ADC_ClearInterrupt();
- // 结果低8位
- res = ADC_RESL;
- // 结果高8位
- res |= (ADC_RES & 0x0F) << 8;
- // 再次启动, 使得ADC连续转换,
- ADC_Start();
- }
-
- void main(void)
- {
- // 设置系统频率
- SYS_SetClock();
- // 结果输出
- UART1_Config8bitUart(UART1_BaudSource_Timer2, HAL_State_ON, 115200);
- // 设置P11高阻输入模式
- GPIO_P1_SetMode(GPIO_Pin_1, GPIO_Mode_Input_HIP);
- // 使用通道: ADC1
- ADC_SetChannel(0x01);
- // ADC时钟 = SYSCLK / 2 / (1+15) = SYSCLK / 32
- ADC_SetClockPrescaler(0x0F);
- // 右对齐, 方便转换为双字节的结果
- ADC_SetResultAlignmentRight();
- // 开启全局中断和ADC中断
- EXTI_Global_SetIntState(HAL_State_ON);
- EXTI_ADC_SetIntState(HAL_State_ON);
- // 开启ADC电源
- ADC_SetPowerState(HAL_State_ON);
- // 开始ADC转换
- ADC_Start();
-
- while(1)
- {
- // 转换结果输出
- UART1_TxString("Result: ");
- UART1_TxHex(res >> 8);
- UART1_TxHex(res & 0xFF);
- UART1_TxString("\r\n");
- SYS_Delay(100);
- }
- }
4.使用ADC1, ADC2双通道进行转换, 中断(interrupt)方式
下面介绍一个更实用的例子, 中断形式进行多通道ADC转换, 可以用于无线小车遥控, 双声道音频采样等
- #include "fw_hal.h"
-
- // 用于记录当前采样的通道编号
- uint8_t pos;
- // 记录各通道的采样结果
- uint16_t res[2];
-
- // 中断处理方法
- INTERRUPT(ADC_Routine, EXTI_VectADC)
- {
- ADC_ClearInterrupt();
- // 记录采样结果
- res[pos] = ADC_RESL;
- res[pos] |= (ADC_RES & 0x0F) << 8;
-
- // 切换到下一个通道
- pos = (pos+1) & 0x1;
- if (pos == 0)
- {
- /**
- * 在采样频率较高时, 加上这两句能提高精度. 其机制是切换到开漏模式清除采样口上的残留电压
- GPIO_P1_SetMode(GPIO_Pin_1, GPIO_Mode_InOut_OD);
- GPIO_P1_SetMode(GPIO_Pin_1, GPIO_Mode_Input_HIP);
- */
- ADC_SetChannel(0x01);
- }
- else
- {
- /**
- * Uncomment these lines in high speed ADC
- GPIO_P1_SetMode(GPIO_Pin_2, GPIO_Mode_InOut_OD);
- GPIO_P1_SetMode(GPIO_Pin_2, GPIO_Mode_Input_HIP);
- */
- ADC_SetChannel(0x02);
- }
- ADC_Start();
- }
-
- // 下面的代码和前面的基本上是一样的, 就不详细注释了
- void main(void)
- {
- SYS_SetClock();
- // For debug print
- UART1_Config8bitUart(UART1_BaudSource_Timer2, HAL_State_ON, 115200);
- // Channel: ADC1
- ADC_SetChannel(0x01);
- // ADC Clock = SYSCLK / 2 / (1+15) = SYSCLK / 32
- ADC_SetClockPrescaler(0x0F);
- // Right alignment, high 2-bit in ADC_RES, low 8-bit in ADC_RESL
- ADC_SetResultAlignmentRight();
- // Enable interrupts
- EXTI_Global_SetIntState(HAL_State_ON);
- EXTI_ADC_SetIntState(HAL_State_ON);
- // Turn on ADC power
- ADC_SetPowerState(HAL_State_ON);
- // Set ADC1(P1.1), ADC2(P1.2) HIP
- GPIO_P1_SetMode(GPIO_Pin_1|GPIO_Pin_2, GPIO_Mode_Input_HIP);
- // Start ADC
- ADC_Start();
-
- while(1)
- {
- UART1_TxString("Result: ");
- UART1_TxHex(res[0] >> 8);
- UART1_TxHex(res[0] & 0xFF);
- UART1_TxChar(' ');
- UART1_TxHex(res[1] >> 8);
- UART1_TxHex(res[1] & 0xFF);
- UART1_TxString("\r\n");
- SYS_Delay(100);
- }
- }
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 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 HID设备的传输中主要使用的是控制传输.
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 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 | 描述报文的长度 |
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-IF对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 | 数字 | 配置描述报文数目 |
设备描述报文结构中的