沁恒 CH32V208(二): CH32V208的储存结构, 启动模式和时钟

news2025/1/11 5:57:58

目录

  • 沁恒 CH32V208(一): CH32V208WBU6 评估板上手报告和Win10环境配置
  • 沁恒 CH32V208(二): CH32V208的储存结构, 启动模式和时钟

CH32V 存储容量命名方式

在介绍下面的内容前, 先看一下CH32V系列和存储相关的命名格式, 以CH32V203为例, 前面的CH32V203代表一个系列, 后面的字符分别代表了Pin脚数量, Flash大小, 封装和工作温度范围

CH32V203G6U6
        ||||
        |||`-> Temperature range
        ||`--> Package: QFN
        |`---> Flash Size
        `----> Pin Count

其中的Flash大小表示为

4 = 16K
6 = 32K
8 = 64K
B = 128K
C = 256K

以及以D开头的容量表示形式(在用户手册中会出现)

D6 32KB or 64KB, Low-and-medium-density general
D8 128KB or 256KB, High-density general
D8C 128KB or 256KB, Connectivity or interconnectivity
D8W 128KB or 256KB, Wireless

这些容量类型与型号的对应关系为

  • CH32V20x_D6
    CH32V203F6, CH32V203G6, CH32V203K6, CH32V203F8, CH32V203G8, CH32V203K8, CH32V203C6, CH32V203C8
  • CH32V20x_D8
    CH32V203RB
  • CH32V20x_D8W
    CH32V208GB, CH32V208CB, CH32V208RB, CH32V208WB
  • CH32V30x_D8
    CH32V303CB, CH32V303RB, CH32V303RC, CH32V303VC
  • CH32V30x_D8C
    CH32V305FB, CH32V305RB, CH32V307RC, CH32V307WC, CH32V307VC

可以看到 CH32V208 全系列属于 CH32V20x_D8W 容量类型

CH32V208 的存储

数据手册中对存储部分的说明为

  • 内置最大 64K 字节 SRAM 区, 用于存放数据, 掉电后数据丢失. 具体容量要对应芯片型号.
  • 内置最大 480K 字节程序闪存存储区(Code FLASH), 用于用户的应用程序和常量数据存储. 其中包括零等待程序运行区域和非零等待区域.
  • 内置 28K 字节系统存储区(System FLASH)用于系统引导程序存储(厂家固化自举加载程序).
  • 128 字节用于系统非易失配置信息存储区, 128 字节用于用户选择字存储区

CH32V208 的存储器地址映射

下图是 CH32V208 的存储器地址映射

地址分配和 ARM Cortex M 几乎是一样的

  • Flash地址从 0x0800 0000 开始
  • RAM地址从 0x2000 0000 开始
  • 根据 BOOT pin 的设置, 启动时将对应的地址映射到 0x0000 0000

其中 Flash 大小是 480KB, 而 RAM 是可以配置的(应该是一块总计192KB的RAM), 根据零等待Flash的大小不同, 有三种划分选项 128KF + 64KR, 144KF + 48KR, 160KF + 32KR. 当启动时, 对应大小的code从 Flash 载入到 RAM 中执行, 实现零等待.

Flash RAM 映射关系

CH32V208 的 Flash 分为三块: 最开始的128KB固定映射到RAM, 在复位后复制到RAM; 之后的32KB是可配置区域; 除了前面的160KB, 后面的320KB是固定的非零等待代码区域.

        | Fixed    | Dynamic |
| ----- | -------- | ------- | -------------------- | ------ |
| Flash | 128KB    | 32KB    | 320KB                | 32KB   |
          ------------------------------------------
                                                 └───480K 用户可擦写可执行
| ----- | -------- | ------- | ------ | -------------------- |
| RAM   | 128KB    | 32KB    | 32KB   |
          --------   -------   ------
             |          |       └─── 32K固定RAM
             |          └───32K可配置为RAM或Flash映射
             └───128K固定Flash映射, 复位后硬件拷贝

在LD文件中设置可用 Flash 大小

编辑项目中的 link.ld, 在 MEMORY 部分修改, 下面的例子将 Flash 设置为 448KB

MEMORY
{  
  FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 448K
  RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K
}

注意 Flash 的 ORIGIN 从 0x0000 0000 开始, 不是 0x0800 0000, 因为执行时 Flash 会被映射到 0 地址, 连接时代码的地址都以0地址为偏移量.

启动模式

在启动时, 通过自举引脚(BOOT0 和 BOOT1), 可以选择三种自举模式中的一种

BOOT0BOOT1启动模式
0X从Code Flash 启动
10从System FLASH 启动
11从内部 SRAM 启动
  • BOOT0 为独立的pin
  • BOOT1 为PB2

QFN28封装的 CH32V208GBU6 比较特殊, 一是没有引出 BOOT1, 默认接地, 二是 BOOT0 与 PB8 共用同一个物理PIN脚, 在手册第19页有单独说明:

BOOT0引脚引出, 但BOOT1/PB2引脚未引出的芯片, 内部BOOT1/PB2引脚将下拉到GND. 此时如果进入低功耗模式配置IO口状态时, 建议BOOT1/PB2引脚使用输入下拉模式防止产生额外电流.

BOOT0和PB8引脚合封芯片, 建议外接500K下拉电阻, 保证芯片上电稳定进入程序闪存存储器自举模式. 另外, 此PB8引脚及其复用功能只保留了输出驱动功能, 所有输入功能已被禁止.

这个500K下拉可以保证BOOT0不浮空的同时, 对PB8作为输出不造成影响.

28引脚封装芯片有许多合封引脚(至少2个IO功能引脚物理合为一个引脚), 此时驱动不要同时配置输出功能, 否则可能损坏引脚. 有功耗要求的注意引脚状态.

简单说就是合封的pin脚, 不要同时设为输出模式

CH32V208 的时钟

根据数据手册, 时钟树结构如下

CH32V208 的时钟相对于 CH32V307 的不同点: 在CH307中没有 ETH-PHY

对于 CH32F20x_D8C 和 CH32V30x_D8C, 当使用 USB 功能时,CPU 的频率必须是48MHz、96MHz 或 144MH

而在 CH32V208中, ETH-PHY 的时钟通过 HCLK 提供

CH32F20x_D8W, CH32V20x_D8 和 CH32V20x_D8W 若同时使用 USB 和 ETH 功能, 需将 USBPRE[1:0]置为 11b

对于 USBPRE[1:0] 这个寄存器值为 0B11 时的说明

5 分频, 且 PLL 的源为 HSE 二分频(适用于PLLCLK=240MHz , 仅 适 用 于 CH32V20x_D8W/ CH32F20x_D8W) 注: CH32V20x_D8W、CH32F20x_D8W 具有 11b 选项, 其余型号该选项保留

可以看到, CH32V208 如果要同时使用 USB 和 ETH, 为了同时满足 USB 的48MHz, ETH-PHY 的60MHz, 需要将 PLLCLK 升至240MHz, 5分频后输出给 USB, 而 ETH-PHY 则从 120MHz 的 HCLK 通过2分频得到 60MHz

另一个需要注意的点是, BLE的 RFCLK 时钟是由 HSE 提供的, 如果时钟树没错的话, 可以理解为只有外接时钟源才能使用 BLE.

时钟设置代码

在沁恒提供的 SDK 和代码示例中, 与时钟相关的代码主要是这两个文件

ch32v20x.h

文件中定义了外置时钟源的频率 HSE_VALUE, CH32V208 默认使用的是 32MHz, 如果使用其他频率的晶振, 需要在这里修改

#if defined(CH32V20x_D8) || defined(CH32V20x_D8W)
  #define HSE_VALUE    ((uint32_t)32000000) /* Value of the External oscillator in Hz */
#else
  #define HSE_VALUE    ((uint32_t)8000000) /* Value of the External oscillator in Hz */
#endif

而内建时钟源是固定的 8MHz

#define HSI_VALUE              ((uint32_t)8000000) /* Value of the Internal oscillator in Hz */

system_ch32v20x.c

这个文件存在于每个示例项目的 User 目录下, 已经实现了常用的频率值函数, 通过修改宏配置可以切换不同的系统频率

//#define SYSCLK_FREQ_HSE    HSE_VALUE
//#define SYSCLK_FREQ_48MHz_HSE  48000000
//#define SYSCLK_FREQ_56MHz_HSE  56000000
//#define SYSCLK_FREQ_72MHz_HSE  72000000
//#define SYSCLK_FREQ_96MHz_HSE  96000000
//#define SYSCLK_FREQ_120MHz_HSE  120000000
#define SYSCLK_FREQ_144MHz_HSE  144000000
//#define SYSCLK_FREQ_HSI    HSI_VALUE
//#define SYSCLK_FREQ_48MHz_HSI  48000000
//#define SYSCLK_FREQ_56MHz_HSI  56000000
//#define SYSCLK_FREQ_72MHz_HSI  72000000
//#define SYSCLK_FREQ_96MHz_HSI  96000000
//#define SYSCLK_FREQ_120MHz_HSI  120000000
//#define SYSCLK_FREQ_144MHz_HSI  144000000

在里面搜索(3<<22), 对应 RCC->CFGR0, (3<<22)就是 USBPRE 寄存器, 可以看到在设置系统频率为 120MHz 时的特殊处理.

void SystemCoreClockUpdate (void)
{
  uint32_t tmp = 0, pllmull = 0, pllsource = 0, Pll_6_5 = 0;

  tmp = RCC->CFGR0 & RCC_SWS;

  switch (tmp)
  {
    case 0x00:
      SystemCoreClock = HSI_VALUE;
      break;
    case 0x04:
      SystemCoreClock = HSE_VALUE;
      break;
    case 0x08:
      pllmull = RCC->CFGR0 & RCC_PLLMULL;
      pllsource = RCC->CFGR0 & RCC_PLLSRC;
      pllmull = ( pllmull >> 18) + 2;

      if(pllmull == 17) pllmull = 18;

      if (pllsource == 0x00)
      {
          if(EXTEN->EXTEN_CTR & EXTEN_PLL_HSI_PRE){
              SystemCoreClock = HSI_VALUE * pllmull;
          }
          else{
              SystemCoreClock = (HSI_VALUE >> 1) * pllmull;
          }
      }
      else
      {
#if defined (CH32V20x_D8W)                                 // 对应 CH32V208 额外的处理逻辑
        if((RCC->CFGR0 & (3<<22)) == (3<<22))              // 如果 USBPRE 为 11, 仅出现在 120MHz的配置函数中
        {
          SystemCoreClock = ((HSE_VALUE>>1)) * pllmull;    // 系统时钟为 32 / 2 * 15 = 240MHz
        }
        else
#endif
        if ((RCC->CFGR0 & RCC_PLLXTPRE) != (uint32_t)RESET)
        {
#if defined (CH32V20x_D8) || defined (CH32V20x_D8W)
          SystemCoreClock = ((HSE_VALUE>>2) >> 1) * pllmull;
#else
          SystemCoreClock = (HSE_VALUE >> 1) * pllmull;
#endif
        }
        else
        {
#if defined (CH32V20x_D8) || defined (CH32V20x_D8W)
            SystemCoreClock = (HSE_VALUE>>2) * pllmull;
#else
          SystemCoreClock = HSE_VALUE * pllmull;
#endif
        }
      }

      if(Pll_6_5 == 1) SystemCoreClock = (SystemCoreClock / 2);

      break;
    default:
      SystemCoreClock = HSI_VALUE;
      break;
  }

  tmp = AHBPrescTable[((RCC->CFGR0 & RCC_HPRE) >> 4)];             // 通过 AHBPrescTable 对应的分频系数, 降回 120MHz
  SystemCoreClock >>= tmp;
}

AHBPrescTable 的分频系数数组为

__I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};

在 SetSysClockTo120_HSE(void) 中, 设置了 RCC_HPRE_DIV2

RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV2;

而 RCC_HPRE_DIV2 的值对应的是 0x00000080, RCC_HPRE 的值是 0x000000F0

#define RCC_HPRE                                ((uint32_t)0x000000F0) /* HPRE[3:0] bits (AHB prescaler) */
#define RCC_HPRE_0                              ((uint32_t)0x00000010) /* Bit 0 */
#define RCC_HPRE_1                              ((uint32_t)0x00000020) /* Bit 1 */
#define RCC_HPRE_2                              ((uint32_t)0x00000040) /* Bit 2 */
#define RCC_HPRE_3                              ((uint32_t)0x00000080) /* Bit 3 */

#define RCC_HPRE_DIV1                           ((uint32_t)0x00000000) /* SYSCLK not divided */
#define RCC_HPRE_DIV2                           ((uint32_t)0x00000080) /* SYSCLK divided by 2 */
#define RCC_HPRE_DIV4                           ((uint32_t)0x00000090) /* SYSCLK divided by 4 */

通过 RCC->CFGR0 & RCC_HPRE, 可以还原回 0x00000080, 再右移4位, 就变成 0x00000008, 对应 AHBPrescTable 中的第9个, 值为1, SystemCoreClock 右移1位, 相当于除以2, 值从240MHz变回120MHz.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/482615.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

剑指offer(C++)-JZ47:礼物的最大价值(算法-动态规划)

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 题目描述&#xff1a; 在一个m\times nmn的棋盘的每一格都放有一个礼物&#xff0c;每个礼物都有一定的价值&#xff08;价值大于…

使用CometD技术实现web系统中的主动推送

CometD技术通过Http长轮询(或websocket长链接)方式在服务器与客户端之间构建了一条交互的链路。它们都遵守Bayeux协议,交换的消息是Bayeux message,消息格式是JSON。 1、需求说明 当用户登录后,后台根据用户订阅把最新的信息反推给web客户端,展示在页面上。CometD服务器…

菜地管理系统【控制台+MySQL】(Java课设)

系统类型 控制台类型Mysql数据库存储数据 使用范围 适合作为Java课设&#xff01;&#xff01;&#xff01; 部署环境 jdk1.8Mysql8.0Idea或eclipsejdbc 运行效果 本系统源码地址&#xff1a;https://download.csdn.net/download/qq_50954361/87737285 更多系统资源库地…

计算机组成原理9控制单元的结构

9.1操作命令的分析 取值周期间址周期执行周期中断周期 取指周期数据流 PC存放下条指令的地址给MAR访问存储器相应单元&#xff0c;将数据取出来送给MDR寄存器&#xff0c;MDR取出来的内容送给IR指令寄存器&#xff0c;然后对指令进行译码&#xff0c;把指令的操作码部分取出…

医学图像的深度学习的完整代码示例:使用Pytorch对MRI脑扫描的图像进行分割

图像分割是医学图像分析中最重要的任务之一&#xff0c;在许多临床应用中往往是第一步也是最关键的一步。在脑MRI分析中&#xff0c;图像分割通常用于测量和可视化解剖结构&#xff0c;分析大脑变化&#xff0c;描绘病理区域以及手术计划和图像引导干预&#xff0c;分割是大多数…

MySQL解析器和优化器,你了解它们吗?

解析器都做哪些事情 其主要功能是将输入的SQL语句分解为语法单元&#xff0c;然后将这些语法单元转换为内部表示的数据结构&#xff0c;最终生成一个可执行的查询计划。解析器是MySQL中的一个重要组成部分&#xff0c;它直接影响查询的性能和正确性。 词法分析&#xff1a; …

【win11的CARSIM2020安装教程最全,包括下载地址,关闭防火墙】

carsim2020.0软件下载地址参考&#xff1a;https://www.cnblogs.com/bbman/p/15148890.html 百度网盘提取后&#xff0c;先关闭防护墙。 如何永久关闭windows defender杀毒软件。 第一种方式 安装某一杀毒软件&#xff0c;比如某管家、某60&#xff0c;杀毒软件会覆盖Defender…

PC或服务器装双系统

1. 准备工作 1.1U盘启动盘的制作 ①准备一个 4G 以上的 U 盘&#xff0c;备份好U盘资料&#xff0c;后面会对 U 盘进行格式化。 ②去CentOS官网下载你想要安装的 ISO 格式镜像文件&#xff0c;现在通常是CentOS6、7或者8。如果你英文不太好&#xff0c;可以选择使用edge浏览…

【Python入门】NumPy数组副本 vs 视图 / 数组形状 / 数组重塑

前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 副本和视图之间的区别 副本和数组视图之间的主要区别在于副本是一个新数组&#xff0c;而这个视图只是原始数组的视图。 副本拥有数据&#xff0c;对副本所做的任何更改都不会影响原始数组&#xff0c;对原始数组所做的任…

《花雕学AI》27:如何在ChatGPT时代提高数字媒体艺术的原创性和价值?

引言 数字媒体艺术是指使用各种数字、信息技术制作的各种形式的有独立审美价值的艺术作品&#xff0c;具有模拟现实的虚拟性、艺术创造的想象性、交互性和使用网络媒体的基本特征。数字媒体艺术是一个跨自然科学、社会科学和人文科学的综合性学科&#xff0c;集中体现了“科学…

vue3+element-plus角色权限管理分配

这里的图片是截图这个老师的项目 为了方便大家使用,我会在每个图片下面将代码原封不动打一遍 在src/uitls/permission.js加入以下内容 本段代码讲解: 参数一:后台传来的路由 参数二:前端所有的路由 先遍历前端所有路由,在里面继续遍历后台路由,通过二者某一个关键字的是否相同…

入门大纲 我为什么使用delta-io 数据湖 替代hive

1 大厂背书 databricks宣布把delta-io共享给apache基金会 并且delta-io从以前打杂的0.x版本升级为1.x 随后就是bug的各种修复和新功能的增加. release note可以看: Releases delta-io/delta GitHub 2 并发控制(解决了多任务并发读写表时的 读写冲突) hive/spark 如果多个任…

Android DownloadManager 下载安装App功能实现

@[DownlaodManager 实战] 升级功能是APP必备功能,本文以下载安装APP的完整流程来说明DownlaodManager的基本使用方法。 前提准备 下载需要互联网权限,需要申请<uses-permission android:name="android.permission.INTERNET" />权限; 安装APP,需要申请<…

【微机原理】8088/8086CPU引脚

8086是16位微处理器数据线有16根&#xff1b;8088是准16位微处理器&#xff0c;它对外的数据线是8位的。他们的地址线都是20位的&#xff0c;8088/8086均为40条引线、双列直插式封装 地址线决定了访问主存的容量&#xff0c;数据线决定了CPU的运输能力 为了能在有限的40条引线范…

【C语言】十大经典排序代码及GIF演示

&#x1f525;&#x1f525;&#x1f525;专栏推荐&#xff1a;C语言基础语法&#x1f525;&#x1f525;&#x1f525; 十大经典排序代码 1. 冒泡排序2. 选择排序3. 插入排序4. 快速排序5. 归并排序6. 堆排序7. 希尔排序8. 计数排序9. 桶排序10. 基数排序 1. 冒泡排序 通过依次…

MQ 服务占用 CPU 太高

文章目录 MQ 服务占用 CPU 太高1. 出现问题2. 分析过程1. 通过日志定位问题服务2. 查询异常服务进程、CPU、内存、IO、锁和网络3. CPU 占用过高分析 3. 解决方案 MQ 服务占用 CPU 太高 1. 出现问题 测试环境中&#xff0c;匹配业务运行时会出现响应缓慢或超时失败的情况 2. …

继承的相关介绍---C++

一、概念及定义 概念&#xff1a; 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段&#xff0c;它允许程序员在保持原有类特性的基础上进行扩展&#xff0c;增加功能&#xff0c;这样产生新的类&#xff0c;称派生类。继承呈现了面向对象程序设计的层次结…

复现CVE-2023-21839

攻击机安装jdk1.8 下载jdk1.8 https://www.azul.com/downloads/?versionjava-8-lts&osubuntu&architecturex86-64-bit&packagejdk#zulu 或 wget https://cdn.azul.com/zulu/bin/zulu8.60.0.21-ca-jdk8.0.372-linux_x64.tar.gz tar -zxvf zulu8.60.0.21-ca-jdk8.…

GB/T 28181-2011、2016、2022变更对比

一、GB/T 28181-2016与GB/T 28181-2011变更对比 GB/T 28181-2016与GB/T 28181-2011相比&#xff0c; 除编辑性修改外主要技术变化如下&#xff1a; ----(1) 修改了标准名称&#xff1b; ----(2) 增加了媒体流TCP传输要求(见4.3.1&#xff0c; 5.2&#xff0c;附录F&#xff…

Ubuntu磁盘和目录和文件的相关操作

目录 1、目录的切换 2、查看目录及文件 3、目录的常见操作 4、文件的常见操作 5、查看文件及目录大小 6、命令查看硬盘信息 1、目录的切换 打开终端窗口&#xff08;”ctrlaltt“&#xff09; 一般使用&#xff08;”pwd“&#xff09;显示当前所在的目录 比如&#x…