- 2023-11-28
-
回复了主题帖:
【国产高性能运动控制MCU 先楫HPM5361】基于RT-Thread Studio搭建开发环境
qinyunti 发表于 2023-11-28 10:16
RCSN 发表于 2023-11-28 08:10 别误导,segger embedded studio对于开发先楫商业免费。只需官 ...
先楫有免费的GCC,有免费商用的SES,总比ARM生态里没有免费的商业IDE用好多了吧。 我知道ses是商业软件,需要授权怎么了,先楫免费申请个lic也很简单啊。 https://license.segger.com/hpmicro.cgi 既然你提到说买断许可,那还需要担心商业这些问题吗?先楫发展到现在早就过了品牌推广阶段。 相比arm开发用盗版的话,SES不用盗版都可以免费用,Segger也不会来找,这不宽松多了吗
-
回复了主题帖:
【国产高性能运动控制MCU 先楫HPM5361】基于RT-Thread Studio搭建开发环境
别误导,segger embedded studio对于开发先楫商业免费。只需官网申请lic,也很快。
- 2023-11-21
-
回复了主题帖:
测评入围名单: 国产高性能运动控制MCU 先楫HPM5361
期待大佬移植Arduino
- 2023-11-19
-
加入了学习《先楫半导体 EVK 快速开发的保姆级教程》,观看 HPM6000 开发板上手教程
- 2023-08-25
-
加入了学习《AT32F437识别二维码》,观看 AT32F437识别二维码
- 2023-08-07
-
发表了主题帖:
[玩转先楫CANFD外设系列之二]CAN外设的接收筛选过滤组详解
一、背景
在实际应用中,CAN总线上的数据,对于某些CAN节点来说,可能需要的数据并不多,希望收到的数据是自己关心的即可,不必要的数据过滤同时也可以减少中断的次数,减轻CPU负荷,也可以节省数据记录设备中的存储空间。
从上篇文章《[玩转先楫CANFD外设系列之一]轻松搞起CANFD》中可知道,先楫的CANFD包括了CAN和MCAN。其中HPM6700/HPM6400/HPM6300系列产品用的是CAN,而HPM6200系列使用的是MCAN。本文统称HPM6700/HPM6400/HPM6300系列产品的CANFD为CAN。
本文阐述CAN外设,MCAN外设不在本文阐述范围内。
CAN有16组独立的筛选器,并没有所谓的列表和掩码模式的过滤器组。每个筛选器都是独立,只要任意一组的筛滤器器满足条件则能被接收。配置也极其简单,只需要配置ID CODE和对应的匹配MASK位等寄存器则照样可以实现所谓的列表和掩码方式。
二、实现流程
对于筛选器来说,CAN可操作的寄存器很少,只需要三个寄存器即可实现筛选ID的效果。分别对应的名称是ACF_XXX后缀。
(一)ID筛选
寄存器上主要操作ACFCTRL和ACF的CODE_MASK位(一共有29位,满足标准帧和扩展帧)。
配置IDCODE和MASK,也就是说匹配MASK的位基于IDCODE来进行匹配。选择哪个筛选器通过ACFCTRL寄存器的ACFADR配置,一共4bit,也就是16个筛选器。
在can_set_filter这个API中,对于筛选器的选择,可以这么参考。
由于IDCODE和MASK共用一个寄存器也就是ACF的CODE_MASK,在配置的时候需要选择的是IDCODE还是MASK。这里需要用到ACFCTRL的SELMASK位。然后依次进行对CODE_MASK赋值。
需要注意的是:
在CAN外设当中,当MASK对应的位为0的时候,必须于IDCODE对应的位进行匹配比较,当为1的时候则忽视。
同样在can_set_filter这个API也是这么操作。
需要注意的是:IDCODE和MASK的时候,必须是在CAN复位模式下才能设置。
在can的sample当中,注释也特别提醒。
(二)标准帧和扩展帧筛选
每个筛选器还可以对标准帧和扩展帧进行筛选,主要是通过ACF寄存器的AIDEE和AIDE位进行配置。
这里描述的大概可以这样理解:
AIDEE=1 AIDE=x(不关心) 接收标准帧和扩展帧
AIDEE=1 AIDE=0 只接收标准帧
AIDEE=1 AIDE=1 只接收扩展帧
在can_set_filter这个API中,也是通过判断can_filter_id_mode_t枚举进行判断筛选。
(三)筛选器组的启用和禁用
在手册中,每个筛选器都可以单独启用和禁用。主要通过ACF_EN配置。
(四)实现列表和掩码模式效果
从上面的配置可知道:
列表模式:
IDCODE配置为需要接收的ID号,MASK配置为0,那么该筛选器就可以实现只接收一个ID的列表方式,比如:
标准帧下,IDCODE=0x21,MASK=0(全部比较)。那么该筛选器只能筛选ID为0x21这个ID。
掩码模式:
DCODE配置为需要接收的ID号,MASK配置需要比较的位,那么该筛选器就可以实现只接收一个ID的掩码方式,比如:
标准帧下,IDCODE=0x100,MASK=0x700。需要匹配的位是第十位到第八位,其他的位都不做关心,那么接收的ID范围就是0x100~0x1ff
三、代码实现
在hpm_sdk的can这个sample当中,有个测试项目就是筛选器测试,对应的是board_can_filter_test这个函数。这里使用的是内部环回模式,可以不用接外置PHY即可测试。
在函数的开头注释就说到两个注意点,开发者在开发的时候需要注意。
1、CAN的筛选器只能在CAN复位模式下配置,建议使用can_init这个API,通过传参代入筛选器参数,can_init这个API自动处理。否则需要需要调用can_set_filter这个API,则需要先调用can_reset进行复位。这时候也同样需要重新设置下波特率。
2、can_filter_config_t结构体的mask成员,1代表在IDCODE对应的位忽视,0代表该位将于IDCODE比较匹配。
这个函数同样实现了类似掩码模式和列表模式。
掩码模式:
log可以看到:
列表模式:
在sample当中,16个筛选器的mask都设置为0,也就是必须与idcode的所有位匹配才接收。
同样也是发送ID为0~2048的2048个消息帧,应该只能收到16个ID帧。分别是以下
四、总结
1、HPM6700/HPM6400/HPM6300系列产品的CAN长达16个过滤组,如果需要更多的过滤组,可以选择HPM6200等是MCAN外设,比如hpm6200的MCAN,标准帧可以达到128个过滤组。
2、HPM6700/HPM6400/HPM6300系列产品的CAN过滤组设置相对简单易用。
3、HPM6700/HPM6400/HPM6300系列产品的CAN过滤组的IDCODE和MASK只能在CAN复位模式下配置。建议每次配置过滤组直接调用can_init这个API。
-
发表了主题帖:
[玩转先楫CANFD外设系列之一]再也不用担心CANFD玩不起来了
本帖最后由 RCSN 于 2023-8-7 11:52 编辑
一、概述
先楫的CANFD外设,有两个CANFD的IP,其中HPM6700系列,HPM6400系列、HPM6300系列使用的是CAN,包括了经典CAN和CANFD。而HPM6200系列则使用的MCAN系列,同样也包括了经典CAN和CANFD。两个CANFD有所差异,hpm_sdk也分为了两个驱动文件,但基本的操作接口保持一致。
本文阐述HPM6700系列,HPM6400系列、HPM6300系列的CAN,这里就统称为CAN。MCAN部分后续文章阐述。
先楫的CAN外设功能比较丰富,比如涉及到各种CAN模式、CAN错误警告提示、时间戳等等。可以看看手册CAN特征描述。
本文主要介绍CAN的基础配置(引脚时钟初始化,波特率设置,正常模式下的收发流程),其他的功能可参考hpm_sdk,后续根据需要也会进行文章阐述。
本文以hpm_sdk的操作接口API为例子,进而介绍CAN的相关知识。
关于更多的CAN/CANFD知识可以访问CIA官网,spec文档可以参考ISO11898-1-2015。本文部分阐述都是基于CIA和ISO的描述参考。
二、实现流程
hpm_sdk中,关于can的例子在samples/drivers/can。使用的板子为hpm6750evk2.
(一)引脚初始化和功能时钟初始化
对于CAN的引脚初始化,极其简单,只需要把引脚复用为CAN功能即可。参考hpm6750evk2的board中的CAN初始化。参考board_init_can API
对于CAN功能时钟的开启,在hpm6750手册中,默认时钟由时钟源PLL1_CLK1F五分频得到,PLL1_CLK1为400M,那么CAN的功能时钟就为80M.
同样sdk当中也做了相关时钟初始化,参考board_init_can_clock API
(二)波特率设置
对于CAN差分信号,这里不做阐述,本文统称的高低电平均为逻辑0和1。
跟uart一样,都是需要每个位的的时间确定,保持双方采样的准确。在CAN的概念中,有一个比较重要的次,BitTime(CAN位时间),这个决定一个bit传输需要的时间,这也决定了波特率。比如一个位传输200ns,那么1S就可以传输100000000(ns)/200(ns) = 5000000bit,也就是5Mbps波特率。
在先楫官方HPM6700_HPM6400_UM手册当中,对于CAN位时间有这么一段描述。
可以看到,一个位时序还包括了Sync_seg、Prop_Seg、Phase_Seg1, Phase_Seg2。Sample point指的是采样点,也就是这段电平保持时间中的采样位置点,比如200ns的位时间,采样点80%,那么就是在160ns中采样决定他是0或者是1。
在ISO11898-1:2015中,文档也说明了这部分参数的定义。
这里比较重要的是Sync_Seg,这部分的位时间指的是同步段,用来同步CAN总线的节点,若检测到的跳变沿被包含在此段的范围内,那么时序就是同步的,采样点sample_point采样到的电平就是该位的电平。上述可知道,该段的时间固定为1TQ
其他段时间均是相位的缓冲段,补偿边沿阶段的误差,保证位电平的稳定和重新同步。具体可看ISO文档解释。
如果两个节点位时间不同(各个位段时间不同),也就如果波特率不一样或者差别到一定程度(CAN的波特率有一定的范围,只要能保证采样点的位置大致相同),那么采样到的数据也有可能不一样。所以对于位时间的各个段的参数需要保持一致,以便通信同步一致。
如果两个节点位时间相同,CAN控制器会自身保持同步,同步的方式有两种:硬同步和重新同步。
这两种方式都必须遵守以下规则:
、一位时间内(两个采样点之间)只允许一次同步。 检测到边沿后,应禁用同步,直到下一次在采样点检测到的总线状态为隐性。
、仅当在前一个采样点检测到的总线状态(前一个读总线状态)为隐性时,边沿才应引起同步
、当节点处于总线集成状态时,在帧间间隔期间(除了间歇的第一位),以及在 CANFD 帧内,应在边缘上执行硬同步。也就是在SOF上开始硬同步,其他bit都在重新同步。
、满足规则 1 和 2 的所有其他隐性到显性边缘均应用于重新同步,但有一个例外:传输 CANFD 帧的节点在传输该帧的数据阶段时不应同步,而采取重新同步。
这里截取了ISO文档说明:
1、硬件同步
在以上规则中,我们可以通过检测SOF帧起始,SOF帧的开始就是在CAN总线空闲下(连续11个位的隐形电平,也就是逻辑1),一旦有显性电平出现,那就是有SOF帧开始,根据上述规则1和2,可以直接使用的是硬同步。硬同步会强制把位时间拉至边沿,保持同步。这个阶段不受同步跳转宽度SJW限制。
I SO文档同样也有说明:
比如:以下波形,当检测到SOF时候,CAN控制器需要满足ISO标准,执行硬同步。
2、重新同步
如果在仲裁段相当比较长的时间内,比如ID段,连续的传输会带来相位的左右偏移,这时候就需要重新同步了。这时候就需要SJW,对seg1和seg2适当进行延长或缩短一定的TQ。
这看起来有点难理解,那么还是以时序图来说明,以相位超前超后例子。
相位超前,CAN控制器会根据sjw同步跳转宽度进行加入对应的TQ,使之sync_seg段能同步到下一个边沿。
相位超后,CAN控制器会根据sjw同步跳转宽度进行减少对应的TQ,使之sync_seg段能同步到下一个边沿。
在上面的阐述中,采样点的取值范围尤为重要,对于同步上也是比较关键的参数,在ISO中并无此建议值,但是在hpm_sdk中有提及,建议是75%到87.5%。
从上面讲了一大堆,其实上面所阐述的一些同步均由CAN控制器实现,但是为了方便理解软件开发,是有必要了解。
CAN的位时序涉及到CAN时基、sync_seq、sjw、seg1和seg2。在先楫当中也有涉及到这些寄存器,分为仲裁段(标称位)和数据段位。需要注意的是:
先楫的CAN的seq1包括了位时序的sync_seg+prog_seg+phase_seq1。
先楫的CAN的seq2是位时序的phase_seq2。
根据ISO规定,sjw不计入到位时序中。
3、hpm_sdk的波特率设置API
sdk的CAN驱动的波特率设置,写的比较贴心,提供了两种方式来设置。一种是直接代入实际的波特率,第二种是自己写入位时序参数。根据use_lowlevel_timing_setting这个变量来决定哪种方式。
如果需要使能CANFD,则需要开启enable_canfd,超过1M的数据段波特率,建议开启enable_tdc。
这些参数的成员说明可以查看SDK的can_config_t结构体,这里不做阐述。
对于直接代入实际的波特率的方式,sdk使用ISO文档建议的位时序参数,适合通用的场合。
SDK中,根据波特率转换成对应的位时序参数。API接口是can_set_bit_timing。里面调用了can_calculate_bit_timing这个API得出位时序参数带入到CAN的对应寄存器中,完成波特率的设置。
can_calculate_bit_timing根据波特率和CAN时钟先算出TQ和分频系数相乘值num_tq_mul_prescaler,然后依次代入分频系数直到算出tq和分频系数等于num_tq_mul_prescaler,得出一个位时序的TQ数量,再根据采样点范围算出SEQ1和SEQ2,但会再匹配ISO建议的位时序参数的对应最小和最大值,保证在此范围内。否则返回错误。
需要注意的是:先楫的这个CAN外设的CANFD支持非ISO标准和ISO标准,也就是enable_can_fd_iso_mode,sdk默认是使用ISO标准,需要使用can_get_default_config这个API初始化参数。否则可能就是非ISO标准。
非ISO标准和ISO标准,对于经典CAN不受影响,但是CANFD会报CRC错误。
(三)收发数据流程
对于CAN帧结构,这里不做阐述。调用ISO文档的截图理解:
上述提到,对于接收来说,有16个FIFO;对于发送来说,有8个副发送缓冲器。这样对于收发来说是相当足够的。
在sdk当中,收发都提供了非阻塞和阻塞接口,分别对应的后缀是_noblocking或者blocking。
接收阻塞API:can_receive_message_blocking
接收非阻塞API:can_read_received_message
建议使用非阻塞API,开启接收中断以及满FIFO中断。在中断读取FIFO数量,最大性能提取数据,然后依次调用can_read_received_message。比如:
对于发送:
阻塞接口:can_send_message_blocking
非阻塞接口:can_send_message_nonblocking
建议使用非阻塞接口,每次调用前判断发送缓冲器是否满,然后依次调用can_send_message_nonblocking塞入数据。比如:
(四)实现效果
HPM6750的四路CANFD,在500K仲裁段5M数据段的波特率下,可以几乎跑满载。
实验平台:hpm6750evkmini+stephen大佬开发的CANFD适配扩展板。
三、总结
1、对于CAN的,本文主要阐述波特率设置、数据收发流程。其他的比如过滤器组设置、总线错误等后续再阐述。
2、先楫HPM_SDK对于CAN驱动,相关API接口以及结构体定义比较清晰,容易入手使用。
3、先楫的CANFD的收发自带FIFO缓存器,开发者可以根据需求使用,可以提高收发性能,达到满载测试性能。
- 2023-07-24
-
发表了主题帖:
[玩转先楫串口uart外设系列之一]利用硬件FIFO实现非阻塞方式发送
一、背景
在先楫HPM6000系列中,DMA控制器分别有HDMA和XDMA,每个控制器有8个通道,一共有16个DMA通道。但是hpm6000系列中的串口有些16个,有些8个等等。如果相对应的每个串口的接收和发送都使用了DMA,那么也不一定够用,而且有些重要的外设也需要DMA。在这种情况下,硬件的串口FIFO就有了实际意义了。
先楫hpm6000系列的串口,发送和接收都有16字节的硬件FIFO。
对于接收使用硬件FIFO,还可以使用接收FIFO timeout标志来接收不定长数据,这个先楫官方公众号对此方案进行了阐述(传送门:再谈 HPM6700/6400/6300 产品系列串口接收不定长数据的方式),对应的例子也在hpm_sdk 1.2版本当中,对应相对路径为 ../hpm_sdk/samples/drivers/uart/uart_rx_timeout
因此,本文只阐述发送端的利用硬件FIFO实现无阻塞发送机制。
二、适用性
相比单字节发送进入中断来说,可以减少16倍的中断次数,比如发送5112字节,不使用FIFO的情况下,发送需要进入512次中断,如果使用FIFO,那么只需要进入32次中断。
所以对于这种方式来说,有一部分应用场景可以适用。
1、使用的串口数量多,但是DMA通道不够用的情况。
2 、发送数据量不大的情况。
3、内部数据交互有ringbuffer,依次出列发送的情况。
4、需要非阻塞发送的情况。
5、实在不想用DMA但是想非阻塞发送的情况。
三、实现流程
对于这个流程,hpm_sdk同样也提供了对应例子,对应相对路径为 ../hpm_sdk/samples/drivers/uart/uart_irq。本文就以此例子作为本文阐述发送的sample。
该例子主要是演示串口中断收发功能,也就是串口的收发都在中断中完成,串口的收发可以通过使能FIFO_MODE来决定是否使用硬件FIFO进行收发。
例子中的CMakelists.txt中,宏定义了CONFIG_UART_FIFO_MODE,默认使能了硬件FIFO模式。这样我们可以根据使能和禁能硬件FIFO来测试发送的中断次数。
(一)配置流程
1、串口引脚和时钟初始化
需要注意的是,在自己的项目当中,需要先初始化好引脚,再开启工作时钟。否则可能会出现串口收发上电乱码的问题。这是因为这时候串口已经开始工作,而后初始化引脚后检测到串口电平的变化导致认为是数据产生。
在hpm_sdk中,每个官方开发板的board.c中对于串口的初始化都是基于上述配置。比如hpm6750evk2的board的串口初始化。内部函数也做了注释说明,为了其可靠性,建议开发者按照官方操作流程进行初始化。
2、串口配置初始化
对于配置初始化,比如设置波特率,parity,传输长度等等,在hpm_sdk中,已经封装为uart_config_t结构体进行描述,在其结构体成员中赋值对应成员,然后调用uart_init这个API接口,即可完成串口配置初始化。
在uart_config_t成员赋值中,驱动提供了一个缺省接口赋值,建议开发者可以进行调用,以便串口可以在缺省安全的参数启动。
(二)硬件FIFO开启
在hpm_sdk的uart_irq这个sample中,根据上述阐述,可以通过fifo_enable进行对硬件FIFO的开启和使能,对串口的数据寄存器,手册中,定义了两个寄存器,一个是接收一个是发送。
分别是RBR和THR寄存器。开发者可以根据FIFO模式进行对应的赋值操作。
在hpm_sdk中,对于这两个寄存器,注释也做了说明。
当为FIFO模式的时候,这两个寄存器相当接收和发送硬件FIFO,而当非FIFO模式,也就是BUFFER模式的时候,这两个寄存器仅仅是个字节缓存,也就是单个读写。
(三)填充FIFO,使能中断
经过上述初始化之后,需要开启相关中断,对于中断使能位,手册和SDK同样提供了对应了寄存器操作API。比如hpm6700系列。
在这里使能了发送FIFO为空时候产生中断,也就是上述的ETHEI位域,以便告知这时候发送的硬件FIFO是为空的,可以填充数据到FIFO。
当为非FIFO模式的时候,THRE标志产生的中断标志依旧可以作为发送单字节完成标志。
在发送利用硬件FIFO流程:
1、在填充硬件FIFO之前,需要关闭掉对应的中断,然后根据发送的长度和FIFO长度(16字节)比较得出最小长度。以便保证FIFO空间不溢出,塞满整个硬件FIFO,之后开启FIFO为空中断
2、进入中断之后,重复1的操作,进而在中断里面继续塞入硬件FIFO
3、在中断自己操作2,直到整个发送数据完成。
这个流程,SDK中的例子也封装好了演示接口
那么当接收收到指定的长度数据后,开始在中断调用以上接口,以此在中断循环上述流程。详细的代码参考可以在hpm_sdk对应的sample中看到。
四、验证
在官方的sample中,可以做个测试,以此来验证FIFO模式和非FIFO模式发送所需要的中断次数。收发数据固定为128字节。
定义一个中断次数变量,每当进入一次FIFO为空中断标志,累加1。发送完成打印中断次数。
可以从打印log看到,除了第一次配置发送不需要在中断,按16个字节FIFO,128字节需要填充8次FIFO,相比单字节发送中断次数需要128次中断,利用硬件FIFO发送只需要7次,缩短16倍以上的次数。
五、实战(ringbuffer+硬件FIFO实现异步非阻塞发送)
在实际项目当中,对于发送来说,有一部分利用ringbuffer 软件FIFO异步发送来实现无阻塞发送方案。
把需要发送的数据入列到软件ringbuffer,在一个线程或者状态机,轮询ringbuffer是否有待出列的数据,如果有就进行发送。实际发送的实现可以DMA发送,或者阻塞发送一定数据量。
在这里可以使用上述的阐述,流程如下:
1、在有待发送的软件FIFO出列时候,先判断下硬件FIFO是否为空,若为空,则关闭发送为空的中断,然后读取ringbuffer数据,塞入到硬件FIFO当中
2、当ringbuffer仍有数据,那开启发送为空的中断,若没有,那就是这一帧发送完成,不需要再开中断。
3 、进入中断后,循环1。
如此以来可以实现异步无阻塞发送,并且最大化减少中断次数。
串口中断判断是否为发送FIFO为空标志,若是,则硬件FIFO可以继续塞入数据,再次调用以上接口
效果如下:
六、总结
1、先楫对于串口这个外设,提供了16字节的发送和接收硬件FIFO,开发者可以根据自己需求对其进行利用。
2、对于DMA不适用的场景,比如上述的背景描述,可以利用16字节的硬件FIFO,相比非FIFO能减少16倍以上的中断次数来最大化实现发送性能,实现非阻塞式发送。
- 2023-07-21
-
回复了主题帖:
[玩转先楫SPI外设系列之四] 细说SPI从机发送和接收配置以及性能
jobszheng5 发表于 2023-7-21 10:20
SPI接口对于slave模式的应用,感觉用处也不大。
不一定,看自己需求吧。比如做jtag,可以双SPI做,一个从机一个主机。
- 2023-07-20
-
回复了主题帖:
[玩转先楫SPI外设系列之四] 细说SPI从机发送和接收配置以及性能
lcofjp 发表于 2023-7-20 16:55
必应这垃圾,竟然搜不到先楫官网,然后百度一下就有了
哈哈,我来发一个
先楫半导体 (hpmicro.com)
-
发表了主题帖:
[玩转先楫SPI外设系列之四] 细说SPI从机发送和接收配置以及性能
一、概述
对于嵌入式通用MCU来说,SPI作为从机的应用相比作为主机来说比较少,网络上无论是哪个品牌的MCU,对于SPI从机的介绍以及性能也是比较少。
先楫在SPI外设上,单从hpm_sdk上看,SPI外设的每个sample例子,比如poll方式、中断方式、DMA方式的例子都有对应的主机和从机例子,相对应提供了SPI主机和从机的操作方式。
本文就从hpm_sdk的spi sample例子入手,对于poll和中断方式本文不阐述,若需要了解差异可看该系列文章的SPI主机说明部分。
本文以spi的sample中的DMA例子来进行阐述,硬件上需要两块HPM的板子,比如两块hpm6200evk。以此来说明SPI从机发送和接收的性能。
二、HPM_SDK的SPI从机相关API介绍
SPI外设的大多API接口都可适用于SPI主机或者从机,本文主要介绍SPI从机需要的API接口
(一) SPI从机的功能时钟开启
hpm_sdk中,每个芯片的官方板子,也就是对应的board文件,比如hpm6200evk的board.c,里面包含了board_init_spi_clock这API,里面实现了SPI的时钟指向group以及时钟的来源。
可以看到,SPI时钟来源于PLL时钟的PLL0_CLK--400M,经过五分频得到的,也就是80M。本文就是基于这个SPI工作频率下进行验证。
需要说明的是:SPI作为从机的时候,对应的SPI主机设备驱动输出的SCK时钟必须小于等于SPI工作频率的1/4,也就是20M的SPI SCLK驱动。
(二)SPI从机的两种数据传输模式
SPI作为从机,对于数据的传输分为两种:纯数据模式和非纯数据模式。这个设置位在TRANSCTRL中的SLVDATAONLY位域。
那么何为纯数据模式呢,在UM手册可以看到SPI控制器对于SPI从机的介绍:
SPI作为从机模式下,如果不配置SLVDATAONLY这个位域,那么默认就是0,也就是控制器会把SPI总线上的信号,分别解释为命令、等待和数据。而且命令和等待段都是固定为8位。
这也就是说,非纯数据模式下,SPI从机在收到SCLK开始,前面的头16个时钟周期,也就是8个字节,会被从机认为是命令、等待段,其余的才是数据段。
这样就好理解纯数据模式下是什么意思了,纯数据模式下,SPI作为从机时,SPI控制器会把SPI总线上的所有信号都解释为数据段。
需要注意的是,在纯数据模式下,SPI传输模式只能同时读写模式,同时也只能用于常规模式(1线模式)。
可以做个实验验证下,在保持SPI DMA的master sample保持不变,也就是SPI主机会发命令和地址段。在SPI从机看来就是命令和地址段。
当为非纯数据模式下时,SPI从机的数据段不会带有SPI主机发过来的命令和地址字节。
当为纯数据模式下,SPI从机会把SPI总线上的所有数据识别为数据段。
对应的API接口可以在spi_control_config_t结构体里面的spi_slave_control_config_t结构体
对应赋值API接口主要有传输API和SPI控制配置初始化
在进行配置传输的时候,比如spi_setup_dma_transfer 这个DMA方式的API,这个API在SPI作为从机上,主要调用了上面的spi_control_init API对SPI的TRANSCTRL寄存器进行配置,主要就是配置数据传输是否为纯数据模式,以及SPI传输模式、然后开启接收或者发送DMA。
开发者使用该API,只需要赋值对应的结构体成员,然后进行调用。比如本文使用的SPI DMA中的SPI slave 这个例子.。
配置好SPI之后,需要配置下DMA,比如DMA的源地址和设备地址等等。之后等待从机的收发数据计数是否到了配置TRANSCTRL寄存器的收发长度。
对应的API为spi_slave_get_sent_data_count 和 spi_slave_get_received_data_count 两个API
详情的流程可以参考hpm_sdk中的sample/drivers/spi/dma的代码。
三、SPI从机收发性能验证
SPI的功能时钟,手册中写的是80M,SPI作为从机的时候,SPI总线的频率时钟不能大于四分之一SPI功能时钟,也就是20M的SPI SCLK时钟。
需要注意的是:测试收发的数据要放在AHB SRAM当中。
(一)SPI 20M SCLK时钟驱动符合手册预期
sample的例子也是基于20M进行验证。下载之后查看双板log以及示波器,可以看到SPI总线的SCK时钟保持连续,并且双板通信收发正常。
在SPI 25M SCLK时钟驱动依旧收发正常。
(二)SPI 25M以上 SCLK时钟驱动数据收发不正常 (根据自己应用进行小技巧纠正)
在手册规定下的SPI总线20M的时钟下,对于SPI从机来说,25M以下的频率,SPI从机还能来得及采样。但一旦超过25M之后,SPI从机对于总线上的数据,由于来不及采样,导致可能会出现第一位或者第二位出现丢失导致数据收发异常,从而导致数据接收左移。
比如在SPI总线40M SCLK情况下,会遇到由于SPI从机没采样到第一位,导致后续的数据位全部左移一位。
sdk的sample使用的SPI模式是模式3,也就是总线空闲时是高电平,第1个跳变沿是下降沿,第2个跳变沿是上升沿,数据在第2个跳变沿(上升沿)采样。
1、从机接收纠正小技巧
从机的SCLK时钟由主机提供,接收主机的数据的时候,所以无论是哪种方式,都会由于采样速度慢于主机的原因,延后一个或者两个的采样位,导致数据产生偏移。那么软件上可以通过偏移的采样位进行右移得到正确的数据,比如从机收到的是0x02,采样位偏移1位,那么右移可以得到0x01。则正确对上数据。
SCK的时钟阴影部分表示从机丢失的时钟采样。
2、从机发送纠正小技巧
跟上面同样的原理,由于主机采样比从机快,所以不存在数据左移的情况。那么从机的SPI模式可以改成跟主机的SPI模式相反。
比如主机的SPI模式是模式3,也就是总线空闲时是高电平,第1个跳变沿是下降沿,第2个跳变沿是上升沿,数据在第2个跳变沿(上升沿)采样。
而从机可以配置为模式0,也就是空闲时是低电平,第1个跳变沿是上升沿,第2个跳变沿是下降沿,数据在第1个跳变沿(上升沿)采样。
SCK的时钟阴影部分表示从机丢失的时钟采样。
例子可以这么改动。
编译后烧录,SPI主机从机的发送和接收,可以看到:
SPI主机发送给SPI从机的数据,从机显示总会左移一位。可以通过软件处理偏移,但不推荐。
而SPI从机发给SPI主机,由于双方的模式相反,根据上述阐述,主机收到的数据跟从机发送的一样。
四、总结
1、先楫在SPI从机功能时钟为80M下,从机收发可以达到20M到25M左右,单线SPI可以2.5MB/s左右速度。达到这符合官方手册说明。
2、可以通过提升SPI功能时钟提升从机收发性能,但不保证功能是否正常,不在本文和官方手册阐述范围内。
3、由于再高的SPI SCLK,会导致从机时序采样过慢导致数据收发异常,如果想继续提升从机的收发性能,可以通过一些小技巧来避免时序错位的问题。但这些小技巧取决于自己的应用,不在官方手册阐述范围内。
-
回复了主题帖:
[玩转先楫SPI外设系列三] 以读写串行NOR flash为例总结SPI主机性能
火辣西米秀 发表于 2023-7-19 23:09
看来华邦W25Q64JV还是挺不错的,毕竟是老牌子的
1、本文重点不是介绍华邦这个flash,而是先楫这个spi外设的qspi接口,完完全全可以达到理论性能。2、这颗芯片不算可以,算是中等,编程和擦除时间太久,擦除4k需要几百ms,国产的都能比这个快。3、只要是遵守sfdp标准的flash,都算可以,因为可以减少客户研发成本,替换啥flash都可以做到通用。
- 2023-07-17
-
回复了主题帖:
凑了Arduino三兄弟,亲哥俩和表弟
队长,赞!!!!!唤醒水群,血脉觉醒!
- 2023-07-16
-
发表了主题帖:
[玩转先楫SPI外设系列三] 以读写串行NOR flash为例总结SPI主机性能
一、题外话
本文主要是使用NOR flash作为从机设备,以此验证SPI主机性能,不限与驱动flash器件。
对于真正有实际nor flash器件驱动的需求,先楫提供了更为方便性能更优的外设-XPI外设。不但支持多种外部存储器,而且这次1/2/4/8数据模式,重点是只需要调用ROM API接口,无需自己写驱动!
本文主要介绍SPI外设,XPI外设不在本文讨论范围。
二、目的
在系列中的前三篇中,分别介绍了SPI外设的收发性能最大化实现方案以及谈谈先楫hpm_sdk的组件serial_nor--SFDP定义。sfdp可以帮助我们得到flash器件的相关器件,比如擦除、读写指令。由于先楫HPM6000系列一次收发传输是512字节(据说后续的新系列不会限制在512字节),对于flash器件来说,从读flash上来看,可以读取超过512字节的地址数据长度,从写flash来看,最多一次写一页(256字节或者512字节)。所以从实际应用出发来看,SPI传输不会限制在512字节内。
要使用超过512字节的传输量,单从性能角度出发,无论是1线还是4线传输模式,肯定是DMA方式效果最佳。这也是本文的讨论重点和目的。
三、理论读写性能
在验证SPI主机性能之前,我们需要知道从机器件的性能。比如写一页256字节和擦除一个sector或者block的性能。
本文使用的器件还是华邦W25Q64JV,从页编程来看,一次页编程经典是0.3ms,擦除4K sector需要45ms经典值,假设从单纯编程上看,写速度大概是625KB/S经典值.
从读来看,主要看flash支持的SPI频率。从手册看,读取03H普通读取指令以外的指令(比如EBh 四线读取)可以到133M,那么理论上四线SPI可以到66.5MB/S最大值,单线SPI主要只能对接03H指令,那么最大50M频率,最大可以6.28MB/S
本文限制于杜邦线连接FLASH,所以四线QSPI四线速度限制在50M,当然如果接在PCB上,那么SPI外设的80M想必也没什么问题。
所以,在四线QSPI频率50M下,对本文所用的flash器件,理论编程速度应该是625KB/s,而读取速度理论应该要25MB/S.
三、验证理论读写性能。
(一)先楫HPM6000的DMA概述
先楫的HPM6000系列的DMA包括两个控制器,一个XDMA,64位总线宽度,连接在AXI总线上;一个HDMA,32位总线宽度,连接在AHB外设总线上。DMA请求路由器一共16个通道,两个控制各划分为8个。
DMA支持非链式传输,也支持链式传输。链式传输可以在CPU不干预的情况下,连续完成多个不同的配置传输任务。也就是说,每个链存放着配置传输的描述符,但每个链传输完毕,只要下一个链地址是合法的,那就接下去传输。但需要注意的是,描述符最多只能4K内存。而每个描述符的格式占用32字节。
本文就以非链式DMA和链式DMA来进行说明传输性能的优化。
(二)SPI启动传输流程
在配置好SPI的相关初始化之后,比如引脚初始化、SPI模式、时钟分配等。之后就是传输过程。
传输过程需要以下三步:
1、配置TRANSCTRL寄存器,该寄存器主要是配置地址段和命令段使能和禁能、数据段格式(1/2/4线)、传输模式(比如同时读写、只读、填充dummy后读等)、写入长度、读取长度等
2、配置CMD寄存器,该寄存器在SPI作为主机下,无论是否禁止命令,都需要为之赋值,因为一旦赋值就是标志开始传输,根据需要传输的写入或者读取长度发出相对的SCLK时钟数量。
3、之后在DATA寄存器读取或者写入对应的数据,如果是轮询读写FIFO方式,那么就是阻塞收发FIFO,如果是DMA方式那么则需要配置DMA的源地址和设备地址等参数。
对于这些传输过程,hpm_sdk已经有对应的API接口:
对于轮询方式,可以见该文章《先楫hpm6000的SPI外设使用四线模式操作读写华邦flash》,本文不再做相关阐述
对于DMA方式,SDK提供的API接口是spi_setup_dma_transfer,之后开发者需要自己配置好DMA参数,然后再调用dma_setup_channel这个API即可开启传输。无论是发送还是接受,均可使用该接口。详细参考也可以看hpm_sdk中的sample/drivers/spi文件夹下的DMA例子。
(三)非链式DMA传输
由于flash在读写操作API上,从用户的角度看,接口传参只要读写的buffer地址,以及传输的长度即可。内部逻辑并不做任何关心,在这里本文也封装了两个接口。分别是program和read APIS。
从program API看,内部需要对其传入的编程长度进行拆分,对应也对编程地址和buffer地址进行对应的拆分长度偏移,再者根据flash的地址模式(24位还是32位)通过SFDP获取到的对应的Program指令。加上还需要配置DMA参数,所以这部分也需要一定的指令执行时间损耗。在循环DMA写入页的时候还需要等待页编程完成,也就是需要0.4ms的等待时间。
从read API看,由于hpm6000的最大收发长度为512字节,内部也需要对齐进行读取长度的拆分,然后也需要根据flash的地址模式(24位还是32位)通过SFDP获取到的对应的Program指令。加上还需要配置DMA参数,也需要一定的指令时间,但在读取的过程中,不需要等待flash busy状态,发完512之后继续再发下一帧512字节,以此循环。
为了最大减少指令的执行时间,对于HPM的DMA来说,每次传输完之后需要重新配置传输长度以及传输的源地址和设备地址,再重新启动DMA,这些单独的配置同样HPM也有提供。可以以此做个独立的API接口。
为了验证大数据量下的DMA方式读写flash收发传输性能,本文以SPI频率50M, 15K数据进行读写flash,开启最大优化O3。另外加个IO来测试上述API的执行时间。
以计时差计算15K传输的速度,如下所示。从上述自身flash器件的编程性能630KB/S.在这里是明显达到编程性能的。但是从读速度来看,理论25MB/S,但实际22MB/S。
通过逻辑分析仪抓取SCLK、CS、还有IO测试脚看到:
在进入读取API的时候,并不是立马就执行发送,而是有个配置逻辑处理时间,以及每包的DMA配置时间。
1、测试读取API,接收15K数据,总的耗时是692.742us,也就是22.172MB/S,与打印的一致。
2、总的耗时,主要费在入口拆包以及传输配置耗时11.666us,还有每个512字节包DMA配置传输的总耗时56.5us.两者费时68.166us
3、那么实际传输数据的就692.742 - 68.166 = 68.166us.也就是15360/624.576 = 24.6MB/S. 这段还包括了地址和指令的传输,这样看来,DMA实际传输还是能明显达到理论的25MB/S
(四)链式DMA传输
在DMA非链式传输当中,主要费时还是在于每包512字节传输后再次配置DMA的耗时,虽然每包耗时1.8us左右,但总包数累加,还是有一定的时间损耗。那么有没有办法消除该每包之间的间隔呢。由上述的说明可知道,链式DMA只要在传输之前把SPI的传输流程放在DMA描述符当中,那么这样就不需要CPU去干预,而让DMA自己去执行。这个思路也一同验证下。
1、SPI一次传输需要配置TRANSCTRL、CMD、DATABUF DMA配置,所以每组SPI传输链需要三个DMA描述符,每个描述符占用32字节,DMA描述符边界长度是4K,那么非链式传输最大可以传输4096/(32 * 3) * 512= 21845字节,也就是21K传输长度。描述符的写入同样也有对应的API接口-> dma_config_linked_descriptor
2、那么启用第一次DMA传输的时候,只需要把linked_ptr地址指向写好的描述符空间首地址即可。
再次验证下,可以看到链式DMA反而比非链式DMA提升不大,而且还慢了些。
一样通过逻辑分析仪测试SCLK ,CS,测试IO上看。
为什么链式DMA传输会比非链式传输慢了点,从分析仪可以看到,有以下几个原因:
1、配置DMA描述符等工作执行耗时较久,耗时49.088us。这由于数据地址和数据长度的不确定,描述符在接口内部填充是无可避免的。
2、链与链之间会有间隔时间,也就是图中的1.266us,这是执行CTRL和CMD寄存器以及写CMD确定发送的延时间隔,这时间合理,也是无可避免。
3、由于SPI一次传输,DMA描述符里面有两个寄存器的DMA传输,但DMA传输对于大数据量传输能减少时间,但对于寄存器的赋值,hpm6000高达几百兆的主频,往往几个指令就能搞定的事情,所以与非链式DMA上每包传输的配置DMA间隔也跟链式DMA链间隔时间差不多,设置CPU的能够更快。
4、那么实际传输数据的就613us(650 - 1.26 * 30).也就是15360/650 = 25.03MB/S. 这段还包括了地址和指令的传输,这样看来,DMA实际传输也是能明显达到理论的25MB/S,甚至比非链式DMA快。
四、总结
1、先楫HPM6000的SPI外设由于传输长度限制512字节的原因,在大数据量传输中,需要进行分包非链式DMA传输,或者使用链式DMA传输,在指令优化下,也同样能达到理想速度。
2、对于SPI外设来说,链式DMA传输,需要一部分执行时间用来填充DMA描述符,但填充完之后的DMA数据传输不需要CPU干预,这比分包非链式DMA传输更加快些。
3、非链式DMA,每包需要重新配置DMA,这会带来每包的间隔时间,这需要开发者自身优化,但由于hpm的主频足够高,一般间隔也不会超过1.28us。
4、链式DMA传输,如果传输的是大数据量的数据,那么优势最为明显,但是对于一个寄存器的DMA传输,hpm的主频优势,CPU执行赋值会比DMA更加快。
5、从SPI外设来看,链式DMA由于每组SPI传输需要传输两个寄存器值,效果上没有比分包非链式DMA效果明显,从易用性来看,优先考虑分包非链式DMA传输方案。
6、hpm的SPI外设主机功能,至此结束。
- 2023-07-15
-
发表了主题帖:
谈谈先楫hpm_sdk的组件serial_nor--sfdp定义
一、概述
在串行flash中,比如nor flash,由于flash厂家品牌较多,对于用户来说,一些操作比如扇区和块擦除指令,大小等等,能够做到统一指令更好,但事实上,并非所有的厂家都是这么做。所以有这么一家组织叫JEDEC定义了一套适用串行flash的标准来进行规范化,也就是JESD216标准。
该标准从最初的JESD216发展到现在,最新已经是JESD216F.02了。发展历程和版本的不同点,也在协议文档中一一举出,这里不做阐述。最新的JESD216文档可以自己下载了解。
那么这个标准中,对于上述的指令差异,标准定义了一个table表,也就是本文需要阐述的重点,SFDP。该表有很多para table,每个table都会有描述flash相关的参数指令,比如上述所说的扇区擦除指令,只要该flash遵守该标准,那么就可以在para table中读到该扇区擦除指令。
本文以W25Q64JV的华邦flash、JESD216D版本为例阐述。
二、定义
JESD216标准文档长达162页,不多也不少,但是如果需要自己去源码定义这么多table,那是有点难以短时间能够接受过来。
hpm_sdk中的组件components中,有个serial_nor组件文件夹,里面有个sfdp_def.h头文件,从命名可知道,这是一个关于sfdp的相关定义。这些定义和结构体能够帮助快速去了解其JESD216标准。
该文件描述的最新标准是JESD216D,本文就基于该版本进行简单的阐述说明。
(一)SFDP表读取
在标准文档中,开头就是描述了读取SFDP指令处理,描述中主要大致理解为:
1、SFDP读取指令是0x5A,hpm_sdk中对应的宏定义是kSerialFlash_ReadSFDP
2、SFDP区域中,想读取的地址空间,都为三字节字段,也就是24位地址。
3、在要读取数据之前,也就是发完地址之后,必须有8个spi clock时钟的dummu等待。比如单线SPI读取的情况下,那么需要一字节的dummy。四线读取下需要4个字节dummy。(一般都是采用单线SPI读取)
4、SPI时钟最好控制在50M以下读取。
由上述描述,我们可以利用hpm的spi驱动封装个读取sfdp的API接口。
static hpm_stat_t hpm_spi_nor_read_sfdp(SPI_Type *ptr, uint32_t addr, uint32_t *buffer, uint32_t bytes)
{
spi_control_config_t control_config = {0};
uint8_t cmd = kSerialFlash_ReadSFDP;/*指令为5A*/
/*地址设置24位*/
ptr->TRANSFMT |= SPI_TRANSFMT_ADDRLEN_SET(sizeof(uint32_t) - 2);
spi_master_get_default_control_config(&control_config);
control_config.master_config.cmd_enable = true; /* cmd phase control for master */
control_config.master_config.addr_enable = true; /* address phase control for master */
control_config.master_config.addr_phase_fmt = spi_address_phase_format_single_io_mode;
control_config.common_config.trans_mode = spi_trans_dummy_read;
control_config.common_config.data_phase_fmt = spi_single_io_mode;
control_config.common_config.dummy_cnt = spi_dummy_count_1;
return spi_transfer(ptr, &control_config, &cmd, &addr, NULL, 0, (uint8_t *)buffer, bytes);
}
(二)SFDP标头结构
在标准中,该结构的地址在0x000000。标头结构包括了:SFDP Header和Parameter Header两个标头,而Parameter Header可以是多个,这取决于SFDP Header的NPH参数,这个参数决定了Parameter Header的数量,而Parameter Header的parameter length又决定了parameter table的数量。
所以如果需要读取parameter table,必须得先读取SFDP Header以及Parameter Header。
1、SFDP Header
在标准文档中定义知道,SFDP Header的结构如下,同样hpm_sdk中的sfdp_def也实现了定义。
signature就是标识符,ASCII就是“SFDP”,需要读到该标识符才可解析。
param_hdr_num:指的是parameter header的数量,长度+1。
在这里需要解释下minor_rev主版本,在上面中,hpm已经定义了版本,比如JESD216D就是 kSfdp_Version_Minor_D。
#define kSfdp_Version_Major_1_0 (1U)
#define kSfdp_Version_Minor_0 (0U) /* JESD216 */
#define kSfdp_Version_Minor_A (5U) /* JESD216A */
#define kSfdp_Version_Minor_B (6U) /* JESD216B */
#define kSfdp_Version_Minor_C (7U) /* JESD216C */
#define kSfdp_Version_Minor_D (8U) /* JESD216D */
这些定义是在哪里看到的,我们可以看到版本差异的一节,可以看到,比如JESD216B定义为5
2、Parameter Header
这里hpm_sdk的sfdp_def同样有定义。
需要解释三个参数。
parameter_id:这是表示当前的flash支持的指定功能prameter table的ID值,比如Basic SPI protocol这个ID值,表示该flash支持24bit地址下常规SPI操作,比如是否支持1-4-4 qspi读取,flash的容量空间多少,擦除扇区指令等等。通过该参数可以知道flash支持的功能。这个参数分为MSB LSB,需要整合一起。
这个参数,可以在标准中找到,同时hpm_sdk中的sfdp_def也同样定义了。
table_length_in_32bit :parameter table的数量,需要注意的是,这是四字节对齐,比如该值为2,那么就需要2*4=8,表示友8个parameter table表。
parameter_table_pointer[3]: 表示读取parameter table的24bit 地址,从这个地址读取所有的parameter table。
3、Parameter table
table表比较多,特别是支持Basic SPI protocol的ID,支持20个字的参数。每个字都有指定的意义。不过hpm_sdk的sfdp同样也做好了定义
三、验证
比如截取3rd parameter table。该table定义了1-4-4(cmd一线,地址和数据四线)读取指令:需要的dummy clock、需要的mode dummy clock,指令值。
那么我们先读取该table值,可以看到:
那么我们翻开操作的flash型号的手册,查找到0xeb指令。也就是在QSPI下的读取指令。从时序图可以看到。
说明sfdp表读取出来的参数跟flas手册对应的指令参数完全一致。
- 2023-07-06
-
回复了主题帖:
[玩转先楫SPI外设系列之二] 细说SPI主机接收性能最大化实现方案
jobszheng5 发表于 2023-7-6 10:10
我还以为QSPI是一个很简单的操作呢!
看了楼主的描述,还是挺难的,需要些调试时间才能搞定的
不不不,如果你使用先楫的单片机,那么很简单,使用QSPI只需要spi drivers里面的spi_transfer这个API即可,相关的结构体开启四线QSPI。
本文只是更多讲解这个外设的实际性能提高到理论速度性能的优化方式。
- 2023-07-02
-
发表了主题帖:
[玩转先楫SPI外设系列之二] 细说SPI主机接收性能最大化实现方案
一、概述
经过[玩转先楫SPI外设系列之一] 细说SPI主机发送性能最大化实现方案的文章,我们可以照葫芦画瓢来进行一个SPI主机接收性能实现方向的方案,本文基于这个进行说明。
验证条件是:使用SPI四线模式(QSPI),从机器件为华邦flash--W25Q64,读取一页数据256字节。
二、步骤
(一)读取页数据分析
从华邦对应的手册来看,QSPI读取数据的指令之一是Fast Read Quad I/O---EBh,从手册可以看到,该指令操作是:
指令(8 number clock) + 地址(3 * 2 number clock)+ dummy (3 * 2 number clock) + DATA(2 number clock)
(二) 代码实现
从(一)可知,SPI主机需要开启命令段和地址段,地址段是24位并且是QSPI模式发送,读取需要发送三个dummy。那么基于上篇的SPI发送DMA配合一起,可以这样实现:
封装的spi_rx_trigger_dma API如下:
(三) 现象不符合预期
试着烧录代码之后,发现在20M左右的SPI频率,接收时候SPI SCLK波形能够连续,但再高之后的SPI频率,就会出现接收到一定数据量后突然产生间隔,之后会继续连续,这种间隔产生是不符合预期的,也会导致接收速度性能达不到理想速度。时间间隔长达2.41us。
(四)实现预期效果
从上述上看,在接收到20个字节突然SPI主机就不发时钟,待到一段间隔后,才重新开始,这看起来就是DMA还没启动,但是SPI已经开始传输。
从flash角度上看,当接收到对应的指令后,flash这个从机已经准备好数据,等待SPI SCLK时钟;从SPI主机来看,先楫的SPI接收FIFO长达4个字,也就是16个字节。那么会不会是DMA还没配置和启动,SPI就已经开始传输了,然后等到DMA启动之后,才开始从SPI FIFO搬运。
以下做个单步调试,打断点到配置SPI和SPI DMA的API接口spi_setup_dma_transfer上。
进行到下一个语句,逻辑分析仪发现,SPI已经开始在传输,但是这时候DMA尚未配置和启动。可以理解为,由于SPI已经提前配置后开启靠着FIFO接收一定数据量后阻塞了总线,之后配置的DMA这个流程也需要一定的耗时,所以也会造成一定的间隔产生。
由此可知,我们需要先配置好DMA以及开启DMA,再配置SPI启动,这样可以让DMA时刻在等待SPI的事件请求。把spi_setup_dma_transfer 这个API放于spi_rx_trigger_dma这个API之后,从逻辑分析仪可以看到,SPI SCLK以及可以保持连续。
(五)再进一步(使用SPI merge功能)
上述的都是基于数据量为四的整数倍的接收,如果遇到不是整数倍的,如果使用merge功能,会导致数据合并错误的问题。但我们可以从DMA上去入着手点:
1、照样开启SPI的merge功能
2、DMA传输使用四字节宽度,突发传输长度保持不变。
3、遇到不是四的整数倍,强行四的整数倍的数据量,并且这个数据量作为DMA传输长度,但源数据量长度保持不变。比如要接收252字节,给SPI的传输长度是252字节,但是给DMA传输的长度是补满4字节,也就是256字节。
4、SPI传输完毕后,需要再判断下DMA是否传输完成以便复位下DMA,方便下次收发。
通过示波器可以看到,接收到252字节,SPI就停止传输了。与预期的符合。
三、总结
1、先楫的SPI主机无论从发送还是接收角度上看,都可以满足手册所标的SPI频率,并且可以做到SCLK不连续。
2、性能的提升需要借助外设的细节功能以及场景需要,需要开发者从自己的应用角度出发优化。
-
回复了主题帖:
[玩转先楫SPI外设系列之一] 细说SPI主机发送性能最大化实现方案
火辣西米秀 发表于 2023-7-2 09:37
在二问题的所有描述当中,速度达不到理想性能,归根到底是字节之间产生间隔累积形成。
这个 ...
1、比如在单线SPI模式,每个字节需要8个clock,下一个字节也应该保持8个clock连续,如果出现下一个clock有一段明显的低电平或者高电平间隔,看起来就像是spi sclk时钟线断了一样。
2、按1描述,如果每个字节之间都带有这样的间隔,那么一帧,加入200个字节,那么就带来200个间隔时间累积产生。传输时间又加长。
3、也许你可以随便找个单片机,使用DMA进行收发,你可以看下分析仪或者示波器看下SPI SCLK波形。
- 2023-07-01
-
发表了主题帖:
[玩转先楫SPI外设系列之一] 细说SPI主机发送性能最大化实现方案
一、背景
在先楫hpm6000的SPI外设使用四线模式操作读写华邦flash 一文中介绍了先楫SPI外设是为flash器件而生的控制器,但是楼主在该篇文章读写flash的页是用的poll轮询读写spi fifo的接口,并没用DMA来进行加速优化。本篇就是基于SPI配合DMA实现理论速度性能。
二、问题点
(一) SPI FIFO poll阻塞发送无法发挥SPI理论速度性能
使用spi poll阻塞的时候,虽然能实现数据的完整传输,但是传输的时间并不能达到理想传输速度,比如SPI四线模式下,30M的SPI SCLK时钟,理论可以达到15MB/S速度。但实际测量当中并未达到该性能。从逻辑分析仪看到,发送flas一页数据,也就是256字节,从开始传输到结束传输的时间需要37.034us,合计为6.91MB/S,与理论速度相差了2到3倍的距离。
从波形上看,导致这个速度达不到原因就在于,每个字节之间存在了一定的间隔时间,这些间隔的累积导致传输时间变长,导致总的时间变长,进而速度远远跟不上。
(二)使用了DMA仍然有SPI SCLK时钟不连续问题
从(一)的问题可以看出,要想达到理论速度,必须消除每个字节的SCLK间隔,缩短传输时间。这时候需要DMA来加持速度性能,但实际上,在使用了相关配置之后,速度虽然有些提升,但还是存在些许间隔产生。
在这里的例子验证条件是:SPI SCLK时钟频率为50M,主机发送512字节。理论传输速度可以25MB/S.
从逻辑分析仪可看到,间隔有所缩短,但依然存在字节间隔。512字节传输需要45.97us,合计为11.173MB/S。距离25MB/S也有两倍的差距。
三、解决问题
在二问题的所有描述当中,速度达不到理想性能,归根到底是字节之间产生间隔累积形成。
所以我们的问题解决点是:再配合DMA,进行其他优化。达到理想速度性能。
(一) 使用AHB SRAM(内存32KB空间)作为数据交互RAM。
在HPM6000系列中,AHB/APB外设总线连接了一个内存为32KB空间的AHB SRAM,与之同时连接的也有DMA控制器之一HDMA。
从官方文档可知,AHB SRAM和HDMA以及SPI外设同样位于AHB/APB外设总线中,AHB SRAM是专门给HDMA进行低延时访问的内存,也是SPI进行DMA低延时传输保证。
在上面的DMA搬运中,待发送的数据放在AXI SRAM中。那么把这发送的数据放在AHB SRAM,看下会不会有所提升。
从以下逻辑分析仪结果看出,传输512字节,相比放在AXI_SRAM中,在AHB_SRAM只需要22.97us,缩短了23us, 合计22MB/S,提高了两倍速度性能。当仍与25MB/S理想速度有些许差距。
通过放大波形查看,有些字节依然产生间隔,这也是导致速度没达到理想速度的原因。
(二)使用DMA的burst突发传输
先楫的DMA,对于源地址数据来说,支持突发传输。例如传输位宽为8,设置burst数量为4,那么就是相当一次DMA请求设置了4个节拍,连续传输4个字节。是单次传输的4倍效率。在这里来说,待发送的数据就是源地址数据。
先楫的SPI控制器有四个字的FIFO数据空间,每个FIFO是32位。SPI请求DMA搬运是通过发送FIFO阈值请求。从效率上来看,最好是一次请求中能把FIFO数据全部搬运。从上面的优化流程来看,都设置为了默认,TX FIFO阈值设置为了0,也就是只要TXFIFO有一个为空就请求一次,DMA的源数据burst数量为0,也就是相当设置了1个节拍的突发传输,传输宽度为8位,一次DMA请求就塞给一次FIFO,等待FIFO完全塞满后这时候没法请求,所以会导致一次周期的间隔,当DMA收到请求后连这样能解释上面为何每隔四个字节会产生间隔的原因。
所以这里我们可以SPI的TXFIFO阈值为3,当出现一个空位的时候就请求一次,设置burst数量为2,也就是四个字节,一次请求搬运四个字节。通过逻辑分析仪可看到:
配合(一)的方案,传输512字节,只需要20.468us,合计为25MB/S左右,接近了理想速度性能了。
放大波形查看,也能看到SCLK时钟连续了。
(三)压榨性能(使用SPI的字节合并merge功能)
先楫官方手册说明的是SPI时钟可以80M,保守是40M。在四线模式下,SPI时钟SCLK为80M,相比单线来说可以提高四倍性能传输,也就是可以达到40MB/S。
但是在实际操作的时候,分频SPI SCLK频率到66M,又出现了SCLK时钟不连续的情况,导致与理想速度不符合。
仔细翻下官方手册,可以知道SPI有个寄存器是TRANSFMT,有一个位是DATAMERGE,对于描述如下:
特别说明的是,由于SPI的数据FIFO是32位,这个功能只在数据单位长度为8位的时候有效,而且合并的数据量需要以四的整数倍。如此来说,在配置DMA的时候,传输宽度可以从8位变到32位,传输的带宽也能提高了四倍。那么我们这样配置下,逻辑分析仪结果如下:
在66M的SPI SCLK时钟下,逻辑分析仪抓到的SCLK能保持连续,并且数据能对的上。
512字节耗时15.352us,合计33MB/S左右,与理论速度33MB/S接近。
80M的SPI SCLK频率,传输512字节,耗时12.794us,合计40MB/S左右,也能满足预期40MB/S速度。
SCLK波形也能保持连续。
四、号外(单线SPI总线可以达到120M)
四线模式既然能达到80M,那么楼主想试下80M的单线,也是没问题的,效果如下:
再尝试一把,把SPI SCLK分频到120M,只是稍微有点间隔,但单线SPI也是没问题的。
五、总结
1、对于先楫这个SPI外设来说,配合DMA,SPI的数据FIFO以及相关SPI配置,能达到手册描述的性能。无论是四线模式还是二线模式还是单线模式,都能到达80M的SPI时钟性能。
2、对于SCLK不连续的问题在于DMA搬运和SPI传输不同步造成,导致传输间隔中断,特别是SPI频率越来越高的情况下。解决同步问题就不会有SCLK不连续的问题存在。
-
回复了主题帖:
【雅特力AT-START-F437评测】4. SPI2 + DMA 读写测试
DMA发送,SPI的SCLK波形是否连续,楼主有用逻辑分析仪抓过么,连续SCLK波形决定是否有50M的性能