个人成就
- 发布了43篇内容
- 获得了2次赞同
- 获得了5次收藏
个人简介
擅长领域
暂时没有设置哦~
-
RISC-V鸿蒙开发板单片机简介(项目连载8)
我们使用的单片机是深圳睿思芯科公司出品的Pygmy-E系列的单片机,它是面向IOT超低功耗32位SOC,内含睿思独立设计的ORV32 RISC-V内核,实现了RV32IMC标准指令扩展。主要用于IOT终端设备的感知、控制、连接等领域,如智能家居、智能控制、智慧工业、智慧园区等。
这款单片机主频可以高达100Mhz。在存储方面,一级缓存8KB,二级缓冲256KB,存储容量还是比较大的。同时它支持丰富的外设接口,比如GPIO,UART,SPI,IIC等,可以实现复杂的通信及控制功能。
在低功耗方面,Pygmy-E拥有极低的功耗表现,通过对CPU极致的低功耗微架构设计和对SoC系统层级进行深度优化设计,动态功耗远优于同等计算性能的ARM芯片,并且实现的uW级别的SOC待机功耗。
整个处理器的框架图,是如下这个样子,供大家参考。
我们后面会分几个模块来介绍这款单片机,比如GPIO接口、I2C接口、SPI接口以及中断系统等等。这篇文章里面,主要介绍系统时钟以及GPIO接口的一个关键点,介绍这个的时候,我会尽可能的和ARM架构的单片机进行比较,在对比中把这款单片机架掌握得更加深刻。
1. 时钟
整个系统主频和外设的频率(SysTick定时器,UART,I2C,SPI等)是可以自己设置的,不过从目前我这边拿到的资料来看,在时钟树这块,它的架构不像ARM内核那样可以通过对RCC寄存器的配置来统一对时钟进行管理。
不过这并不影响单片机的使用,只是在配置时钟这块需要对各个外设寄存器有更加深入的了解,因为外设时钟信号的配置更多是在这里进行的。
我们这里外部使用的是20Mhz的有源晶振,实际系统中的主频也就配置成了20Mhz。
1. GPIO
这款芯片的外设使用的是新思科技的模块,对GPIO口复用的配置主要是通过各个接口的SEL和DIR两个寄存器来实现的。SEL和DIR寄存器的值都为0的时候,使用的是复用模块的功能;SEL和DIR寄存器中的值均为1的时候,单片机使用的是GPIO口模块的功能。但是每个接口模块,只能有一个复用功能,这个地方不同于我们经常使用的STM32单片机。在STM32单片机中,大部分接口都会支持好几个功能模块的复用。
从上图中,大家可以看出来每一个端口目前只有一个复用功能,我们在实际进行端口配置的时候,灵活度相对来说要小不少。
大家可以看一下GPIO模块的通用框图。
从这个通用框图,可以看出来,GPIO输入和输出值的改变,是通过D触发器完成的。用硬件原理上来说,如果GPIO接口的模块没有时钟信号,那么这个模块就不能够工作。我们可以直接通过控制时钟模块就可以控制响应接口是不是处于工作,这样就可以达到控制整个芯片功耗的目的。但是我们在一般的ARM单片机提供的参考手册中看不到这样的框图,这个框图中对D触发器的解释相应地来说,会更加清晰。
-
RISC-V鸿蒙开发板单片机简介2(项目连载9)
这篇文章我们来简要的介绍一下Pygmy-E单片机使用的I2C通信接口,主要内容是通信协议的描述,框图的讲解,以及相关寄存器的介绍等等。本文介绍的时候,尽量把它和主流的STM32单片机对比,这样大家可以对两个I2C的外设都有更清晰的认识。其中红色框框选出来的关键字属于Pygmy-E单片机支持的I2C,绿色框框选出来的属于STM32F030C8T6这款单片机。
1. 传输速度
和主流的STM32单片机一样,Pygmy-E系列的单片机的I2C接口,一样支持不同的传输速度,从而适配不同的外设和场景诉求。
2. 组成框图
从框图上来看,两种类型的I2C接口组成单元是类似的。要实现I2C协议相关的信息收发功能,首先必须要有这些相应的功能;再一个,按照这样的模块进行对整个功能模块进行切分,可能在电路设计上相对最为简单。但是整体上,还是STM32这款单片机对I2C各个组成模块及其相互之间的关系描述得更加清晰。
3. 开始信号和信号传输信号
I2C通信协议,开始信号和结束信号,是在SCLK为低的时候,通过SDA这个引脚的变化来产生的。 SDA由高到低的时候,是start信号;SDA由低到高的时候,是STOP信号。那么既然这两个信号是开始和终止信号,那么在信号传输的过程中一定要避免这样的信号出现。那么从协议上看,在数据传输的过充中,数据信号的采样(保持不变)只能是在SCL为高电平的时候,数据信号的改变只能是在SCL电平为低的时候。另外为了避免,在SCL为高的时候,万一SDA的信号发生了变化,可以适当配置SDA和相对于SCLK信号的先发和延后时间。这个误识别的诱发因素会很多,比如布线的时候,如果不是等长线,就有可能导致默认配置的先发和延后时间失去作用。Pygmy-E单片机和STM32单片机都提供了相应的寄存器来配置这个时间。
从这个方面上来看,两款I2C电路基本是一致的,只是配置延时时间的寄存器可能是不一样的。
4. 寄存器
下面是配置两款I2C接口时所使用的寄存器,大家从图上可以看出,Pygmy-E系列的单片机I2C接口所需要配置的寄存器非常复杂,总计有68个寄存器需要配置。相比之下,STM32单片机所需要配置的寄存器就要少很多,明显感觉易用性更好。Pygmy-E单片机的I2C接口使用这么多的寄存器好处也当然是有的,它可以帮助我们对I2C接口模块有相对更为深入的理解。但是需要我们对I2C通信协议以及相关的应用非常熟悉才行,否则这么多的寄存器相当容易把开发者搞晕。
5. 从地址
两个I2C接口模块,都支持7位和10位地址协议。接口的通用性上都差不多。具体的通信内容的格式,这里就不在进行比较了。
对上述几个关键的方面进行比较后,我们基本上可以感觉到,相比STM32f030C8T6这款单片机,Pygmy-E单片机的I2C接口使用起来难度相对较高。但是它数量庞大的寄存器,同时增强了使用它时的灵活度。两款接口应该是各有所长,但是从通用性上而言,STM32的I2C接口可能会更加友好。
-
RISC-V鸿蒙开发板单片机简介3(项目连载10)
我们今天继续RISC-V I2C接口的介绍。上篇文章,我们大体比较了新思科技的I2C接口与STM32F030XX的I2C接口之间宏观上的区别。整体上,我们可以感觉新思科技的I2C接口使用起来相对来说比较麻烦。好在新思科技给出了最初版本的驱动程序设计,我们可以在他们的基础之上进行适当的改动,就可以把I2C通信的功能建立起来,同时在使用中可以加深对这些接口模块的理解。
上图的代码是程序中对I2C接口的初始化部分,和STM32的寄存器配置基本是一样的。主要是对寄存器的配置,通过在寄存器那里设定给定的值以后,I2C模块就可以按照指定的方式进行工作。
我们先从架构上来分析一下,首先代码中的reg_i2c这个变量,是I2C相关寄存器的基地址。红色字体寄存器的名字其实表示的是相关寄存器的偏移地址,这里也是采用“基地址+偏移地址”的方式进行寻址的。
上图中红框部分的描述,应该比较容易理解,相应的位配置好以后,写到DW_IC_CON(控制寄存器)里面去,就可以实现相应的功能了,比如选择FAST_SPEED模式、MASTER模式以及进行RESTART等等。
大家可以看一下,对于不同的速率,I2C接口模块会配置不同的时钟信号值。从数据上就能够看出来Slow模式和Fast模式,单周期内占用的时钟个数是不一致的,SLOW模式使用的时钟个数更多,不管是HCNT(high counter)还是LCNT(low counter)。这样的配置也是相对来说容易理解的,信号传递的速度越高,时钟频率肯定就会越大,那么时钟的HIGH和LOW部分肯定占据了更少的时间。从寄存器上来看,对于低速(SS)、快速(FS)和高速(HS)配置的时候,使用的是不同的寄存器,而不是在同样的寄存器中使用不同的位来配置。
上面两个函数是计算时钟的公式,具体这些计算方法和参数的设置应该是新思科技结合自己的硬件特性给出的。我们不要过于深究。
相比之下,STM32F030xx这款芯片中,对于时钟的配置就要简单的多。Reference Mannual中,对于不同的速率,只需要在SCL和SDA中配置相应的值就可以了,而且系统给出了推荐值,是不是简化了不少?不过它这样的表述,从另一方面来看,也掩盖了许多硬件实现的细节。这些细节,对于我们从硬件层面上更加深刻的去理解I2C协议,肯定比较有帮助。只不过我们实际使用的过程中,一般不需要了解得这么细致。当然,从本质上讲,两者实现的思路肯定是一样的。
时钟信号配置完以后,下一步就是要对SDA和中断系统进行配置了。
大家可以看一下,IC_SDA_HOLD这个位的解释,就是SCL由高到低变化后,SDA这个位还需要保持多少时间。我们说I2C的协议里面是不是定义了,在SCL为高的时候,SDA的数据信号是不是不能变化的呀。怎么能够实现不能变化呢?是不是就是要把SDA的信号延时一部分时间再变化,是不是就可以了呀。这个寄存器就是实现这个功能的。
最后一个是关于SPKLEN的,这个是一个保护设计,为了防止电压或者电流冲击对信号造成影响的。我们按照默认的配置写就可以了。
今天关于I2C初始化部分介绍,我们就先到这里。后续的内容,我们会在文章中持续更新。
-
RISC-V鸿蒙开发板单片机简介4(项目连载11)
讲完了I2C接口,我们下面来看一下UART通信相关的配置。UART接口是GPIO复用功能中最常用的一种硬件通信方式。如果只是单线通信的话,只需要一根数据线就够了,所以我们经常使用uart接口输出程序中的变量,进行调试。
在这块开发板中,对uart接口进行初始化的函数如下图所示。对UART接口配置,一个非常关键的参数就是波特率的配置。下图中,绿色部分一大块内容都是对波特率配置的程序段,可见其相对来说比较复杂,我们不需要去关注这些实现的细节,实际使用的时候拿过来用就好了。
下面,我们对相关的寄存器进行简要的介绍。
LCR:Line Control Register是线控制寄存器,所谓线控制器,主要是用于配置和传输相关的一些参数。比如,第4位是用来做奇偶校验配置的,第0-1位是用来控制一次传送多少个bits的。
IER: Interrupt Enable Register,这个是中断控制器主要控制和UART相关的中断操作的,是不是允许相应的中断。
FCR:是用来控制FIFO 相关的参数的。我们这里不使用FIFO,所以要把它禁止掉。和FIFO相关的参数,这里就不给大家展示了。
MCR,全称是 Modem Control Register,也就是和调制解调相关的寄存器。我们这里配置的两个位一个是RTS(Request to Send),一个是DTR(Data Terminal Ready)。这两个位,我们在实际使用串口的时候也会经常用到,但是实际一般不配置相应的握手协议。
配置好寄存器之后,就要开始发送数据了。数据的发送,主要是THR这个寄存器。THR的全称是Transmit Holding Register。实际发送数据的时候,只需要往这个寄存器里面写入数据就可以了。写入数据之后,UART相关的电路,自动发送数据给接收端。
上面就是这块开发板中关于UART配置的简要介绍。整体的思路和STM32的配置是相同的。新思科技的uart外设提供了非常多的寄存器进行的配置,更加贴近底层,但是这需要我们对uart的电路实现以及相关协议进行更加深入的理解才能驾驭得了。
-
RISC-V鸿蒙开发板单片机简介5-中断系统(项目连载12)
大家好,我是张飞实战电子张角老师
讲完了基本的外设接口,我们今天看一下这个开发板的中断系统。中断系统可以说是单片机能够响应任务需求的核心机制,对于一个固定的单片机而言,计算资源只有那么多,如何高效地利用这些计算资源,是单片机设计者必须回答的问题。目前主流的方案,就是我们今天要讲的中断系统。我们可以人为给不同的中断请求设定不同的级别,这样单片机系统就知道执行任务的轻重缓急了,系统的实时性提高了,计算资源就相当于被高效地利用了。
那么,在嵌入式实时操作系统中,任务又是一个什么概念呢?我们知道,任务也是有优先级的,不同的任务优先级是不同的,那任务的优先级和中断的优先级是什么关系?我觉得,可以把任务理解成一种不靠硬件抢占执行的中断,应该归入大中断的概念中去,本质上是进一步提升了单片机响应任务的实时性。我们知道,一般中断程序的执行时间都不宜太长,只是负责变量打标,然后快速退出就可以了。一个高中断优先级的中断程序,执行得太长的话,那么所有相应的低优先级的中断程序是不是就完全无法执行了。所以程序开发的时候,才尽可能地让中断程序只是负责对变量打标而已。中断程序对相应的变量打标以后,实际工作的执行,还主要是任务的调度来实现的。这样的设计,相比而言能够让计算资源的实时性得到最大程序的发挥。这个也是嵌入式实时操作系统被设计出来的一个关键的原因。
鸿蒙系统外部中断,到底是怎么样被初始化的呢?
上图中,第一个红框里面代码的意思就是开中断,这样单片机的各种硬件中断就可以进行响应了。这段代码执行以后,使用鸿蒙系统的中断创建函数创建外部中断。
我们先不看外部中断内部是怎么实现的,先看一下这个中断创建函数是怎么工作的。在STM32中,中断函数能够被执行,是因为中断函数的地址或者叫做向量被放在中断向量表这个地方。当有外部中断请求发生的时候,单片机的硬件电路就会自动寻址到相应的中断函数,进而进入到函数里面去执行相应的代码。当然这中间肯定伴随着各种环境参数的保存,环境参数的保存,主要是为了解决从中断中恢复的时候,程序找到回去的路。
鸿蒙系统的中断创建函数,所做的主要事情,如下图所示,就是把一个数组g_hwiForm里面的数值初始化了。
这个数组里面的每一个数值存放了相应中断函数的地址以及相关的参数值。我们可以暂且认为这个数组存放的就是中断向量表,那这些个函数是怎么执行起来的,或者说是如何被调用的呢?
我们看一下,我们这款单片机的中断执行机制。RISC-V单片机中断向量表的起始地址,是由CSR寄存器mtvt来指定的,具体说明,如下图。那也就是说当有外部中断触发的时候,单片机硬件查询的首先是这个寄存器的地址,然后执行这个寄存器中的地址所指向的代码。那么我们的鸿蒙系统中,有没有对这个寄存器进行初始化呢?
大家可以看一下,实际上在操作系统启动的汇编代码部分,这个向量是被初始化了的。如下图,操作系统把TrapVector函数地址放入了t0,然后把t0放入了mtvec这个寄存器中。那TrapVector到底是什么呢?
大家看一看,这个TrapVector函数里面,在经过一系列的寄存器操作以后,调用了OsHwiInterruptDone和TaskSwitch这两个函数。这也就是说每次中断请求发生以后,都会执行这两个函数。
这个OsHwiInterruptDone函数地址里面,是不是就调用了我们初始化的中断函数呀。它根据不同的中断号码执行不同的中断函数,如下图所示。
在本篇文章刚开始的时候,我们是不是提到的外部中断响应函数OsMachineExternalInterrupt,如果有外部中断发出请求的话,就会进入到这个函数中去。