STM32F103ZET(基于秉火开发板)+Cubemx(F1 V1.60库)+RTC(修复官方例程日期不更新bug)

HaoYou 回复了问题 • 10 人关注 • 6 个回复 • 2004 次浏览 • 2018-09-11 00:04 • 来自相关话题

stm32实现SSI接口

乌龟也会飞 发表了文章 • 1 个评论 • 169 次浏览 • 2018-08-22 13:53 • 来自相关话题

本文章采用的是2个IO口来模拟SSI协议接口读取MTS的绝对值编码器数据,经过验证一切oK第一步:首先需要配置io口一个输出 一个为输入第二步:定义较为准确的微秒级别的延时(SSI协议的对延时有较高的要求,两帧数据之间的间隔要求大于16us最好大于1倍以上32us比较保险,故此读取SSI编码器的数据时钟周期必须要小于16us,否则SSI编码器不停的认为开始了新的一帧数据发送,会致使结果读取失败)。可以采用定时器的方式来实现较为准确的延时可以参考http://www.stm32cube.com/article/176 文章 第三步:实现SSI协议接口(具体下一请百度)大概就是空闲没有数据交换时刻数据线和时钟线应该维持为高电平的状态 在第一个时钟下降沿时刻编码器锁存当前的位移值,然后再接下来的每个上升沿脉冲时刻将数据发送发送到数据线上,发送完成后SSI编码器会将数据线拉低16us然后恢复为空闲状态拉高数据线,故此两针数据的读取间隔必须大于16us我采用的是30us,然后空闲手机壳遇到时钟下降沿又是新的一帧数据开始我所实现的MTS传感器的24位数据(故此以下代码为24位SSI编码器读取代码,至于其他位数的编码器时序相同的话只需更改for循环的次数即可)以上提供by荒野大嫖客 如有疑问可以留言或者至cubeMX中文QQ群里 里面的大牛较多以下为FPGA实现SSI接口by 荒野大嫖客 查看全部

本文章采用的是2个IO口来模拟SSI协议接口读取MTS的绝对值编码器数据,经过验证一切oK

第一步:首先需要配置io口一个输出 一个为输入

第二步:定义较为准确的微秒级别的延时(SSI协议的对延时有较高的要求,两帧数据之间的间隔要求大于16us最好大于1倍以上32us比较保险,故此读取SSI编码器的数据时钟周期必须要小于16us,否则SSI编码器不停的认为开始了新的一帧数据发送,会致使结果读取失败)。可以采用定时器的方式来实现较为准确的延时可以参考

http://www.stm32cube.com/article/176 文章 

第三步:实现SSI协议接口(具体下一请百度)大概就是空闲没有数据交换时刻数据线和时钟线应该维持为高电平的状态 在第一个时钟下降沿时刻编码器锁存当前的位移值,然后再接下来的每个上升沿脉冲时刻将数据发送发送到数据线上,发送完成后SSI编码器会将数据线拉低16us然后恢复为空闲状态拉高数据线,故此两针数据的读取间隔必须大于16us我采用的是30us,然后空闲手机壳遇到时钟下降沿又是新的一帧数据开始

我所实现的MTS传感器的24位数据(故此以下代码为24位SSI编码器读取代码,至于其他位数的编码器时序相同的话只需更改for循环的次数即可)


image.png

以上提供by荒野大嫖客 如有疑问可以留言或者至cubeMX中文QQ群里 里面的大牛较多

以下为FPGA实现SSI接口

image.png


image.png


by 荒野大嫖客


STM32 HAL库实现微秒级别延时

乌龟也会飞 发表了文章 • 2 个评论 • 320 次浏览 • 2018-08-22 13:30 • 来自相关话题

HAL库函数中有延时函数HAL_Delay();进行毫秒级别的延时,但是在实际的开发中编写时序时有时需要进行较为准确的微秒级别延时像SSI协议接口等,本文章采用一个通用定时器的方式实现微秒级别的延时,而不采用滴答定时器是由于HAL库函数中很多地方需要使用滴答定时器进行超时判断,更改滴答配置恐引发意外,另外传统的for循环延时不太准确。首先配置使能定时器(我选择的是定时器6)第二步:配置定时器(当然在此步骤前默认读者已经完成了系统时钟的配置,F1系列我配置为72MHz,那么对于定时器6来说时钟也是72MHz)进行72分频(71+1)那么得出分频后定时器的时钟为1MHz即1us进行数据变化一次,根据这个思想,我将定时器配置为向上计数,自动重装,好方目前位置cubeMX配置方面已经完成了,生成工程就行第三步:打开工程 在tim.c 文本中的begin 和end之间添加以下代码即可/*********************************************功能:实现us级别延时***作者:荒野大嫖客***版本:V1.0*******************************************/void delayXus(uint16_t us){ uint16_t differ=0xffff-us-5; //设定定时器计数器起始值 __HAL_TIM_SET_COUNTER(&htim6,differ); HAL_TIM_Base_Start(&htim6); //启动定时器  while(differ<0xffff-6) //补偿,判断  {    differ=__HAL_TIM_GET_COUNTER(&htim6); //查询计数器的计数值  }  HAL_TIM_Base_Stop(&htim6);}第四步:将void delayXus(uint16_t us);添加至tim.h头文件中 在其他地方引用该头文件即可使用该延时,经过实际测试可得还是比较准备的以上整理提供by 荒野大嫖客 有问题可以一起探讨探讨或者cubemx中文QQ群发表 里面大牛比较多 查看全部

HAL库函数中有延时函数HAL_Delay();进行毫秒级别的延时,但是在实际的开发中编写时序时有时需要进行较为准确的微秒级别延时像SSI协议接口等,本文章采用一个通用定时器的方式实现微秒级别的延时,而不采用滴答定时器是由于HAL库函数中很多地方需要使用滴答定时器进行超时判断,更改滴答配置恐引发意外,另外传统的for循环延时不太准确。

首先配置使能定时器(我选择的是定时器6)

image.png

第二步:配置定时器(当然在此步骤前默认读者已经完成了系统时钟的配置,F1系列我配置为72MHz,那么对于定时器6来说时钟也是72MHz)

image.png

进行72分频(71+1)那么得出分频后定时器的时钟为1MHz即1us进行数据变化一次,根据这个思想,我将定时器配置为向上计数,自动重装,好方目前位置cubeMX配置方面已经完成了,生成工程就行

第三步:打开工程 在tim.c 文本中的begin 和end之间添加以下代码即可

/******************************************

***功能:实现us级别延时

***作者:荒野大嫖客

***版本:V1.0

*******************************************/

void delayXus(uint16_t us)

{

uint16_t differ=0xffff-us-5; //设定定时器计数器起始值

__HAL_TIM_SET_COUNTER(&htim6,differ);

HAL_TIM_Base_Start(&htim6); //启动定时器

  while(differ<0xffff-6) //补偿,判断

  {

    differ=__HAL_TIM_GET_COUNTER(&htim6); //查询计数器的计数值

  }

  HAL_TIM_Base_Stop(&htim6);

}

第四步:将void delayXus(uint16_t us);添加至tim.h头文件中 在其他地方引用该头文件即可使用该延时,经过实际测试可得还是比较准备的


以上整理提供by 荒野大嫖客 有问题可以一起探讨探讨或者cubemx中文QQ群发表 里面大牛比较多


关于stm32cube中UART串口的新库函数的一些东西理解,长久记录

風影旋新月 回复了问题 • 26 人关注 • 10 个回复 • 26553 次浏览 • 2018-08-20 16:24 • 来自相关话题

(直播)基于Freemodbus的灵活应用--Freemodbus的使用技巧和简单改造

zjczm 回复了问题 • 17 人关注 • 13 个回复 • 5424 次浏览 • 2018-07-30 18:03 • 来自相关话题

查表法CRC16和CRC8校验程序,HAL库中的CRC32不能用于modbus校验

回复

匿名用户 回复了问题 • 4 人关注 • 1 个回复 • 2930 次浏览 • 2018-07-24 15:49 • 来自相关话题

(转)CPU时钟调高时出现异常的案例分享

admin 发表了文章 • 1 个评论 • 225 次浏览 • 2018-06-17 09:55 • 来自相关话题

近日某论坛一STM32用户反馈,使用STM32F103内部时钟,把系统时钟配置成64MHz单片机就不跑了,配置成36MHz程序就正常妥妥的,频率稍高点就容易导致死机。他贴出的代码如下:void RCC_Configuration(void) 

 RCC_DeInit();//将外设 RCC寄存器重设为缺省值 
RCC_HSICmd(ENABLE);//使能HSI   
while(RCC_GetFlagStatus(RCC_FLAG_HSIRDY) == RESET);//等待HSI使能成功 
//FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); 
//FLASH_SetLatency(FLASH_Latency_2); 
RCC_HCLKConfig(RCC_SYSCLK_Div1);    
RCC_PCLK1Config(RCC_HCLK_Div2); 
RCC_PCLK2Config(RCC_HCLK_Div1); 
//设置 PLL 时钟源及倍频系数 
RCC_PLLConfig(RCC_PLLSource_HSI_Div2, RCC_PLLMul_16);
。。。。。。结合他的问题描述及他贴出来的代码,大致可以判断出很可能是因为他屏蔽了指令预取和flash读取等待延迟的参数配置而导致的异常。即上面两条红色标注出来的代码。后来我明确地提醒他这点后,他似乎并没及时反应过来,还折腾了几下才开启了上述配置,问题最终得以解决。其实,关于这个问题经常有人遇到,尤其是那些基于STM32标准固件库进行开发或自行编程时的新手更容易碰到这个问题。主要原因是他们对上述两行代码的功能不了解,导致有意或无意的将库例程中相关代码屏蔽掉无视掉不做配置、或者配置不正确。这里将这个问题再次分享出来,对上面两行代码简单做些解释。希望更多人对此有所知晓,少在这个地方走弯路。这句FLASH_PrefetchBufferCmd();用来作为flash指令预取功能的使能或禁用。现有STM32各个系列都是基于ARM cortexM内核的微处理器,采用多级流水线的哈佛结构,即一条指令的执行分割为几个阶段,如取指、译码、执行等,使得当前指令的取指操作完成后就可以开始后续指令的取指、译码等操作,程序指令就这样像流水一样执行下去,大大提高了指令的执行效率。具体到STM32各系列单片机,这个指令预取功能的开启或关闭可以软件配置,一般配置为开启。要注意的是,芯片复位后不同的系列该功能有的默认为开启有的则默认为关闭。比方STM32F1系列的flash指令预取功能就是默认打开的,当然你也可以关闭。其中,明确要求打开的情景就是当那个AHB时钟预分频系数不等于1时。再比如STM32F4系列,它的指令预取功能在芯片复位后是默认关闭的,你可以自行打开。但明确要求关闭的场景就是芯片的供电电压低于2.1V时。其实,STM32F4的预取功能与STM32F1不尽一样,STM32F4、STM32F2、STM32L4、STM32F7等系列芯片使用了ST的专利技术ART存储加速器【Adaptive real-time memory accelerator】。该加速器使用指令预取队列和分支跳转缓存技术,从而提高 Flash 程序代码执行速度,使得CPU即使在其最高主频下也能完美实现0等待执行flash程序指令。上面大致讲了指令预取功能,预取主要是为了实现指令读取和执行的高效性。具体细节请参考相关技术手册。我们知道CPU的运行速度可调,可以很快,通常使用高速总线访问FLASH接口控制器,FLASH控制器收到来自CPU的取指指令后然后去读取相应地址的指令或数据。Flash控制器自身的读取速度相比CPU的高速请求来说可能会出现滞后,往往需要CPU做相应的延时等待。为了让CPU准确及时读取 Flash 数据,我们须根据 CPU 时钟频率、FLASH控制器自身特性以及器件供电情况在Flash存取控制寄存器(FLASH_ACR)中正确地编程等待周期数(LATENCY),类似上面提到的第二句代码:FLASH_SetLatency(FLASH_Latency_n);这里的等待周期数视不同的STM32系列也各有差异,不妨以STM32F4为例:下面是个关于STM32F4系列部分产品线的LATENCY设置的表格。从表格中可以看出LATENCY参数的设置与CPU的时钟、电源电压都有关系。另外,当电源电压在2.1V以下上要关闭预取功能。在设置上面的等待周期参数时,选择合适的就好。不难理解,设置太大了影响CPU性能的充分发挥,太小了容易导致异常。具体回到开头的案例,它出现死机问题,极可能是因为没有合理配置等待周期参数导致异常,因为它屏蔽了参考例程中那两句配置代码,即使用其默认功能,对于STM32F1,指令预取功能默认为开启。而STM32F1系列芯片的latency默认值即为0,无等待。这样的话,当他把时钟调高到一定程度时出现死机就不难理解了。另外,当他反馈时钟调高产生异常时,我还给他提醒了注意检查VDDA的电源情况。我碰到有人遇到因VDDA没接好使得PLL不正常的情况。我们知道,对于STM32芯片,调高其工作时钟,往往借助于锁相环。而PLL的供电来自VDDA,如果PLL没有被正常供电,也是个非常隐蔽的麻烦。曾经有个客户为此折腾好久,才愿沉下心来检查其“坏品”的电源,结果发现有个VDDA脚虚焊。一直以芯片低频没问题,频率高了就异常为由怀疑芯片品质问题而耽误时间。最后给点建议,做STM32开发的话,尤其是新手,如果参照ST的官方例程的话,有些配置在没看懂的情况下不要轻易屏蔽或修改。我碰到多个类似本案随意屏蔽例程中的初始化配置代码或断言代码出现异常,自己又找不到方向的。另外,尽可能使用ST官方的stm32cubeMx图形配置工具做基本的配置,通过它来生成初始化配置文件,这样方便省事很多。当然,即使使用STM32CUBEMX配置也不是万能的。比方:曾经有人使用STM32F0开发产品,用CUBEMX配置初始化文件,刚开始配置时时钟选择得比较低, STM32CubeMx自然根据他选择的时钟做了相关参数配置。后来他自己在用户代码里手动调高了时钟,而不知相应调整跟FLASH读取等待有关的参数,也是发生跟本案同样的情况。所以呢,如果能对原理有更多更深的把握那是再好不过了。本文转自:https://mp.weixin.qq.com/s/E29t7t5YuLFZtHYWcwguJQ 感谢【ST MCU 信息交流】的经验分享。 查看全部

近日某论坛一STM32用户反馈,使用STM32F103内部时钟,把系统时钟配置成64MHz单片机就不跑了,配置成36MHz程序就正常妥妥的,频率稍高点就容易导致死机。他贴出的代码如下:

void RCC_Configuration(void) 

 RCC_DeInit();//将外设 RCC寄存器重设为缺省值 
RCC_HSICmd(ENABLE);//使能HSI   
while(RCC_GetFlagStatus(RCC_FLAG_HSIRDY) == RESET);//等待HSI使能成功 
//FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); 
//FLASH_SetLatency(FLASH_Latency_2); 
RCC_HCLKConfig(RCC_SYSCLK_Div1);    
RCC_PCLK1Config(RCC_HCLK_Div2); 
RCC_PCLK2Config(RCC_HCLK_Div1); 
//设置 PLL 时钟源及倍频系数 
RCC_PLLConfig(RCC_PLLSource_HSI_Div2, RCC_PLLMul_16);
。。。。。。

结合他的问题描述及他贴出来的代码,大致可以判断出很可能是因为他屏蔽了指令预取和flash读取等待延迟的参数配置而导致的异常。即上面两条红色标注出来的代码。


后来我明确地提醒他这点后,他似乎并没及时反应过来,还折腾了几下才开启了上述配置,问题最终得以解决。

其实,关于这个问题经常有人遇到,尤其是那些基于STM32标准固件库进行开发或自行编程时的新手更容易碰到这个问题。主要原因是他们对上述两行代码的功能不了解,导致有意或无意的将库例程中相关代码屏蔽掉无视掉不做配置、或者配置不正确。

这里将这个问题再次分享出来,对上面两行代码简单做些解释。希望更多人对此有所知晓,少在这个地方走弯路。

这句

FLASH_PrefetchBufferCmd();

用来作为flash指令预取功能的使能或禁用。

现有STM32各个系列都是基于ARM cortexM内核的微处理器,采用多级流水线的哈佛结构,即一条指令的执行分割为几个阶段,如取指、译码、执行等,使得当前指令的取指操作完成后就可以开始后续指令的取指、译码等操作,程序指令就这样像流水一样执行下去,大大提高了指令的执行效率。


具体到STM32各系列单片机,这个指令预取功能的开启或关闭可以软件配置,一般配置为开启。要注意的是,芯片复位后不同的系列该功能有的默认为开启有的则默认为关闭。比方STM32F1系列的flash指令预取功能就是默认打开的,当然你也可以关闭。其中,明确要求打开的情景就是当那个AHB时钟预分频系数不等于1时。

捕获7.PNG

再比如STM32F4系列,它的指令预取功能在芯片复位后是默认关闭的,你可以自行打开。但明确要求关闭的场景就是芯片的供电电压低于2.1V时。

其实,STM32F4的预取功能与STM32F1不尽一样,STM32F4、STM32F2、STM32L4、STM32F7系列芯片使用了ST的专利技术ART存储加速器【Adaptive real-time memory accelerator】。该加速器使用指令预取队列和分支跳转缓存技术,从而提高 Flash 程序代码执行速度,使得CPU即使在其最高主频下也能完美实现0等待执行flash程序指令。

上面大致讲了指令预取功能,预取主要是为了实现指令读取和执行的高效性。具体细节请参考相关技术手册。我们知道CPU的运行速度可调,可以很快,通常使用高速总线访问FLASH接口控制器,FLASH控制器收到来自CPU的取指指令后然后去读取相应地址的指令或数据。Flash控制器自身的读取速度相比CPU的高速请求来说可能会出现滞后,往往需要CPU做相应的延时等待。为了让CPU准确及时读取 Flash 数据,我们须根据 CPU 时钟频率、FLASH控制器自身特性以及器件供电情况在Flash存取控制寄存器(FLASH_ACR)中正确地编程等待周期数(LATENCY),类似上面提到的第二句代码:

FLASH_SetLatency(FLASH_Latency_n);

这里的等待周期数视不同的STM32系列也各有差异,不妨以STM32F4为例:

捕获8.PNG


下面是个关于STM32F4系列部分产品线的LATENCY设置的表格。从表格中可以看出LATENCY参数的设置与CPU的时钟、电源电压都有关系。另外,当电源电压在2.1V以下上要关闭预取功能。


捕获9.PNG

在设置上面的等待周期参数时,选择合适的就好。不难理解,设置太大了影响CPU性能的充分发挥,太小了容易导致异常。

具体回到开头的案例,它出现死机问题,极可能是因为没有合理配置等待周期参数导致异常,因为它屏蔽了参考例程中那两句配置代码,即使用其默认功能,对于STM32F1,指令预取功能默认为开启。而STM32F1系列芯片的latency默认值即为0,无等待。这样的话,当他把时钟调高到一定程度时出现死机就不难理解了。

另外,当他反馈时钟调高产生异常时,我还给他提醒了注意检查VDDA的电源情况。我碰到有人遇到因VDDA没接好使得PLL不正常的情况。我们知道,对于STM32芯片,调高其工作时钟,往往借助于锁相环。而PLL的供电来自VDDA,如果PLL没有被正常供电,也是个非常隐蔽的麻烦。曾经有个客户为此折腾好久,才愿沉下心来检查其“坏品”的电源,结果发现有个VDDA脚虚焊。一直以芯片低频没问题,频率高了就异常为由怀疑芯片品质问题而耽误时间。

捕获10.PNG


最后给点建议,做STM32开发的话,尤其是新手,如果参照ST的官方例程的话,有些配置在没看懂的情况下不要轻易屏蔽或修改。我碰到多个类似本案随意屏蔽例程中的初始化配置代码或断言代码出现异常,自己又找不到方向的。另外,尽可能使用ST官方的stm32cubeMx图形配置工具做基本的配置,通过它来生成初始化配置文件,这样方便省事很多。当然,即使使用STM32CUBEMX配置也不是万能的。比方:曾经有人使用STM32F0开发产品,用CUBEMX配置初始化文件,刚开始配置时时钟选择得比较低, STM32CubeMx自然根据他选择的时钟做了相关参数配置。后来他自己在用户代码里手动调高了时钟,而不知相应调整跟FLASH读取等待有关的参数,也是发生跟本案同样的情况。所以呢,如果能对原理有更多更深的把握那是再好不过了。


本文转自:https://mp.weixin.qq.com/s/E29t7t5YuLFZtHYWcwguJQ

感谢【ST MCU 信息交流】的经验分享。

(转)HID+CDC复合设备在WIN10的识别问题

admin 发表了文章 • 0 个评论 • 433 次浏览 • 2018-06-17 09:20 • 来自相关话题

1 问题现象有客户使用STM32F405并参照ST官方USB标准库下的HID+CDC的示例代码做产品,发现在WIN7上使用得好好的,可放到WIN10上,CDC第一次能够识别,再次拔插后就不能再识别,且此后无论插拔多少次都无法再识别,除非再次上电,又会重复上述现象,只有板子上电后第一次才能正确被识别,后续均不行。2 问题分析客户使用 ST官方示例代码STM32_USB-Host-Device_Lib_V2.2.0\Project\USB_Device_Examples\Composite_Examples\CDC_HID_Composite(下载页面:http://www.stmicroelectronics.com.cn/content/st_com/en/products/embedded-software/mcus-embedded-software/stm32-embedded-software/stm32-standard-peripheral-library-expansion/stsw-stm32046.html)当我尝试使用此示例代码重现客户所遇到的问题时,发现此代码在WIN7运行OK,但与客户不同的是,我测试到的情况是在WIN10下CDC一次都无法识别,HID却一直可以识别。下面来分析下问题,既然WIN7下HID和CDC都能正常识别,放在WIN10上才不正常,那么初步可以判断,此问题可能与WIN10操作系统的USB主机驱动实现有关。通过USB分析仪分析客户代码在WIN10下USB枚举异常的数据通讯:Figure 1 第一次USB枚举过程上图是客户代码第一次正常枚举的通讯数据,从图中可以看出,WIN10 USB主机在正常获取HID报告描述符后,紧接着会获取虚拟串口状态和设置波特率,这样就正常枚举结束了。我们再来看看采集到的异常USB枚举过程:Figure 2 异常枚举过程上图是WIN10下异常枚举过程。从图中可以看出,WIN10系统上USB主机在获取到设备描述符和配置描述符后直接将设备挂起了。很明显,WIN10系统的USB主机驱动实现对设备描述符或者配置描述符的内容并不认可,才会导致无法识别HID+CDC复合设备。我们不妨检查下客户代码中的设备描述符:Figure 3 获取的设备描述符复合设备的class,subclass,protocol必须为0xef,0x02,0x01,这里VID=0x0483,PID=0x3256(Cube库下为0x5740,但这个不重要),接下来看配置描述符:Figure 4 win10不能识别的配置描述符由此可见,客户的描述符是HID interface + IAD + CDC interfaces结构。对于WIN7,这种结构可以识别,但对于WIN10,这种结构WIN10未必能够兼容,我们尝试在HID interface外部加上一层IAD结构,使其成为IAD1 + HID interface + IAD2 + CDC interfaces结构,此时客户的问题得以解决,在WIN10也可以正确识别了,修改后的描述符结构如下:Figure 5 win10能够正确识别的配置描述符结束本篇实战经验之前,让我们再次回顾IAD的概念:IAD(Interface Association Descriptor),为USB设备定义了一个标准来表述捆绑在一个逻辑功能(比如这里的CDC虚拟串口)上的多个接口的聚合的方法。USB协会分配了一个设备级别的类编码(即图3中0xEF),使用IAD的设备必须使用它(如图3的设备描述符);这样可以很容易在设备枚举时就能识别出采用了IAD的设备。IAD描述符通常放在它所要捆绑的多个接口的接口描述符之前。3 结论 在WIN10系统中,建议复合设备每个逻辑功能的接口描述符前都搭载一个IAD描述符,不论这个逻辑功能是单个接口描述符完成(比如这里的HID功能)还是要由多个接口描述符完成(比如这里的CDC功能)。本文转自:https://mp.weixin.qq.com/s/9ot9AVTN4zSMAyudVDeMzA 感谢【STM32单片机】分享的经验。 查看全部

1 问题现象

有客户使用STM32F405并参照ST官方USB标准库下的HID+CDC的示例代码做产品,发现在WIN7上使用得好好的,可放到WIN10上,CDC第一次能够识别,再次拔插后就不能再识别,且此后无论插拔多少次都无法再识别,除非再次上电,又会重复上述现象,只有板子上电后第一次才能正确被识别,后续均不行。

2 问题分析

客户使用 ST官方示例代码

STM32_USB-Host-Device_Lib_V2.2.0\Project\USB_Device_Examples\Composite_Examples\CDC_HID_Composite

(下载页面:

http://www.stmicroelectronics.com.cn/content/st_com/en/products/embedded-software/mcus-embedded-software/stm32-embedded-software/stm32-standard-peripheral-library-expansion/stsw-stm32046.html)

当我尝试使用此示例代码重现客户所遇到的问题时,发现此代码在WIN7运行OK,但与客户不同的是,我测试到的情况是在WIN10下CDC一次都无法识别,HID却一直可以识别。


下面来分析下问题,既然WIN7下HID和CDC都能正常识别,放在WIN10上才不正常,那么初步可以判断,此问题可能与WIN10操作系统的USB主机驱动实现有关。

通过USB分析仪分析客户代码在WIN10下USB枚举异常的数据通讯:

捕获.PNG

Figure 1 第一次USB枚举过程


上图是客户代码第一次正常枚举的通讯数据,从图中可以看出,WIN10 USB主机在正常获取HID报告描述符后,紧接着会获取虚拟串口状态和设置波特率,这样就正常枚举结束了。我们再来看看采集到的异常USB枚举过程:

捕获2.PNG

Figure 2 异常枚举过程


上图是WIN10下异常枚举过程。从图中可以看出,WIN10系统上USB主机在获取到设备描述符和配置描述符后直接将设备挂起了。很明显,WIN10系统的USB主机驱动实现对设备描述符或者配置描述符的内容并不认可,才会导致无法识别HID+CDC复合设备。

我们不妨检查下客户代码中的设备描述符:

捕获3.PNG

Figure 3 获取的设备描述符


复合设备的class,subclass,protocol

必须为0xef,0x02,0x01,这里

VID=0x0483,PID=0x3256(Cube库下为0x5740,但这个不重要),接下来看配置描述符:

4.jpg

Figure 4 win10不能识别的配置描述符


由此可见,客户的描述符是HID interface + IAD + CDC interfaces结构。对于WIN7,这种结构可以识别,但对于WIN10,这种结构WIN10未必能够兼容,我们尝试在HID interface外部加上一层IAD结构,使其成为IAD1 + HID interface + IAD2 + CDC interfaces结构,此时客户的问题得以解决,在WIN10也可以正确识别了,修改后的描述符结构如下:

5.jpg

Figure 5 win10能够正确识别的配置描述符


结束本篇实战经验之前,让我们再次回顾IAD的概念:

IAD(Interface Association Descriptor),为USB设备定义了一个标准来表述捆绑在一个逻辑功能(比如这里的CDC虚拟串口)上的多个接口的聚合的方法。USB协会分配了一个设备级别的类编码(即图3中0xEF),使用IAD的设备必须使用它(如图3的设备描述符);这样可以很容易在设备枚举时就能识别出采用了IAD的设备。IAD描述符通常放在它所要捆绑的多个接口的接口描述符之前


3 结论

在WIN10系统中,建议复合设备每个逻辑功能的接口描述符前都搭载一个IAD描述符,不论这个逻辑功能是单个接口描述符完成(比如这里的HID功能)还是要由多个接口描述符完成(比如这里的CDC功能)。


本文转自:https://mp.weixin.qq.com/s/9ot9AVTN4zSMAyudVDeMzA

感谢【STM32单片机】分享的经验。

CUBEMX 配置时报引脚冲突,如何解决,这样配置,SPI不能使用,单独配置,SPI与USART均能正常使用

回复

奇缘时间 发起了问题 • 1 人关注 • 0 个回复 • 236 次浏览 • 2018-05-31 09:48 • 来自相关话题

你知道modbus调通并取得数据之后的操作吗?欢迎来讨论freemodbus的应用问题

自由 回复了问题 • 5 人关注 • 4 个回复 • 2209 次浏览 • 2018-05-22 17:52 • 来自相关话题

emwin显示汉字

等。。。待 发表了文章 • 0 个评论 • 611 次浏览 • 2018-04-19 13:49 • 来自相关话题

我们一般用keil编程是,软件的编码模式是GB2312模式,此时emwin显示汉字会显示乱码,无法正常使用。多次实验后,有两种方式可以解决这个问题。第一种:emwin汉字解码用的是UTF-8模式,而我们常用的编码模式是GB2312,二者之间无法使用固定的公式转换,也就是没有直接联系。此时要显示汉字,需要做一个列表,将UTF-8和GB2312两种编码联系起来。    若是将所有的常用汉字列表都写入程序,这个不太现实(文件挺大的),下面是实现方法uint16_t das[23335][2]=//部分列表
{
{0x00,0x0000},
{0x01,0x0001},
{0x02,0x0002},
{0x03,0x0003},
{0x04,0x0004},
{0x05,0x0005},
{0x06,0x0006},
{0x07,0x0007},
{0x08,0x0008},
};
//转换程序
uint16_t Get_GB2312(char *str)
{
uint16_t a=0;
a=(uint8_t)(*str);
a<<=8;
a|=(uint8_t)(*(str+1));
return a;
}

uint16_t GET(char *ds)
{
uint32_t dsa=0;
if(((uint8_t)(*ds))<0x80)
{
return  (uint8_t)(*ds);
}
else  //if (((*ds) &0xf0) && ((*(ds + 1)) &0xf0))
{
dsa=Get_GB2312(ds);
for (i = 0; i < 23335; i++)
{
if (das[i][0] == dsa)
{
return das[i][1];
}
}
}
return 0;
}
const char* GET_UTF1(char *ds)
{

uint8_t *bi1t;
uint16_t dsadd=0;
if(((uint8_t)(*ds))<0x80)
{
return  ds;
}
else  //if (((*ds) &0xf0) && ((*(ds + 1)) &0xf0))
{
dsadd=GET(ds);

bi1t[0] = 0xe0 | ((dsadd >> 12)&0x0f);  //高四位填入1110xxxx 10yyyyyy 10zzzzzz中的xxxx  

bi1t[1] = 0x80 | ((dsadd >> 6) & 0x3f);  //中间6位填入1110xxxx 10yyyyyy 10zzzzzz中的yyyyyy

bi1t[2] = 0x80 | (dsadd & 0x3f);  //低6位填入1110xxxx 10yyyyyy 10zzzzzz中的zzzzzz  
return bi1t;
}
}     再就是只将自己需要的汉字列出来,这样有一个缺点,无法现实任意汉字
 typFNT_GB24 Fontff[100]=
{
{"不","\xe4\xb8\x8d"},
{"同","\xe5\x90\x8c"},
{"期","\xe6\x9c\x9f"},
{"时","\xe6\x97\xb6"},
{"间","\xe9\x97\xb4"},
{"储","\xe5\x82\xa8"},
{"能","\xe8\x83\xbd"},
{"机","\xe6\x9c\xba"},
{"设","\xe8\xae\xbe"},
{"置","\xe7\xbd\xae"},
{"最","\xe6\x9c\x80"},
{"大","\xe5\xa4\xa7"},
{"行","\xe8\xa1\x8c"},
{"程","\xe7\xa8\x8b"},
{"合","\xe5\x90\x88"},
{"分","\xe5\x88\x86"},
{"闸","\xe9\x97\xb8"},
{"电","\xe7\x94\xb5"},
{"流","\xe6\xb5\x81"},
};
uint16_t SearchhzIndex(uint8_t  lo, uint8_t  hi) //查找汉字编码位置
{
    uint16_t i = 0;

for (i = 0; i < sizeof(Fontff); i++)
{
if ((Fontff[i].Index[0] == lo) && (Fontff[i].Index[1] == hi))
{
return i;
}
}
    return 0;
}第二种:将软件编码模式改成UTF-8,这样有一个坏处,写程序的时候会出现莫名其妙的问题 查看全部

我们一般用keil编程是,软件的编码模式是GB2312模式,此时emwin显示汉字会显示乱码,无法正常使用。多次实验后,有两种方式可以解决这个问题。

第一种:emwin汉字解码用的是UTF-8模式,而我们常用的编码模式是GB2312,二者之间无法使用固定的公式转换,也就是没有直接联系。此时要显示汉字,需要做一个列表,将UTF-8和GB2312两种编码联系起来。

    若是将所有的常用汉字列表都写入程序,这个不太现实(文件挺大的),下面是实现方法

uint16_t das[23335][2]=//部分列表
{
{0x00,0x0000},
{0x01,0x0001},
{0x02,0x0002},
{0x03,0x0003},
{0x04,0x0004},
{0x05,0x0005},
{0x06,0x0006},
{0x07,0x0007},
{0x08,0x0008},
};
//转换程序
uint16_t Get_GB2312(char *str)
{
uint16_t a=0;
a=(uint8_t)(*str);
a<<=8;
a|=(uint8_t)(*(str+1));
return a;
}

uint16_t GET(char *ds)
{
uint32_t dsa=0;
if(((uint8_t)(*ds))<0x80)
{
return  (uint8_t)(*ds);
}
else  //if (((*ds) &0xf0) && ((*(ds + 1)) &0xf0))
{
dsa=Get_GB2312(ds);
for (i = 0; i < 23335; i++)
{
if (das[i][0] == dsa)
{
return das[i][1];
}
}
}
return 0;
}
const char* GET_UTF1(char *ds)
{

uint8_t *bi1t;
uint16_t dsadd=0;
if(((uint8_t)(*ds))<0x80)
{
return  ds;
}
else  //if (((*ds) &0xf0) && ((*(ds + 1)) &0xf0))
{
dsadd=GET(ds);

bi1t[0] = 0xe0 | ((dsadd >> 12)&0x0f);  //高四位填入1110xxxx 10yyyyyy 10zzzzzz中的xxxx  

bi1t[1] = 0x80 | ((dsadd >> 6) & 0x3f);  //中间6位填入1110xxxx 10yyyyyy 10zzzzzz中的yyyyyy

bi1t[2] = 0x80 | (dsadd & 0x3f);  //低6位填入1110xxxx 10yyyyyy 10zzzzzz中的zzzzzz  
return bi1t;
}
}

     再就是只将自己需要的汉字列出来,这样有一个缺点,无法现实任意汉字


 typFNT_GB24 Fontff[100]=
{
{"不","\xe4\xb8\x8d"},
{"同","\xe5\x90\x8c"},
{"期","\xe6\x9c\x9f"},
{"时","\xe6\x97\xb6"},
{"间","\xe9\x97\xb4"},
{"储","\xe5\x82\xa8"},
{"能","\xe8\x83\xbd"},
{"机","\xe6\x9c\xba"},
{"设","\xe8\xae\xbe"},
{"置","\xe7\xbd\xae"},
{"最","\xe6\x9c\x80"},
{"大","\xe5\xa4\xa7"},
{"行","\xe8\xa1\x8c"},
{"程","\xe7\xa8\x8b"},
{"合","\xe5\x90\x88"},
{"分","\xe5\x88\x86"},
{"闸","\xe9\x97\xb8"},
{"电","\xe7\x94\xb5"},
{"流","\xe6\xb5\x81"},
};
uint16_t SearchhzIndex(uint8_t  lo, uint8_t  hi) //查找汉字编码位置
{
    uint16_t i = 0;

for (i = 0; i < sizeof(Fontff); i++)
{
if ((Fontff[i].Index[0] == lo) && (Fontff[i].Index[1] == hi))
{
return i;
}
}
    return 0;
}

第二种:将软件编码模式改成UTF-8,这样有一个坏处,写程序的时候会

出现莫名其妙的问题


关于SD-Fatfs

等。。。待 发表了文章 • 3 个评论 • 497 次浏览 • 2018-04-19 13:27 • 来自相关话题

cubemx更新后,关于SD-Fatfs的生成代码无法直接使用,需要加两个回调函数才能正常使用void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
{
  BSP_SD_WriteCpltCallback();
}
void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)
{
  BSP_SD_ReadCpltCallback();
} 查看全部

cubemx更新后,关于SD-Fatfs的生成代码无法直接使用,需要加两个回调函数才能正常使用

void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
{
  BSP_SD_WriteCpltCallback();
}
void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)
{
  BSP_SD_ReadCpltCallback();
}


stm32cubemx配置的stm32的IIC死锁问题解决办法

aiqinhaii 回复了问题 • 5 人关注 • 5 个回复 • 1867 次浏览 • 2018-03-28 09:25 • 来自相关话题

KEIL转visual studio工具

pkokoc 回复了问题 • 10 人关注 • 6 个回复 • 3524 次浏览 • 2018-03-26 20:27 • 来自相关话题

分享一个串口的使用技巧(单字节中断后续字节处理)

skyhahaha 回复了问题 • 8 人关注 • 4 个回复 • 3040 次浏览 • 2018-02-08 11:44 • 来自相关话题