您当前的位置: 首页 >> 独家 > >> 内容页

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

2023-05-02 06:29:50 来源:博客园
目录沁恒 CH32V208(一): CH32V208WBU6 评估板上手报告和Win10环境配置沁恒 CH32V208(二): CH32V208的储存结构, 启动模式和时钟CH32V 存储容量命名方式

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


(资料图片)

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

其中的Flash大小表示为

4 = 16K6 = 32K8 = 64KB = 128KC = 256K

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

D6 32KB or 64KB, Low-and-medium-density generalD8 128KB or 256KB, High-density generalD8C 128KB or 256KB, Connectivity or interconnectivityD8W 128KB or 256KB, Wireless

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

CH32V20x_D6CH32V203F6, CH32V203G6, CH32V203K6, CH32V203F8, CH32V203G8, CH32V203K8, CH32V203C6, CH32V203C8CH32V20x_D8CH32V203RBCH32V20x_D8WCH32V208GB, CH32V208CB, CH32V208RB, CH32V208WBCH32V30x_D8CH32V303CB, CH32V303RB, CH32V303RC, CH32V303VCCH32V30x_D8CCH32V305FB, 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 为独立的pinBOOT1 为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.

关键词:
分享到:
x 广告
x 广告

Copyright ©  2015-2022 北冰洋时尚网版权所有  备案号:沪ICP备2020036824号-3   联系邮箱:562 66 29@qq.com