国产单片机替代-CH32替代STM32

请保留-> 【原文:  https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】

随着芯片价格疯涨,项目的不可控性越来越大。特别是价格方面,达到了无法想象的地步了。

以之前《UEFI编程实践》所用的YIE002开发板为例,当时选择使用STM32F103C8T6,也是因为它是一款性价比较高的MCU。当然,也有我之前用这款CPU做过几个项目,比较熟悉的原因在。

按我的记忆,之前项目中所用的STM32F103C8T6,价格在9元左右;而现在到立创商城上去查,单片价格到了惊人的109元!十几倍的涨幅,哪个项目还敢用它?

因此,大部分公司,都在准备各种替代方案。

我们也一样,预备使用CH32F103C8T6替代STM32F103C8T6。这两种芯片引脚兼容,内部的资源差不多,理论上代码移植也比较方便。

我就是这么想的,然后就被打脸了。

最大的原因在于,厂家提供的资料太少了!编程相关的CH32F103应用手册,只有短短的31页。我想看的USB设备控制器的寄存器细节,甚至都没有。想想STM32丰富的应用资料、例程和各种视频,感觉从新手级难度到了骨灰级难度了。

不过,再想想CH32这友好的价格,也就释然了。

周末两天,把之前的USB HID通信,在CH32F103C8T6上实现了,估计不久能很快地应用到项目中去。

预计也也有不少网友有类似的需求,我把探索的过程记录下来。

1 固件下载

CH32F103的芯片,支持WCH-Link或者其他SW仿真工具下载,也支持使用WCHISPTool通过USB和串口下载。考虑到后续开发的时候需要调试,我使用的是WCH-Link进行下载。

如图1所示,给出了WCH-Link的实物图(摘自《WCH-Link使用说明-V1.3》)。

图1 WCH-Link实物

由于我的目标是使用它下载程序到CH32F103C8T6中,只需要使用ARM模式就行了,不需要关注RISC-V模式。

继续阅读“国产单片机替代-CH32替代STM32”

3,546 total views, 2 views today

YIE002开发探索-Gitee代码仓库说明

请保留-> 【原文:  https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】

周末花了点时间,把YIE002的代码整理了一下,上传到了Gitee仓库中。

这次整理的主要是和之前博客对应的代码,也就是使用STM32 Cube MX开发的几个项目,用来练手的Legacy Library代码没有上传。

仓库地址为:
YIE002开发探索 https://gitee.com/luobing4365/yie002-explorer

下图为代码仓库的截图:

图1 YIE002开发探索仓库截图

如仓库中的介绍,YIE002是我编写《UEFI编程实践》第9章时,用来做实验的设备。

继续阅读“YIE002开发探索-Gitee代码仓库说明”

2,743 total views, 2 views today

YIE002开发探索10-随机数生成器

请保留-> 【原文:  https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】
(代码仓库地址:https://gitee.com/luobing4365/yie002-explorer
具体参考博客:YIE002开发探索-Gitee代码仓库说明)

在规划YIE002开发板的时候,其中有一个目标是,实现类似ChaosKey一样的,可以在UEFI下访问的随机数生成器。

ChaosKey是一个硬件真随机数生成器,通过USB进行通信。其主页为:https://altusmetrum.org/ChaosKey/。

STM32F2和STM32F4系列的芯片,提供了芯片内部的随机数生成器。而YIE002开发板上使用的STM32F103C8T6,并没有提供。因此,必须使用其他办法来实现目标。

1 随机数生成器构建

上一篇中,已经打通了USB HID的双向通信通道,数据可以很方便地发送给上位机了。所需要的,是产生随机数的源。

在YIE002开发板上,有些引脚是悬空的,其中的PB1可用作ADC通道9进行采样,如图1所示。

图1 YIE002的单片机引脚图
继续阅读“YIE002开发探索10-随机数生成器”

1,521 total views, no views today

YIE002开发探索09-USB(HID双向通信)

请保留-> 【原文:  https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】
(代码仓库地址:https://gitee.com/luobing4365/yie002-explorer
具体参考博客:YIE002开发探索-Gitee代码仓库说明)

编写《UEFI编程实践》的时候,为了演示如何在UEFI下访问USB HID设备,我使用了正点原子的探索者F4开发板,做了一个双向通信的HID设备。随后考虑到版权问题,觉得还是不要放到书中较好。

毕竟书是需要出版的,而探索者F4的HID代码是在正点原子的USB代码上修改的。因此自己弄了个YIE002开发板,重新做了个HID设备。

以上的过程,在UEFI开发探索的系列博客中都写过,相关的代码也贴出过。

本篇所做的工作,是使用STM32 Cube MX重写USB代码,实现双向通信的HID设备。

1 YIE002上的USB

YIE002上使用的主芯片为STM32F103C8T6,它自带了USB,不过只能用作设备,不能作为主机使用。

USB 发展到现在已经有 USB1.0/1.1/2.0/3.0/4.0 等多个版本。目前用的最多的就是 USB1.1 (比如大部分的USB键鼠)和USB2.0、 USB3.0。实际上USB2.0也基本上接近淘汰,常见的USB设备基本都是USB3.0设备了。STM32F103 自带的 USB 符合 USB2.0 规范。

STM32F103的单片机自带USB从控制器,PC主机和单片机在传输时,是通过共享一个专用的数据缓冲区来完成的。缓冲区的大小由所用的端到数目和每个端到最大的数据分组决定,每个端点最大可使用512字节缓冲区,最多可用于16个单向或8个双向端点。

USB 模块同 PC 主机通信,根据 USB 规范实现令牌分组的检测,数据发送/接收的处理,和握手分组的处理。整个传输的格式由硬件完成,其中包括 CRC 的生成和校验。每个端点都有一个缓冲区描述块,描述该端点使用的缓冲区地址、大小和需要传输的字节数。

USB 的中断映射单元:将可能产生中断的 USB 事件映射到三个不同的 NVIC 请求线上:
1) USB 低优先级中断(通道 20):可由所有 USB 事件触发(正确传输, USB 复位等)。固件在处理中断前应当首先确定中断源;
2)USB 高优先级中断(通道 19):仅能由同步和双缓冲批量传输的正确传输事件触发,目的是保证最大的传输速率;
3)USB 唤醒中断(通道 42):由 USB 挂起模式的唤醒事件触发。

如图1所示,给出了USB设备框图。

图1 USB设备框图
继续阅读“YIE002开发探索09-USB(HID双向通信)”

2,048 total views, no views today

YIE002开发探索08-串口(485)

请保留-> 【原文:  https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】
(代码仓库地址:https://gitee.com/luobing4365/yie002-explorer
具体参考博客:YIE002开发探索-Gitee代码仓库说明)

在YIE002开发探索05中,我们介绍过RS485一些基本参数。RS485通信协议由RS232协议改进而来,将物理层改为了差分信号进行传输,因此其抗干扰性强、传输距离远,比较广泛地应用于噪声干扰比较大的工业控制环境中。

1 YIE002上的RS485

RS485的电气特性与RS-232不大一样,采用两线的时候为半双工。其基本的特点包括:

1) 接口电平低,不易损坏芯片。RS485 的电气特性:逻辑“1”以两线间的电压差为+(2~6)V 表示;逻辑“0”以两线间的电压差为-(2~6)V 表示。接口信号电平比 RS232 降低了,不易损坏接口电路的芯片,且该电平与 TTL 电平兼容,可方便与 TTL 电路连接;
2) 传输速率高。 10 米时, RS485 的数据最高传输速率可达 35Mbps,在 1200m 时,传输速度可达 100Kbps;
3) 抗干扰能力强。 RS485 接口是采用平衡驱动器和差分接收器的组合,抗共模干扰能力增强,即抗噪声干扰性好;
4) 传输距离远, 支持节点多。 RS485 总线最长可以传输 1200m 以上(速率≤100Kbps)一般最大支持 32 个节点,如果使用特制的 485 芯片,可以达到 128 个或者 256 个节点,最大的可以支持到 400 个节点。

从软件工程师的角度来看,RS485与RS232的编程几乎一样,需要关注的是其半双工的特性(因为开发板上使用的是两线),必须通过使能脚配置其为发送状态或接收状态。

如图1所示,给出了YIE002的RS485部分的线路图。

图1 YIE002的RS485
继续阅读“YIE002开发探索08-串口(485)”

1,481 total views, no views today

YIE002开发探索07-串口(DMA)

请保留-> 【原文:  https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】
(代码仓库地址:https://gitee.com/luobing4365/yie002-explorer
具体参考博客:YIE002开发探索-Gitee代码仓库说明)

在实际应用中,单片机的CPU是最“忙”的,需要完成的任务非常多。因此,CPU资源是非常宝贵的,能够少用就尽量少用,这也能很大程度上提高系统的稳定性。

DMA(直接存储器访问)就是用来解决类似问题的。它用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输,无须CPU干预,就可以实现数据的快速移动。

我曾和朋友开发过一款DTU的产品,是使用STM32单片机和FreeRTOS构建的系统。系统中有三个串口都需要发挥作用,如果只用中断的话,CPU频繁被中断跳转,效率是很低的,根本无法满足现场的数据需求。因此,我们根据应用需求,使用DMA构建了环状的串口数据接收和发送架构,产品最终表现很好。

本篇准备尝试在新的开发方式下,实现串口的DMA数据传输。

1 STM32的DMA

DMA全称为Direct Memory Access,也即直接存储器访问。CPU完成初始化动作,传输的动作则是由DMA控制器来实现完成的。

典型的例子是移动外设的数据到内存区,这样的操作不需要处理器干预,因此可以让处理器去排程做其他工作。

DMA传输对于高效能嵌入式系统算法和网络很重要,它没有中断处理的现场保留和恢复的过程,通过硬件为内存和外部I/O设备构建直接传输数据的通道,大大提高了CPU的效率。

1.1 STM32的DMA功能

STM32 最多有 2 个 DMA 控制器(DMA2 仅存在大容量产品中), DMA1 有 7 个通道。 DMA2 有 5个通道。每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁起来协调各个 DMA 请求的优先权。

STM32的DMA主要特性如下:
● 12个独立的可配置的通道(请求): DMA1有7个通道, DMA2有5个通道;
● 每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发。这些功能通过软件来配置;
● 在同一个DMA模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),优先权设置相等时由硬件决定(请求0优先于请求1,依此类推) ;
● 独立数据源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和目标地址必须按数据传输宽度对齐;
● 支持循环的缓冲器管理;
● 每个通道都有3个事件标志(DMA半传输、 DMA传输完成和DMA传输出错),这3个事件标志逻辑或成为一个单独的中断请求;
● 支持存储器和存储器间的传输;
● 支持外设和存储器、存储器和外设之间的传输;
● 闪存、 SRAM、外设的SRAM、 APB1、 APB2和AHB外设均可作为访问的源和目标;
● 可编程的数据传输数目:最大为65535。

从外设(TIMx、 ADC、 SPIx、 I2Cx 和 USARTx)产生的 DMA 请求,通过逻辑或输入到DMA 控制器,这就意味着同时只能有一个请求有效。外设的 DMA 请求,可以通过设置相应的外设寄存器中的控制位,被独立地开启或关闭。

如图1,给出各外设的DMA对应通道。

图1 DMA1各通道一览
继续阅读“YIE002开发探索07-串口(DMA)”

1,385 total views, 1 views today

YIE002开发探索06-串口(中断)

请保留-> 【原文:  https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】
(代码仓库地址:https://gitee.com/luobing4365/yie002-explorer
具体参考博客:YIE002开发探索-Gitee代码仓库说明)

用轮询的方式获取串口数据,是一种非常低效的方法。本篇尝试使用中断的方式,来实现串口的通信。

1 STM32的串口中断

在上一篇中,简单介绍过STM32的串口。轮询方式的编码中,只需要了解相关API的用法就可以了,编写相对简单。

对中断编程而言,这种程度的背景知识是不够的。STM32的Cube Library构建了完整的串口中断编程架构,也提供了相应的API函数供调用。不过,从我的角度来看,这个架构有点难以理解。需要使用者从原理上去了解,才可能自如的使用这些函数。

因此,建议开发人员对串口的寄存器、中断事件等,有了基本的了解后,仔细阅读Cube Library提供的串口函数的实现,再去构建代码较好。

1.1 STM32的中断事件和寄存器

以下的内容基本来自于《STM32参考手册》,详细的内容可以在原文档中查找,我主要是将需要的内容进行了整理。

STM32的USART中断请求事情比较丰富,如表1所示。

表1 USART中断请求

中断事件事件标志使能位
发送数据寄存器空TXETXEIE
CTS标志CTSCTSIE
发送完成TCTCIE
接收数据就绪可读TXNETXNEIE
检测到数据溢出OREIDELIE
检测到空闲线路IDLEIDELIE
奇偶检验错PEPEIE
断开标志LBDLBDIE
噪声标志,多缓冲通信中的溢出错误和帧错误NE或ORT或FEEIE

表中的EIE标志位,仅当使用DMA接收数据时,才会使用。

USART的各种中断事件,被连接到同一中断向量,表中的中断事件大致可分为发送期间产生的中断事件和接收期间发生的中断事件。也即:

1)发送期间:发送完成、清除发送、发送数据寄存器空。
2)接收期间:空闲总线检测、溢出错误、接收数据寄存器非空、校验错误、 LIN断开符号检测、噪音标志(仅在多缓冲器通信)和帧错误(仅在多缓冲器通信)。

在实际的编程中,大部分的中断事件,是不需要关注的。编程中,主要关注的是接收数据就绪可读(也即接收数据寄存器非空)、发送完成以及检测到空闲线路。

当然,为了熟悉这些中断事件,对USART的寄存器也必须了解。图1给出了我绘制的思维导图,只对我关系的寄存器位进行了整理,更详细的资料,请参考《STM32参考手册》。

图1 USART寄存器的思维导图
继续阅读“YIE002开发探索06-串口(中断)”

1,021 total views, no views today

YIE002开发探索05-串口(轮询)

请保留-> 【原文:  https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】
(代码仓库地址:https://gitee.com/luobing4365/yie002-explorer
具体参考博客:YIE002开发探索-Gitee代码仓库说明)

串口是相当的古老的接口,大概率比看这篇博客的读者早出生。常用的串口标准RS-232标准是EIA(美国电子工业联合会)和贝尔公司,于1969年公布的。它比较适合数据传输率在20000bit/s内的通讯,标准对信号线功能、电气特性都有明确规定。

常用的串口,包括RS232、TTL232、RS485和RS422标准。不过从软件协议上来看,RS232和TTL232是一样的 ,其区别主要在于硬件。

从硬件的电平上来看,TTL232使用+5V/+3.3V电平表示逻辑“1”,0V电平表示逻辑“0”;RS232则用-3V至-15V表示逻辑“1”,+3V至+15V表示逻辑“0”,也即采用负逻辑来表示。

如表1所示,给出了RS232、RS485和RS422的区别。

表1 串口标准

通信标准电平标准通信距离通信方式信号
RS232逻辑1: -15V至-3V
逻辑0: 3V至15V
15米全双工单端信号
RS485逻辑1: 2V至6V
逻辑0: -6V至-2V
1200米2线半双工差分信号
RS422逻辑1: 2V至6V
逻辑0: -6V至-2V
1200米4线全双工差分信号
继续阅读“YIE002开发探索05-串口(轮询)”

896 total views, no views today

YIE002开发探索04-定时器

请保留-> 【原文:  https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】
(代码仓库地址:https://gitee.com/luobing4365/yie002-explorer
具体参考博客:YIE002开发探索-Gitee代码仓库说明)

定时器的最基本的功能是用来周期性的定时,当然,它还用来实现如下功能:
1) 输入捕获。脉冲计数,在上升沿或下降沿检测,已经PWM的输入检测;
2) 输出比较。脉冲输出和步进电机的控制;
3) PWM生成。电压输出控制、直流减速电机控制等;
4) 编码器接口、霍尔传感器接口等。

当然,根据所用芯片不同,定时器还能提供更多的功能,比如与DMA的配合等。本篇准备用定时器产生的时间中断,周期性地控制LED灯的亮灭,以期对定时器的编程有基本的理解。

1 STM32的定时器

YIE002-STM32所用的芯片为STM32F103C8T6,属于STM32F1系列,在此系列,STM32提供了大量的定时器:2个基本定时器(TIM6、TIM7)、4个通用定时器(TIM2、TIM3、TIM4、TIM5)、2个高级控制定时器(TIM1和TIM8),以及4个特定功能定时器(SysTick、IWDG、WWDG、RTC),共有12个。另外,超大容量系列的产品中,又增加了5个通用寄存器TIM9~TIM14。

从功能上来说,高级控制通用器的功能是通用定时器的超集、通用定时器的功能是基本定时器的超集。比较通用定时器和高级控制定时器,可知它们的功能非常接近,高级控制定时器只是针对电动机的控制增加了一些功能。

因此,本篇准备使用通用定时器TIM3,构建演示程序。

1.1 定时器的时钟

TIM1和TIM8是高级控制定时器,时钟由APB2输出产生;TIM2至于TIM5是通用定时器,TIM6和TIM7是基本定时器,时钟由APB1输出产生。

从前几篇中可以知道,F103C8T6的系统时钟为72MHZ。如图1所示的时钟树,给出了定时器的时钟频率。


图1 定时器的时钟树
继续阅读“YIE002开发探索04-定时器”

1,080 total views, no views today

YIE002开发探索03-外部中断

请保留-> 【原文:  https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】
(代码仓库地址:https://gitee.com/luobing4365/yie002-explorer
具体参考博客:YIE002开发探索-Gitee代码仓库说明)

上一篇中,实现了按键的控制功能。本篇准备使用外部中断的方式,来实现对按键的控制。

1 STM32的外部中断

在跑马灯的实验中,简要的介绍过GPIO的设置。对于本篇来说,使用GPIO作为外部输入中断,所需要关注的知识点有两块:
1) 如何配置外部中断,以及与之相关的中断程序的编写;
2) 中断管理分组以及中断的优先级。

1.1 外部中断/事件控制器(EXTI)

STM32的每个IO都可以作为外部中断的中断输入口。STM32F103C8T6有19个能产生事件/中断请求的边沿检测器,每个输入线可以独立配置成输入类型和对应的触发事件(上升沿、下降沿或双边沿触发)。

这19个外部中断为:
线 0~15:对应外部 IO 口的输入中断。
线 16:连接到 PVD 输出。
线 17:连接到 RTC 闹钟事件。
线 18:连接到 USB 唤醒事件。

也就是说,供IO口使用的中断线只有16个。而STM32的IO口数目是超过16个的,因此,必然产生共用的问题。

在设计上,STM32的管脚GPIOx.0GPIOx.15(x=A/B/C/D/E/F/G)分别对应中断线015,也即每个中断线最多对应7个IO口。比如,线1对应GPIOA.1、GPIOB.1、GPIOC.1…GPIOG.1。中断线每次只能连接到1个IO口上,在平常编程中,需要通过配置决定中断线连接到哪个GPIO上。

如图1,给出了GPIO与中断线的映射关系图。

图1 外部中断通用I/O映像
继续阅读“YIE002开发探索03-外部中断”

1,074 total views, no views today

YIE002开发探索02-按键

请保留-> 【原文:  https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】
(代码仓库地址:https://gitee.com/luobing4365/yie002-explorer
具体参考博客:YIE002开发探索-Gitee代码仓库说明)

按键实验中,使用GPIO的相关知识就可以了。在上一篇中,已经介绍了GPIO的基本用法,这一篇使用这些知识实现按键的功能。

1 在YIE002-STM32型上编程

在后续的实验中,没有必要每次都重新去配置工程。比如时钟树的配置、调试的配置等,每次都差不多。因此,可以直接在之前的工程上进行修改,添加需要的功能即可。本篇的工程,就是在上一篇的基础上进行修改的。

1.1 硬件资源

YIE002-STM32型上准备了三个按键,如图1所示。

图1 按键的硬件资源

开发板上提供的三个按键SW1、SW2和SW3,分别连接到PA6、PA5和PA1上。从图中可以看出,这三个按键都是低电平有效的。

继续阅读“YIE002开发探索02-按键”

1,040 total views, no views today

YIE002开发探索01-跑马灯

请保留-> 【原文:  https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】
(代码仓库地址:https://gitee.com/luobing4365/yie002-explorer
具体参考博客:YIE002开发探索-Gitee代码仓库说明)

(YIE001和YIE002都是我平时为了嵌入式编程所做的开发板,特征在于:方便回家或出差携带、U盘大小。《UEFI编程实践》也使用它们,作为PCIE和USB程序的实验硬件。)

在YIE002上准备了5个LED灯,除去用来显示电源的灯,其余4个都可以用来做实验。这一篇中,我准备用这4个灯实现跑马灯的效果。

1 STM32的GPIO

STM32单片机的每个IO口可以自由编程,不过要求是按32位字进行访问。包括输入和输出,它们可以配置为8种模式。在输入时,包括模拟输入、浮空输入、下拉输入和上拉输入;通用输出时,包括推挽式和开漏式;复用功能输出时,也包括推挽式和开漏式。

为控制IO端口,STM32提供了7个寄存器来进行控制。对于这些寄存器的用法,不需要深入了解也可以编程。有兴趣的话,它们的具体用法,可以查看《STM32中文参考手册_V10.pdf》。

YIE002-STM32型开发板使用的是F103C8T6,其封装类型为LQFP48。当前我使用的版本,准备了5个LED灯,除了其中一个用来表示电源接通,其他4个都可以通过GPIO来控制。

STM32对于GPIO的编程,主要包括使能相应的时钟、设置相应的工作模式、置位或清位。在Cube Library中,主要包含以下可使用的API(参考UM1850《Description of STM32F1 HAL and low-layer drivers》):

1) 初始化函数

void HAL_GPIO_Init (GPIO_TypeDef * GPIOx, GPIO_InitTypeDef * GPIO_Init) ;
void HAL_GPIO_DeInit (GPIO_TypeDef * GPIOx, uint32_t GPIO_Pin) ;

2) IO操作函数

GPIO_PinState HAL_GPIO_ReadPin (GPIO_TypeDef * GPIOx, uint16_t GPIO_Pin);
void HAL_GPIO_WritePin (GPIO_TypeDef * GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);
void HAL_GPIO_TogglePin (GPIO_TypeDef * GPIOx, uint16_t GPIO_Pin);
HAL_StatusTypeDef HAL_GPIO_LockPin (GPIO_TypeDef * GPIOx, uint16_t GPIO_Pin);
void HAL_GPIO_EXTI_IRQHandler (uint16_t GPIO_Pin);
void HAL_GPIO_EXTI_Callback (uint16_t GPIO_Pin);

对于GPIO时钟的配置,一般在CubeMx图形化编程时,会自动生成代码。因此,对GPIO的操作,主要使用读、写、变换(也即上述的HAL_GPIO_ReadPin、HAL_GPIO_WritePin、HAL_GPIO_TogglePin)就可以了。

下面在YIE002-STM32型上,实现跑马灯的代码。

继续阅读“YIE002开发探索01-跑马灯”

1,094 total views, no views today

YIE002开发探索00-起篇

请保留-> 【原文:  https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】

YIE002开发板,是我在撰写UEFI开发探索系列博客,以及编写《UEFI编程实践》的时候,做的一款开发板。

在UEFI下访问USB HID设备,需要一个自制的USB HID设备。最初的时候,我是使用了正点原子的探索者F4的板子,做了一个需要的设备。那个板子很大,在频繁带着它往返家和公司的过程中,终于厌倦了。想着是不是能做个方便携带、可以直接通过USB取电的小开发板,这就是YIE002开发板的来源。

我的需求,本来是只想做个USB HID设备,然后开发个Windows/Linux的上位机程序,以及UEFI下的上位机程序。在定义YIE002的时候,只提出了USB接口和LED灯的要求。将这个想法告诉了做硬件的朋友,他把按键、RS232串口、485串口和TTL串口都加上了,做成了一个不错的小型开发板。

这个过程,我在UEFI开发探索的博客中也曾经说过。既然拿到了这个资源相对丰富的小开发板,我就萌发了再开一个“嵌入式开发”的专栏,按照自己的设想,把一直以来这方面的开发,做个整理。

1 开发板构思

如图1所示,是目前所构思的YIE002的结构图。

图1 YIE002结构图

主要包含以下硬件资源:

4个LED灯和1个电源显示灯;
3个按键;
三类 串口,包括RS232、485和TTL;
USB 全速设备。

当前我在使用的YIE002开发板,其主芯片是STM32F103C8T6。以前曾使用过这个芯片,开发过一些公司的项目,相对比较熟悉。

继续阅读“YIE002开发探索00-起篇”

1,190 total views, 1 views today

Zephyr系统快评

请保留:【作者:张佩】 【原文:www.yiiyee.cn/blog】

Zephry是Linux基金会托管的一个嵌入式RTOS系统,主导开发的是Intel。我对这个系统进行了学习之后,得到了下面的5条总体认识。

  1. Zephyr的内核模式是宏内核,不是微内核;设备驱动都被集成在内核中;内核编译采用Kconfig脚本配置,资源配置也通过它在编译时指定
  2. Zephyr支持的平台:arm、x86、RISC-v、ARC、NIOS 2、POSIX等
  3. ARM平台支持不完全:只支持arm 32系列的m/r平台,a平台不支持;同时,也不支持arm64
  4. Zephyr支持100多种嵌入式开发平台,嵌入式友好;内核size可以小至8kB
  5. Zephyr只有mpu(内存保护单元)的支持,任务之间的内存是相互隔离的,但不支持虚拟内存;多个用户应用同时运行有一定的困难,目前仅支持单应用的形式

内核实现

Zephyr采用的是标准的宏内核的架构,这对于它面向MCU进行开发是有利的。如果采用微内核的话,需要有稳健的IPC机制,不仅执行效率上有所降低,并且需要更多的代码量。

继续阅读“Zephyr系统快评”

5,795 total views, 2 views today

光伏云项目杂记02 – 构建Ringbuff(环形缓冲)1

在做这个项目的时候,遇到一个问题。我们使用的STMF103C8T6,资源很可怜,看看:

图1 STM32F103C8T6的参数

72M的频率,内存只有20K。而这个芯片需要做的事情:

1 3个串口的收发;
2 3个灯的控制,让用户通过灯的闪烁判断处于什么状态,方便维护;
3 GPRS模块的协议管理;
4 与逆变器端的协议管理;
5 看门狗的任务处理;
6  服务器端协议的处理,包含应用端口和配置端口;

以FreeRTOS为架构,估计用了近20个任务来完成这些工作。每个任务给的堆栈是0.5K,加上串口需要的缓冲区,一番操作下来,内存基本用光光。一起开发的兄弟,暂且称为烟枪吧(抽烟很快,1分钟一根,当得起这个称呼)。每天嚷嚷,说内存严重不够用,不能让客户再提新的需求了。虽然通过map文件计算了每个任务所用的栈空间,尽可能不浪费。不过再改的话,程序就直接溢出了,问题出在哪都不知道。

我很同意他的看法,也很同情我们的处境。可是,客户又不是我家的,他想提要求,我又控制不住。何况有些要求还是很中肯的,咱们都是讲道理的,对吧?

那怎么办?

分析源代码,我盯上了串口处理的这段代码。三个串口,除去串口接收发送所需要建立的任务外,每个串口还用了256字节的缓冲区。另外,程序写得耦合性比较差,估计换个芯片,所有代码都得重写。

程序逻辑是这样的:

  1. 将缓冲区分为两段,A段128字节,B段128字节;
  2. 串口接收启用DMA传输,不占用CPU资源;
  3. 当触发串口接收DMA中断,中断处理程序将缓冲区指向A段,接收数据;
  4. 接收完毕后缓冲区指向B段;
  5. 有一个任务专门监听,发现有数据立即取走;
  6. 新的数据来了,DMA接收中断处理程序用B段缓冲区取走数据,收完后缓冲区指针再指向A段。

也就是说,当监听任务在处理数据的时候,DMA接收中断使用了另外一段缓冲区,大家互不干涉(当然程序中也利用了FreeRTOS的互斥量来实现)。

这样实现其实没有太大问题,因为精心计算了串口和DMA的处理时间。接收串口的数据越长,越不会出现问题。

问题就出在小数据上。如果串口接收大量的小数据,频繁的触发中断,CPU就有可能丢失数据了。(实际运行中没有出现过,连续发送1个字节的情况,在客户环境中可认为概率是0)

烟枪和我商量了一段时间,我们决定使用ringbuffer(环形缓冲)来实现。他把这个任务轻飘飘的交给我了,调他的板子去了。

没办法,开始干活吧。 Ringbuffer的基本原理,我大概了解。维基百科中也有说明:https://en.wikipedia.org/wiki/Circular_buffer

A 24-byte keyboard circular buffer. When the write pointer is about to reach the read pointer – because the microprocessor is not responding, the buffer will stop recording keystrokes and – in some computers – a beep will be played.

我能用的资源有限。必须实现原有的串口接收,不能丢数据;同时还有提供尽可能简单的接口,供应用层的函数调用。

因此,我选择使用Contiki中的代码,用它的核心思想来构建这个架构。当时估计错误,我不觉得能有多难,本以为一周左右能搞定。没想到对芯片的DMA以及串口不那么熟悉,整整花了我三周时间。

这是一段痛并快乐的回忆。

3,727 total views, no views today

光伏云项目杂记01—modbus串口丢包了

和深圳某家公司合作光伏云的项目,我们主要做数据传输模块,对方负责开发云端,光伏逆变器外采。针对不同的应用场景,做了三款产品。来来回回用了一段时间,反响还不错,和其他同类产品相比还比较稳定。图一是其中一款用得较多的数据传输模块。

图1 物联网透传设备-gprs dtu

前一段时间现场的工程师报了一个问题,处理过程比较有趣,就记录了下来。

数据传输模块(也即数据透传模块,简称DTU)安装在逆变器上,通过串口把逆变器的数据接收过来,DTU通过GPRS(2.5G)转发给服务器。DTU最重要的是不能断网,任何情况下导致断网都必须重新连接后台,保证数据的连续性。

工程师报告,使用组态软件连接DTU(串口连接),把数据发送到服务器。数据是按包的方式定时发送的,每过5分钟会有一个包丢失。有多个设备通过组态软件连接DTU,也就是说有很多包丢失了。

接到报告后,我开始着手搭建环境,模拟现场。工程师使用的是modbus slave,我找他要来相应的版本软件。DTU与一台小的Mini台式机相连,在我自己的阿里云上运行自己写的网络调试工具。

数据包一个个发出去,几个小时下来,没有出现丢包的现象。把modbus slave换成串口调试助手,也没有出现丢包现象。我把数据包调大,大概200字节一次发送,这次出现了。

联系后台软件工程师,用他的云平台配合modslave以及DTU配合测试。其中DTU与台式机通过USB转串的设备连接,后面也尝试直接用串口线连接。经过20小时的测试,现象不大一致。使用USB转串设备进行测试的,仍旧有拆包现象;使用原生串口的,没有发现拆包现象。不过,对比了服务器的后台记录,基本没有拆包,也有命令包直接就丢失了。

百思不得其解。要后台的工程师配合我,协调起来很麻烦。我回到阿里云,用自己的网络软件模拟后台。同时把modbus slave撤了,用自己以前的代码写了个串口调试的工具,继续测试。这回,不管是usb转串的还是原生串口连接,都没有出现丢包拆包。光伏云项目中,数据获取是有后端服务器软件发起的,由其发送命令包,通过DTU传送给设备,设备再通过DTU发给服务器。在测试中还发现一个现象,如果服务器发送命令包向设备要求数据,时间频率如果设为每5秒发一次,丢包现象还会出现;设置为30秒一次,则机会没有出现丢包过。

看起来有好几个问题混杂在一起,产生了这样奇怪的现象。首先,排除DTU本身固件的问题,DTU中采用了DMA机制,以单片机的处理速度,这点串口数据不应该会处理不了。最大的问题应该有两个:

1 modbus slave和串口调试助手在串口读写的编程中,有哪个地方没有处理好,导致了拆包的现象;

2 至于丢包现象,和服务器端的软件有关。工程师可能习惯了平常的internet,没有考虑到GPRS的网络稳定性远低于有线或者wifi网络。不能过于频繁的去塞数据。

分析完了,寻找证据。第二个问题好解决,请后端工程师改下通讯频率就行了,回头一起测试。 至于第一个问题,只能反汇编modbus slave了。很幸运,用IDA反汇编后,我读了半小时代码,找到了问题所在,如图2。

图2 用IDA反汇编modbus slave

问题出在SetCommTimeouts函数。定位到SetCommTimeouts函数的调用位置,其前三个参数分别为ReadIntervalTimeout=0xffffffff;ReadTotalTimeoutMultiplier=0x00; ReadTotalTimeoutConstant=0x00。也就是说,modbus slave读取一次缓冲区,读操作就完成了。

这样操作是有点问题的,对照我自己的代码,把三个参数分别改为:0x64,0x0A,0x3E8。这些修改是把exe当做文件编辑的,相当于在字节码上直接修改了。

Modbus slave的软件hack完了,重新搭建环境。4个小时后,测试结果显示,修改后的modbus slave没有出现拆包丢包现象。