嵌入式系统与单片机|技术阅读
登录|注册

您现在的位置是:嵌入式系统与单片机 > 技术阅读

技术阅读

  • 超级精简系列之十九:超级精简的循环FIFO池,C实现

    2024-06-23

    一. 前言前面我们实现了循环缓冲区FIFO:https://mp.weixin.qq.com/s/MvL9eDesyuxD60fnbl1nag.在串口驱动:https://mp.weixin.qq.com/s/vzjWu2LxpVGZw-msCooh8Q.PWM音频采集与播放:https://mp.weixin.qq.com/s/nCSw743V5iZjGzrV1oQK4Q等应用场景都有应用。但是以上循环缓冲区FIFO还有一些应用场景并不能很方便和高效的使用。比如在音频应用场景,除了”PWM音频采集与播放”可能还会涉及到算法处理,此时应用场景是”采集-算法-输出”, 多了算法处理的过程,此时上述FIFO就显得不是很适用。我们来看这种应用场景对FIFO的需求:1.数据链路加长,尽量避免...

  • USB/串口/网络数据监控一网打尽-分享一个非常实用的工具

    2024-06-23

    前言话说工欲善其事必先利其器,在嵌入式开发中,往往需要调试板级和PC端的各种通讯,而串口,以太网和USB是几种比较常见的通讯。能够无影响的监控这些数据流会大大提高调试的效率。有一款瑞士军刀级别的PC端工具,就同时具备监控这三种接口的能力,我们这一篇就来分享下这个工具Device Monitoring Studio。安装下载链接:https://pan.baidu.com/s/13E4a9W2wefv1smKigbbPCw?pwd=e89x提取码:e89x解压双击打开device-monitoring-studio.exe弹出用户账户控制对话框点击是点击Next勾选同意License,点击Next勾选所有选项,Next修改安装路径,点击Next点击In...

  • USB系列之-WINUSB枚举过程实例分享

    2024-06-23

    前言  本文分享WINUSB枚举过程和描述符实例。WINUSB枚举过程描述符实例设备描述符static const uint8_t s_dev_desc[] = {    0x12,    0x01,    0x00,    0x02,    0x00,    0x00,    0x00,    0x40,    0x95,    0x19,    0x01,    0x01,    0x00,    0x01,    0x01,    0x02,    0x00,    0x01,};索引EE的字符串描述符12 03 4d 00 53 00 46 00 54 00 31 00 30 00 30 00 41 00“MSFT100A”配置描述符static const uint8_t s_cfg_desc[] = {    0x09,  /* bLength */    0x02,  /* bDescriptorType 0x02...

  • USB系列之-一文搞懂枚举成WINUSB相关的描述符

    2024-06-23

    前言    WinUSB 是 Windows 自带的 USB 设备的通用驱动程序。是Windows下USB开发的一个不错的选择,免驱,开发难度低。 枚举成WINUSB设备,除了标准请求枚举过程,还需要一个特殊的字符串描述符:MS OS字符串描述符,两个厂商相关请求:一个是针对设备的扩展兼容ID OS特征描述符报告兼容ID为”WINUSB”,一个针对接口的扩展属性OS特征描述符,报告接口GUID. 参考https://learn.microsoft.com/zh-cn/windows-hardware/drivers/usbcon/automatic-installation-of-winusb#what-is-a-winusb-device  这一篇就来介绍下这三个请求的详细细节,基于此就可以快...

  • 超级精简系列之十八:超级精简的IO模拟驱动黑白电子书屏LS013B4DN04 1.3" 168x144

    2024-06-23

    一.前言    这一篇我们继续超级精简IO模拟系列。本篇通过IO模拟,实现驱动黑白电子书屏LS013B4DN04 1.3" 168x144. 参考屏的手册https://cdn-shop.adafruit.com/datasheets/LS013B4DN04-3V_FPC-204284.pdfhttps://cdn-learn.adafruit.com/downloads/pdf/adafruit-sharp-memory-display-breakout.pdf    注:测试环境为RP2040开发板。二.接线电源只需要3.3V接VIN,接GND。信号使用3个输出引脚接CS,DI,CLK三.显存映射分辨率144x168,注意这里不要搞反了否则显示会不对。V垂直方向对应Line地址144x168就是168.H水平方向对应P,一个字节对应8列,144列...

  • USB系列之-一文搞懂USB2.0高速PHY端接电阻及其测量

    2024-06-23

    前言USB2.0高速相对于全速其收发器原理不一样,前者使用电流驱动,后者使用电压驱动。对于电流驱动最终还是需要通过电阻来转化为电压进行信号识别。所以电阻的精度就很关键,否则会导致电压摆幅SWING的偏差。对于USB2.0高速,实际就是通过17.78mA的电流源,灌入到45欧的端接电阻来产生电压摆幅,由于HOST端和设备端结构一样,各有一个45欧端接电阻相当于并联,所以实际电压摆幅就是17.78mAx45欧/2=400mV。以上可以看出45欧电阻的精度就很重要了,因此就引出了我们本文的主题, 测试USB PHY端接电阻的阻值。本文即介绍之,并且一贯我们知其然知其所以然的...

  • 基于DWC2的USB驱动开发-控制IN端点长数据发送实现

    2024-06-23

    前言枚举一开始就是一系列标准请求,获取各种描述符的操作,比如通常就是获取设备描述符->设置地址->获取设备描述符->获取配置描述符->......。在控制端点OUT可以接收到SETUP之后,我们就要调试控制端点IN即设备发送数据的过程,即解析SETUP内容按照要求返回各种描述符。以上需要返回即设备IN端点需要发送的描述符内容有短有长,对于复合设备配置描述符往往会很长,可能有一两千字节都是比较常见的,而端点0最大是64字节,那么如何发送这么长的数据呢,本文就分享dwc2的实现。可以想到的是对于长数据肯定是要分次传输的, 传一次中断然后继续传后续的,我们...

  • 分享一个非常有用的虚拟串口工具

    2024-06-23

    一. 前言嵌入式开发中串口通讯是经常用到的通讯方式。很多时候我们需要在没有硬件环境的时候进行串口通讯的调试,比如硬件板还没出来时,先要验证串口协议,此时PC端的虚拟串口创建就可以解决我们的需求。在PC上创建虚拟串口,则可直接在PC上进行串口通讯的调试。PC上就有开源的com0com项目实现。本文即分享该工具的应用。二. Com0com介绍与安装该项目参见https://com0com.sourceforge.net/Com0com可以创建任意的虚拟串口对,虚拟串口对即两个串口,他们是互通的,模拟物理上的两个连接的串口。从https://sourceforge.net/projects/com0com/下载下...

  • 超级精简系列之十六:基于IO模拟+FIFO的串口驱动

    2024-06-22

    一. 前言接上一篇预告,结合前面实现的FIFO和前面实现的IO模拟串口。我们这一篇继续超级精简系列,来实现基于FIFO的串口驱动。 超级精简系列之十四:超级精简的IO模拟UART发送 超级精简系列之十三:超级精简的循环FIFO,C实现测试环境为HPM5300evk开发板,如果需要该工程可以留言。当然直接从系列文章中复制源码也可以很快捷的移植到任意平台。二. 实现过程Uart_fifo.c中包含头文件#include <uart_tx_port.h>#include <uart_rx_port.h>#include <rtthread.h>#include "fifo.h"Fifo实例创建/* FIFO设备实例 */static uint8_t fifo_buffer[1024];static...

  • 超级精简系列之十五:超级精简的IO模拟UART接收

    2024-06-22

    前言前面一篇我们实现了IO模拟UART发送,我们继续来实现UART接收。对于接收底层资源需要一个输入IO且其可配置位下降沿中断,和一个定时器即可。实现过程有了发送的实现,我们依葫芦画瓢,按照发送的模式进行实现,只是一个是发送一个是接收。状态机实现的过程是类似的。启动接收即使能RX引脚的下降沿中断,用于检测起始位。在中断回调中启动定时器延迟1.5个位宽即可采样第一个bit数据。定时器中断回调中进行状态机处理,配置定时器每隔一个bit采样一次,继续处理后续位。直到处理完所有位,然后调用接收回调,并继续下一个循环。数据结构和发送类似,只是TX...

  • 超级精简系列之十三:超级精简的循环FIFO,C实现

    2024-06-22

    一. 前言循环缓冲区FIFO在嵌入式开发中非常常见,比如串口的收发驱动,协议包的接收等都会用到,这一篇我们就来实现一个自己简单的循环FIFO的”轮子”。二. 实现过程2.1 数据结构定义FIFO数据结构/** * \struct fifo_st * FIFO缓冲区结构. */typedef struct {  uint32_t in;          /**< 写入索引        */   uint32_t out;         /**< 读出索引        */   uint32_t len;         /**< 有效数据长度    */   uint32_t buffer_len;  /**< 有效长度        */   uint8_t* buffer;      /**< 缓存,用户...

  • 超级精简系列之十二:超级精简的IO模拟IIC的C实现:驱动OLED屏

    2024-06-22

    前言 前面我们实现了IO模拟IIC,并基于此实现了EEPROM读写与编辑器的应用。https://mp.weixin.qq.com/s/ESzWWqxHpQevsWfjV0s2VQ超级精简系列之三:超级精简的IO模拟IIC的C实现https://mp.weixin.qq.com/s/vlhy7z8XrgxXJoAtYuXw0g超级精简系列之四:超级精简的IO模拟IIC的C实现:EEPROM编辑工具应用。我们这一篇继续分享IIC接口的OLED驱动。实现过程我这里基于HPM5300EVK开发板,其他任何开发板移植也很简单,只需要替换相应的IO操作实现即可,这也得益于之前实现的IO模拟IIC代码的高可移植性。我这里使用的是0.96寸的128x64的OLED,控制器型号是SSD1306...

  • 超级精简系列之十:超级精简的IO模拟SPI的C实现应用值读写FLASH状态寄存器

    2024-06-22

    一. 应用 SPIFLASH状态寄存器的读写以上实现了IO模拟SPI接口,整个过程很自然,参考时序图操作IO即可。是骡子是马拉出来遛遛。那么我们就继续基于此来进行SPIFALSH的操作,以W25Q32FV为例,先实现状态寄存器的读写,然后再实现FLASH的读写实现FLASH编辑器的工具。W25Q32FV手册参考《https://www.winbond.com/hq/product/code-storage-flash-memory/serial-nor-flash/?__locale=en&partNo=W25Q32FV》4.1 W25Q32FV的状态寄存器我们先从手册找到对应的指令表有3个状态寄存器分别都有对应的读写指令。 在实现时我们可以对应的数组通过索引来匹配命令#d...

  • 超级精简系列之九:超级精简的IO模拟SPI的C实现

    2024-06-22

    一. 前言 本文继续超级精简的IO模拟常见总线接口系列。前面实现了IO模拟IIC主机并进行了EEPROM的读写测试,基于命令行实现了EEPROM的编辑工具。见:《https://mp.weixin.qq.com/s/ESzWWqxHpQevsWfjV0s2VQ》《https://mp.weixin.qq.com/s/vlhy7z8XrgxXJoAtYuXw0g》这一篇继续以上的套路,实现IO模拟SPI主机,进行SPIFLASH的读写测试,基于命令行实现SPIFLASH的编辑工具。我们还是坚持简单的设计哲学,尽量具备良好的可移植性,借鉴面向对象的设计思想。二. IO模拟SPI接口设计SPI协议本身就不再赘述了,比较简单,可以以下链接的说明,重点理解4种模式即CPH...

  • 超级精简系列之七:超级精简的软件定时器实现

    2024-06-22

    一. 前言前面我们实现了链表这个非常基础的”轮子”,《https://mp.weixin.qq.com/s/8auensNh3q0T0dOayM0d2A》,基于此我们就可以实现更多的通用组件。这一篇分享比较常见的软件定时器的实现。二. 实现主要思想如下:通过链表管理定时器。定义定时器结构/*** \struct timer_info* 定时器信息结构体.** 通过next_item_pst连接后续节点.*/struct timer_info{ uint8_t mode; /**< bit[0] 0:停止 1:使能 bit[1] 0:单次模式 1:持续模式 */ timer_callback_pf func; /**< 回调函数 */ uint32_t ...

  • 超级精简系列之六:超级精简的单向链表实现

    2024-06-22

    一. 前言链表可以说是嵌入式开发中最常见的数据结构了,RTOS中的任务管理,定时器管理,堆管理等几乎都是用的链表实现。所以我们超级精简系列也有必要准备一个自己趁手的链表的”轮子”,为以后实现自己的超级精简的堆管理,定时器管理等做准备。二. 实现过程2.1 数据结构定义节点值类型,这样可以根据需求配置不同的类型。typedef uint32_t list_itemvalue_t; /**< 节点值类型 */定义函数返回值类型枚举/*** \enum list_res_e* 返回结果枚举.*/typedef enum{ LIST_OK = 0, /**< 成功 */ LIST_PARAM_ERR = 1, ...

  • 超级精简系列之四:超级精简的IO模拟IIC的C实现:EEPROM编辑工具应用

    2024-06-22

    接上文四.应用以上实现了IO模拟IIC接口,整个过程很自然,参考时序图操作IO即可。但是是骡子是马拉出来遛遛。那么我们就基于此来进行EEPROM的读写测试吧,以24LC02为例。4.1 24LC02驱动我们这里以24LC02的驱动为例。手册参考《https://ww1.microchip.com/downloads/aemDocuments/documents/MPD/ProductDocuments/DataSheets/24AA02-24LC02B-24FC02-2K-I2C-Serial-EEPROM-20001709N.pdf》同样的我还是先设计接口。很自然的IIC行为抽象如下typedef void (*eeprom_iic_start_pf)(void); /**< IIC启动接口 */typedef void ...

  • 超级精简系列之三:超级精简的IO模拟IIC的C实现

    2024-06-22

    一. 前言 本文继续超级精简系列。在嵌入式开发中IIC,UART,SPI等是非常常见的接口,其接口时序也很简单,虽然几乎所有的MCU都会有多个这些接口,但是有时候也会资源不够,硬件接口不够,这时我们就可以使用普通IO来模拟。另外一个好处就是使用IO模拟的可移植性,兼容性更强更简单灵活。构建自己的一个IO模拟常见接口的代码库也显得很有意义,这一篇就来实现IO模拟IIC主机,后面还会继续分享,IO模拟SPI,UART等协议,实现自己的一个IO模拟常见接口的”轮子”。我们提一下设计哲学是尽量简单,具备良好的可移植性,借鉴面向对象的设计思想。二. IO模拟IIC接...

  • LPDDR读写时序解读之一:写时序

    2024-06-22

    一. 前言 了解DDR首先要对其命令时序有所了解,尤其是读写命令。这里简单起见以LPDDR为例分享其读写的关键时序和参数。本文重点在读写时序和参数,其他暂时按下不表。参考《JESD209B.pdf》二. 打开行ActiveActive命令,激活行个人觉得称为打开行更贴切。在向LPDDR SDRAM中的bank发出READ或WRITE命令之前,必须打开该bank的一行row。通过ACTIVE命令完成(见下图):BA0和BA1选择bank,地址输入选择要打开的行RA。可以同时有多个bank处于打开(活动)状态。 2.1 tRCD(ACTIVE to READ or WRITE delay)tRCD即打开行后到读写之间的间隔, 行row打开后,就可以在...

  • 玩Android啦,先来ServiceManager走起!

    2024-06-22

    本文涉及到的源码文件:/frameworks/native/cmds/servicemanager/main.cpp/frameworks/native/libs/binder/ProcessState.cpp/frameworks/native/cmds/servicemanager/ServiceManager.cpp/frameworks/native/libs/binder/IPCThreadState.cpp/system/core/libutils/Looper.cpp一、ServiceManager简介二、ServiceManager的启动三、ServiceManager详细剖析(3-1)获取驱动的名称(3-2)初始化进程状态ProcessState并设置重要参数(3-3)创建ServiceManager实例(3-4)设置上下文对象(3-5)创建looper,并执行pollAll四、ServiceManager的唤醒五、总结一...

  • 玩玩linux的uart,爽歪歪!

    2024-06-22

    linux源码相关文件:serial-core.cinclude/linux/serial_core.hunsetunset一、底层串行硬件驱动程序unset 底层串行硬件的驱动程序负责向serial核心驱动程序提供由struct uart_port定义的端口信息和一组由struct uart_ops定义的控制方法,底层驱动程序还负责处理端口的中断,并提供对控制台的支持。unsetunset二、Console支持unset serial核心提供了一些助手函数:uart_get_console()识别正确的端口结构。uart_parse_options()解析命令行参数。uart_console_write()用于执行逐字符写入,将换行符转换为CRLF序列。在驱动程序编写的时候建议使用此函数...

  • 干货 | 一文总结linux的platform驱动

    2024-06-22

    一、简介相关文件;/include/linux/platform_device.h/drivers/base/platform.c在linux设备驱动中,有许多没有特定总线的外设驱动,在实际开发中,又需要使用到总线、驱动和设备模型这三个概念,故而linux提供了platform这个虚拟总线,挂接在platform总线上的驱动称为platform驱动,由struct platform_driver描述,挂接在platorm总线上的设备称为platform设备,由struct platform_device描述。在linux内核的驱动源码中,可以看见很多基于platform驱动框架的驱动案例实现。二、platform总线在linux内核中,使用struct bus_type描述一个总线,为了抽象...

  • linux内核中的debugfs原来可以这样玩!

    2024-06-22

    一、????相关文件/fs/debugfs/file.c:debugfs的file描述文件。/fs/debugfs/inode.c:debugfs的inode描述文件。/fs/debugfs/internal.h:用于debugfs的内部声明。二、????简介debugfs可用于内核向用户空间提供信息,debugfs是个小型的文件系统,与/proc和sysfs不同,debugfs没有较为严苛的规则和定义,我们可以在里面放置想要的任何信息,以便于系统开发和调试。通常使用如下命令安装debugfs:mount -t debugfs none /sys/kernel/debug或者:mount -t debugfs debugfs /sys/kernel/debug/也可以在/etc/fstab文件中使用等效的语句:默认情况...

  • linux驱动模块参数传递的三种玩法!

    2024-06-22

    ????一、简介在嵌入式linux应用开发中,可以给main()函数传递参数,这样应用程序就能知道最初的控制参数是什么,当然也可以选择不向应用程序传递参数。在驱动开发中,会使用到insmod命令来加载一个驱动模块,这时候我们也可以使用insmod命令向驱动模块传递参数。????使用模块的参数传递机制,对调试系统非常方便!????二、参数传递方式参数传递分为两种:内置模块参数传递:即将模块编译构建进内核镜像。外置模块参数传递:使用insmod等命令装在的内核模块。对于内置模块参数传递,一般在bootloader中可向内置的模块传递参数,例如可以在bootargs中...

  • 一脸懵,万千设备,linux内核如何知道?

    2024-06-22

    1、简介本文基于内核源码4.19.4分析。linux内核设备的注册由device_register()函数完成,这个函数是linux设备驱动模型的核心函数,实现在/drivers/base/core.c中:在device_register()函数中,分为两个步骤:(1)调用device_initialize():该步骤用于初始化一个device。(2)调用device_add():该函数用于将device添加到linux内核的device树中。2、device_initialize分析 该函数接收一个struct device *dev参数,在该函数中初始化struct device结构中的几个重要成员:设置dev->kobj.kset为device_kset。device_kset是一个struct kset类型的全局变量...

  • 毛毛雨,linux内核线程就这样诞生了么?

    2024-06-22

    一、开篇 线程是操作系统的重要组成部件之一,linux内核中,内核线程是如何创建的,在内核启动过程中,诞生了哪些支撑整个系统运转的线程,本文将带着这个疑问瞅一瞅内核源码,分析内核线程的创建机制。本文基于linux内核版本:4.1.15。与linux内核1号init进程一样,在rest_init()函数中将调用kthread_init()函数创建kthreadd线程,如下代码:pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);下文将看看kthreadd()中完成了哪些事情。二、kthreadd线程入口分析 kthreadd线程的线程入口为kthreadd(/kernel/kthread.c),如下定义...

  • 在Windows下进行Qt安卓开发,此篇足矣!

    2024-06-22

    一、导读最近,有小伙伴问到关于Qt安卓相关的内容,主要还是在Qt安卓开发环境搭建上。故本文描述如何在Windows下搭建Qt安卓开发环境,由于Qt维护的安卓开发环境和安卓开发环境在很大程度上不兼容,所以在选择Qt版本的时候需要注意JDK和Android SDK环境的选择,否则QtCreator会检测不到正确/合适的Android开发环境。本文基于Qt5.14.2搭建Qt安卓开发环境,具体环境如下所示:环境描述Windows 10 家庭中文版Qt 版本:5.14.2因为Qt for Android的开发环境搭建有一些繁琐,还是一个不断试错爬坑的过程,文末附上了小生常使用的安卓开发环境完整包,可直接...

  • Qt单例模式的玩法

    2024-06-22

    一、导读在实际Qt开发中,很多时候会使用到单例模式,例如:一个数据解析器、一个翻译文件管理器、一个样式表加载器等等,这些都可以抽象成单例,Qt中,提供了一个Q_GLOBAL_STATIC,用于具体的单例实现,本文来描述这个宏定义,并从源码角度看看其背后的实现。二、Q_GLOBAL_STATIC简介Q_GLOBAL_STATIC宏定义在5.1版本后引入。定义如下:Q_GLOBAL_STATIC(Type, VariableName, ...)该宏用于创建一个全局静态对象,类型为QGlobalStatic,名称为VariableName,行为为指向Type的指针(详细过程会在后文中写到)。由Q_GLOBAL_STATIC创建的对象在第一次使...

  • 这四种使用Qt多线程设计的“姿势”...

    2024-06-22

    一、导读本篇文章是关于Qt多线程应用设计方法的总结,描述了Qt中进行多线程设计的四种方法,并列举了常见应用场景下的多线程设计方案。合理选择对应的方法来解决实际开发中遇到的问题有助于对应用程序进行更合理设计。二、【方法一】 QThread:带有可选事件循环的底层APIQThread是Qt中所有线程的基础,每个QThread实例代表和控制一个线程。使用QThread创建线程有两种方法:(1)直接实例化创建:提供了一个并行事件循环,允许在辅助线程中调用QObject槽函数。(2)子类化创建:继承QThread,允许应用程序在启动事件循环之前初始化新线程,或者在没有...

  • Qt资源系统,再也不用担心按钮图标不见了

    2024-06-22

    一、资源系统Qt的资源系统是一种独立于平台的机制,目的是用于在应用程序的可执行文件中存储二进制文件。资源系统基于qmake、Qt的资源编译器(rcc)和QFile实现。如果应用程序总是需要一组特定的文件(例如图标、翻译文件等),并且不想让这些文件丢失而影响应用程序的功能,这时候则可以使用Qt资源系统解决这个问题。二、外部二进制文件Qt对于外部的二进制文件,如果需要使用这个二进制文件,那么则必须将所需要使用的二进制文件转换成Qt的资源数据文件(默认文件名后缀为.rcc)。转换步骤如下:在Windows命令行下(这里以Windows系统为例),输入以...