【沁恒RISC-V内核 CH582】开发板基本情况,细嚼慢咽官方库
<h2 cid="n11" mdtype="heading">开发板基本情况——细嚼慢咽</h2><p cid="n12" mdtype="paragraph">CH582为RISCV架构的32位微处理器,集成开发环境使用基于Eclipse的MounRiver集成开发环境,官方为CH582提供了标准的库函数,位于“./EVT/EXAM/SRC”。</p>
<p cid="n13" mdtype="paragraph">接下来聚焦提供的标准库和TRM。</p>
<h3 cid="n14" mdtype="heading">连接命令文件和系统存储资源</h3>
<p cid="n15" mdtype="paragraph">按照官方给的器件手册,该开发板应当包含4段Flash区域,但是连接命令文件中仅仅启用了两个,即RAM(32k),和FLASH(448k),这些资源足够应付绝大多数的应用场景。另外两个没有写明在连接命令文件中的项目分为为:</p>
<p cid="n16" mdtype="paragraph">24KB 系统引导程序存储区BootLoader</p>
<p cid="n17" mdtype="paragraph">8KB系统非易失配置信息存储区InfoFlash</p>
<p cid="n18" mdtype="paragraph">如果需要使用则需要在连接命令文件中添加如下代码:</p>
<pre cid="n19" lang="LD" mdtype="fences" spellcheck="false">
MEMORY
{
BOOT (rx) : ORIGIN = 0x00078000, LENGTH = 24K
INFO (rw) : ORIGIN = 0x0007E000, LENGTH = 8K
}
SECTIONS
{
.bootloader :
{
KEEP(*(SORT_NONE(.bootloader)))
. = ALIGN(4);
} >BOOT AT>BOOT
.infoflash :
{
. = ALIGN(4);
} >INFO AT>INFO
}</pre>
<p cid="n20" mdtype="paragraph">在C语言代码中如果需要使用则需要告诉链接器,需要保存的段,需要使用下面的声明方法:</p>
<pre cid="n21" lang="C" mdtype="fences" spellcheck="false">
__attribute__((section(".bootloader"))) // 用来修饰需要保存在BootLoader段的代码和数据
__attribute__((section(".infoflash"))) // 用来修饰需要保存在InfoFlash段的数据</pre>
<p cid="n22" mdtype="paragraph">这些代码保留待用。后面的具体实现过程中可能需要。</p>
<p cid="n23" mdtype="paragraph">另外在连接命令文件中提供了一些常量,将来可能供汇编代码使用。(可以看完下一节之后看这里,_highcode_vma_start常量等都是通过直接在这里通过PROVIDE关键字导出的)。</p>
<p cid="n24" mdtype="paragraph">另外我们需要关注到,通过“用户级非易失配置信息”配置可以使能bootloader。在使能之后才会默认从bootloader段代码开始执行。</p>
<blockquote cid="n25" mdtype="blockquote">
<p cid="n26" mdtype="paragraph">CFG_BOOT_EN = 1; // 使能BootLoader</p>
</blockquote>
<p cid="n27" mdtype="paragraph">片上外设也分配了相应的地址,但是在连接命令文件中并没有分配相应的存储空间,这与我平时使用ti公司的相关DSP产品实现方式不同。可以从源代码中找到实现,以TMR0为例,我们可以找到下面的代码:</p>
<pre cid="n28" lang="C" mdtype="fences" spellcheck="false">
#define R32_TMR0_CONTROL (*((volatile unsigned long)0x40002000)) // RW, TMR0 control</pre>
<p cid="n29" mdtype="paragraph">可以看到源代码中也是通过直接使用volatile关键字实现对于特定外设进行配置的,但是并没有在连接命令文件中写明,也就是直接使用了宏模式,而不是使用连接命令文件绑定的变量模式。</p>
<blockquote cid="n30" mdtype="blockquote">
<p cid="n31" mdtype="paragraph">评注:我在最开始学习TI-DSP的时候,也是通过volatile关键字直接绑定地址空间的方式来直接对外设的配置寄存器直接实现读写的。这种方法上手快,容易理解。但是TI选择使用绑定某一个外设变量和一段地址空间这种实现方式的优势在于DSP将特定的外设的一组寄存器编址在一起,可以容易地通过结构体实现访问。</p>
</blockquote>
<h3 cid="n32" mdtype="heading">初始化启动代码</h3>
<p cid="n33" mdtype="paragraph">接下来可以找到初始化启动代码“startup_CH583.S”,可以简单粗略地观察一下其中的布置。都是熟悉的配方,下面通过伪代码简单概括一下框架,然后逐个详细分析。</p>
<pre cid="n34" lang="ASM" mdtype="fences" spellcheck="false">
_start: // .init
jmp _handle_reset
_vector_base: // .vector
// 这里写明了所有的中断类型
// 为所有的中断函数声明了weak标识符修饰的函数
handle_reset: // .handle_reset
// 构建堆栈框架
// 将必要的代码、数据地址加载到寄存器中,
// 初始化全局变量段
// 启用芯片功能:流水线控制位 & 动态预测控制位;打开嵌套中断、硬件压栈功能; 配置向量表模式为绝对地址模式
// 将程序的控制权交给main函数
la t0, main
csrw mepc, t0
mret</pre>
<p cid="n35" mdtype="paragraph">与其他类型的芯片启动过程相似。如果有其他朋友需要研究其中的内容,可以仔细分析一下连接命令文件,其中提供了各类代码的加载地址。</p>
<blockquote cid="n36" mdtype="blockquote">
<p cid="n37" mdtype="paragraph">评注:简单,明快,清晰,值得学习。</p>
</blockquote>
<h3 cid="n38" mdtype="heading">汇编-C接口</h3>
<p cid="n39" mdtype="paragraph">如果考虑仔细研究芯片的具体性能和芯片的一些具体实现,就需要关注RVMSIS文件夹中的两个文件。两个文件都提供了一些封装好的汇编语句,方便用户直接调用从而实现对于特定功能的使能、关闭、检测。</p>
<p cid="n40" mdtype="paragraph">在core_riscv.h中提供了一系列的内联函数,主要是针对PFIC(Programmable Fast Interrupt Controller)的相关设置,东西比较细碎,不做更深入的阅读,用到再说,直接继续。</p>
<h3 cid="n41" mdtype="heading">标准外设库</h3>
<p cid="n42" mdtype="paragraph">接下来到最关键也是作为用户最应关注的部分——外设库。接下来将逐步分析芯片具有的各类外设。</p>
<p cid="n43" mdtype="paragraph">首先是片上总硬件资源汇总:2Mbps 低功耗蓝牙BLE 通讯模块、2 个全速USB主机和设备控制器及收发器、2 个SPI、4 个串口(UART)、1个I2C接口、1个12位14通道(实际写在代码中可以外界相连的数目,不包括2个内部通道)ADC、14通道的触摸按键检测模块、RTC、电源控制器。</p>
<p cid="n44" mdtype="paragraph">接下来从外设库和提供手册的角度分析各个外设的使用方法和特性。</p>
<h4 cid="n45" mdtype="heading">GPIO</h4>
<p cid="n46" mdtype="paragraph">芯片一共提供了40个IO口,其中GPIOA16个,GPIOB24个,其中GPIOB中16个通道具有输入中断功能,其他8个不具备输入中断功能。</p>
<p cid="n47" mdtype="paragraph">要访问GPIO的输入输出数据需要借助引脚输入寄存器:<code>R32_Px_PIN</code>和引脚输出寄存器<code>R32_Px_OUT</code>,作为GPIO使用时,需要首先借助<code>R32_Px_DIR</code>寄存器写入GPIO的方向。以上的寄存器是通过位来表征每一个IO口的输入情况的。</p>
<ul cid="n48" data-mark="+" mdtype="list">
<li cid="n49" mdtype="list_item">
<p cid="n50" mdtype="paragraph">PA 端口中PA~PA位有效,对应芯片上16个GPIO 引脚。</p>
</li>
<li cid="n51" mdtype="list_item">
<p cid="n52" mdtype="paragraph">PB 端口中PB~PB位有效,对应芯片上24个GPIO 引脚。</p>
</li>
</ul>
<p cid="n53" mdtype="paragraph">至于GPIO的驱动能力可以通过<code>GPIOx_ModeCfg()</code>函数来设置。对于中断模式可以使用<code>GPIOA_ITModeCfg()</code>函数来设置。</p>
<p cid="n54" mdtype="paragraph">其中提供了各个GPIO的初始化和功能复用方法。部分I/O 引脚具有复用功能,上电后默认所有I/O 引脚均为通用I/O 功能,<strong>启用各功能模块后,相应的原GPIO引脚被配置成各自功能模块对应的功能引脚</strong>。其中GPIOA、GPIOB可以进行功能映射,然后可以通过函数<code>GPIOPinRemap</code>将部分GPIOA的复用外设重映射到GPIOB。</p>
<h4 cid="n55" mdtype="heading">系统时钟</h4>
<p cid="n56" mdtype="paragraph">全速模式下时钟,应当输入一个32\,MHz的晶振,从PCB中可以安康电脑使用了一颗3225的有源晶振器。经过PLL倍频可以得到32\,MHz\times 15=480\,MHz,然后经过默认5分频可以得到系统频率6.4\,MHz。</p>
<p cid="n57" mdtype="paragraph">对于RTC时钟,可以使用内置的32\,kHz时钟或者使用外部晶振,默认使用的内部晶振可以使用寄存器<code>R8_CK32K_CONFIG</code>进行配置。使用外部32.768\,kHz晶振将会自动复用PA10和PA11端口。在本开发板中没有使用外置的晶振。内部的LSI时钟也可以被HSE校准。</p>
<p cid="n58" mdtype="paragraph">RTC时钟与系统的低功耗模式唤醒有关联,可以用于定时触发唤醒。</p>
<p cid="n59" mdtype="paragraph"><code>clk.c</code>文件中给出了芯片时钟配置相关的内容,包括晶体的偏置电流和负载电容,提供了用于校准内部32k时钟的方法<code>Calibration_LSI</code>,以及RTC相关的应用函数。</p>
<h4 cid="n60" mdtype="heading">通用定时器</h4>
<p cid="n61" mdtype="paragraph">系统提供了4个26位定时器,可以用来测量输入信号的脉冲长度(捕获模式),或者产生PWM波形,或者起到基本的定时计数功能。</p>
<p cid="n62" mdtype="paragraph">在库文件中对定时器的库函数支持非常简单易用,TIM1和TIM2支持DMA为PWM送入脉宽,从而实现SPWM之类的调制效果。TIM0和TIM4可以用来做相对更简单的功能。</p>
<p cid="n63" mdtype="paragraph">按照GPIO章节所说,如果启用计时器的技术模式捕获模式或者PWM模式,将会自动进行复用:</p>
<figure cid="n64" mdtype="table">
<table>
<thead>
<tr cid="n65" mdtype="table_row">
<th>TIM</th>
<th>默认GPIO</th>
<th>重映射GPIO</th>
</tr>
</thead>
<tbody>
<tr cid="n69" mdtype="table_row">
<td>TIM0</td>
<td>PA9</td>
<td>PB23</td>
</tr>
<tr cid="n73" mdtype="table_row">
<td>TIM1</td>
<td>PA10</td>
<td>PB10</td>
</tr>
<tr cid="n77" mdtype="table_row">
<td>TIM2</td>
<td>PA11</td>
<td>PB11</td>
</tr>
<tr cid="n81" mdtype="table_row">
<td>TIM3</td>
<td>PB22</td>
<td>PA2</td>
</tr>
</tbody>
</table>
</figure>
<p cid="n85" mdtype="paragraph">除了4路TIM可以作为PWM使用,也可以使用8路专用PWM输出。在库文件中也提供了相应基准时钟,输出模式的设置,可以使用<code>R8_PWMx_DATA</code>寄存器对占空比直接设置。</p>
<blockquote cid="n88" mdtype="blockquote">
<p cid="n92" mdtype="paragraph">评注:</p>
<p cid="n95" mdtype="paragraph">其中提到了交错输出的模式,但是其实并不是特别理解,也许有其特殊的用法。</p>
<p cid="n93" mdtype="paragraph">对于定时器的同步触发问题,其实在TRM中提到了通用定时器可以同步触发,但是在文档中并没有很明显地体现出来,也许是指通过在两个时钟完成配置之后通过一条指令同时使能,从而使得输出同步。</p>
<p cid="n97" mdtype="paragraph">这样的PWM输出对于单极性的驱动已经足够了,但是对于小型直流电机驱动中需要使用的H桥可能略乏力,但是这个芯片并不是倾向于这个应用方向,所以并没有必要纠结这个方向。这个问题如果确实遇到可以通过外加驱动芯片以及硬件死区等方式实现双极性驱动。总体并不影响使用。</p>
</blockquote>
<h4 cid="n86" mdtype="heading">通用通信接口</h4>
<p cid="n99" mdtype="paragraph">芯片提供的SPI,UART,I2C接口都非常灵活(从时序和使用两个方面)其库函数对详细使用说明比较详细,非常中规中矩,SPI实现了一对线MISO/SIMO,也已经足以在绝大多数场景灵活应用。</p>
<p cid="n102" mdtype="paragraph">USB控制器和蓝牙控制器将会在后面的部分进行更加详细的讨论。这里暂时不做过多展开。</p>
<h3 cid="n112" mdtype="heading">开发板例程烧录测试</h3>
<p cid="n114" mdtype="paragraph">开发板大概明天到手,新买的WCH-link也还没有到,JLINK不能用,所以先缓缓,下周再见,烧一点好玩的程序进去,写一点简单程序框架试试水。</p>
<p>分析的很仔细,顶一下楼主。</p>
<p><img height="50" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/wanwan21.gif" width="63" />能正常收到,挺好的,没受疫情影响</p>
<p>拜读楼主的文章,写得通俗易懂,技术背景很深呀!看来在单片机的领域是一位资深的大神。期待继续拜读您的作品。</p>
<p>确实需要专业基础知识才能看明白</p>
<p>还有没有接下来的操作了</p>
sans555 发表于 2022-3-22 15:13
还有没有接下来的操作了
<p>有哦,刚刚收到编程器,我们几天之后再见哦~<img height="48" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/loveliness.gif" width="48" /></p>
nmg 发表于 2022-3-21 15:48
能正常收到,挺好的,没受疫情影响
<p>是的是的,太好啦!</p>
<p>挺详细的!谢谢分享!</p>
<p>占个坑,收藏一下</p>
<p>这些评测写得挺好的,把这些收集在一起,方便阅读</p>
<p> </p>
页:
[1]