PY32F0系列的封装
在PY32F0系列的封装可以分为两大类, 20PIN及以上的和小于20PIN的.
- 20PIN, 24PIN 和 32PIN, 带有独立的 NRST 和 BOOT0, PIN脚互相独立不复用;
- 8PIN, 10PIN 和 16PIN, 没有 BOOT0, 存在多个PIN脚共用同一个物理管脚的情况
这篇主要介绍没有BOOT0的情况如何修改Option Bytes, 以及如何在物理管脚上使用不同的PIN
PY32F002A 的封装
PY32F002AL15S, PY32F002AA15M, PY32F002AW15S
可以看到 SOP8 和 SOP10 存在复用情况
PY32F002AW15U, PY32F002AF15P
PY32F003 的封装
因为PY32F003型号较多, 这里只列出小于20PIN的封装
PY32F003L1xS, PY32F003L2XD, PY32F003L2xS
PY32F003A18N, PY32F003W1XS
PY32F002A/PY32F003 管脚复用
从上面的管脚配置可以看到, 大部分型号都存在同一物理管脚的复用情况, 有一些是功能脚(PF2/NRST)与普通IO脚的复用.
在 OB(Option Bytes)中禁用和启用 PF2/RESET
PF2/NRST这个PIN是比较麻烦的一个功能脚, 因为默认启用了RESET功能, 不受PIN模式的影响, 所以无论你把它设置成INPUT, OUTPUT 还是 ANALOG, RESET永远生效, 和这个PIN同处于同一个物理管脚的PIN就没法正常使用.
要禁用它的RESET功能, 要在芯片的 OB(Option Bytes)里修改. OB 位于地址 0x1FFF 0E80, 占用4个字节, 其中2字节是配置, 另外2字节是这两个字节的反码. 对应 RESET 功能的设置 NRST_MODE 存储于第14位, 0表示仅复位输入, 1表示禁用复位输入,启用 GPIO 功能.
对于正常带 PF4/BOOT0 的型号, 在上电时拉高 BOOT0, 就可以从 system memory 启动 boot loader, 通过 ISP 工具连接后在工具里修改 OB, 但是 SOP8 和 SOP16 这些封装没有 BOOT0, 所以没法使用 ISP 工具修改. 只能通过代码或第三方工具(例如JLink)修改. 以下以LL库为例, 说明在代码中修改OB的方法
在OB中关闭PF2复位输入的方法
static void APP_FlashSetOptionBytes(void) | |
{ | |
FLASH_OBProgramInitTypeDef OBInitCfg; | |
LL_FLASH_Unlock(); | |
LL_FLASH_OB_Unlock(); | |
OBInitCfg.OptionType = OPTIONBYTE_USER; | |
OBInitCfg.USERType = OB_USER_BOR_EN | OB_USER_BOR_LEV | OB_USER_IWDG_SW | OB_USER_WWDG_SW | OB_USER_NRST_MODE | OB_USER_nBOOT1; | |
/* | |
* 默认的值: OB_BOR_DISABLE | OB_BOR_LEVEL_3p1_3p2 | OB_IWDG_SW | OB_WWDG_SW | OB_RESET_MODE_RESET | OB_BOOT1_SYSTEM; | |
*/ | |
OBInitCfg.USERConfig = OB_BOR_DISABLE | OB_BOR_LEVEL_3p1_3p2 | OB_IWDG_SW | OB_WWDG_SW | OB_RESET_MODE_GPIO | OB_BOOT1_SYSTEM; | |
LL_FLASH_OBProgram(&OBInitCfg); | |
LL_FLASH_Lock(); | |
LL_FLASH_OB_Lock(); | |
/* 重新载入OB, 这会触发软复位, MCU重启 */ | |
LL_FLASH_OB_Launch(); | |
} |
注意, 上面这个方法执行后会重启MCU, 所以在调用前要做个判断, 否则它会一直循环重启下去
/* 检查 PF2 是否已经关闭了复位 */ | |
if(READ_BIT(FLASH->OPTR, FLASH_OPTR_NRST_MODE) == OB_RESET_MODE_RESET) | |
{ | |
/* 如果没关闭则调用 */ | |
APP_FlashSetOptionBytes(); | |
} | |
// 否则继续正常执行 |
这样执行完之后, RESET按钮就失效了, 如果要恢复, 要再将OB改回默认的值
OB_BOR_DISABLE | OB_BOR_LEVEL_3p1_3p2 | OB_IWDG_SW | OB_WWDG_SW | OB_RESET_MODE_RESET | OB_BOOT1_SYSTEM; |
同一物理管脚的其它PIN, 设为模拟(ANALOG)模式
以下以SOP16封装的为例, 启用 PF1, PF0, 禁用对应同一管脚的 PA14 和 PF2
static void APP_GPIO_Config(void) | |
{ | |
//... | |
// PF1 SCL | |
GPIO_InitStruct.Pin = LL_GPIO_PIN_1; | |
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; | |
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH; | |
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN; | |
GPIO_InitStruct.Pull = LL_GPIO_PULL_UP; | |
GPIO_InitStruct.Alternate = LL_GPIO_AF_12; | |
LL_GPIO_Init(GPIOF, &GPIO_InitStruct); | |
// PF0 SDA | |
GPIO_InitStruct.Pin = LL_GPIO_PIN_0; | |
GPIO_InitStruct.Alternate = LL_GPIO_AF_12; | |
LL_GPIO_Init(GPIOF, &GPIO_InitStruct); | |
/** | |
* 根据数据手册第20页, 同管脚的其它PIN应当设为 ANALOG. | |
*/ | |
// PA14 | |
LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_14, LL_GPIO_MODE_ANALOG); | |
// PF2 | |
LL_GPIO_SetPinMode(GPIOF, LL_GPIO_PIN_2, LL_GPIO_MODE_ANALOG); | |
//... | |
} |
电路连线避免干扰
管脚复用之后, 一些功能脚带的开关按钮和电阻电容就会对其它PIN造成影响.
例如对于复位键, 如果上面加了电容, 其容量一般是104(100nF), 用于避免按键抖动, 如果将这个脚禁用复位, 改为I2C的输出, 这个电容就会对输出信号造成干扰, 100nF的容量基本能消除掉1KHz以上的频率, 所以要将这样的电容去掉.
启动增加延时, 确保上电烧录
因为小封装没有 BOOT0, 所以在 SWD 口烧录失败的情况下, 没法用 ISP 工具救场, 如果你的程序加电后没有预留足够长时间的 delay, 又把 SWD 口的 PA13 PA14 给关掉了, 那下一次烧录就会干瞪眼.
一个好习惯是在设置完时钟之后, 保留一到两秒的延时, 可以在加电后从容不迫地按下烧录按钮.
int main(void) | |
{ | |
uint8_t i; | |
BSP_RCC_HSI_24MConfig(); | |
/** | |
* 在SWD口关闭前停留2秒, 保证上电后有足够长的烧录等待时间 | |
*/ | |
LL_mDelay(2000); | |
//... |
代码示例
以 SOP16 封装的 PY32F003W18S 为例, 依然使用 1602LCD 作为参考.
代码通过禁用 PA14 和 PF2, 将 PF1 和 PF0 设置为 I2C 外设接口, 驱动 1602LCD.
源代码已经提交到 GitHub 仓库, 地址: https://github.com/IOsetting/py32f0-template/tree/main/Examples/LL/I2C/PCF8574_1602LCD_PY32F003W_PF0_PF1
运行示例