普冉(PUYA)单片机开发笔记(1): UART通信

普冉(PUYA)单片机开发笔记(1): UART通信

PY32F003 系列 MCU

国产 32 位 MCU 日渐风行。在新做的项目中,为了 Cost-down,考虑要用国产 MCU 替代进口货,如果可行,在单片机这一块,BOM 可以降低一块钱。近日在考虑普冉(PUYA)的32位MCU。由于板子上 MCU 所需功能较为单一,因此考虑使用入门级的一款 MCU 进行替代。最终选择了 PY32F003F18P。

这个型号采用 TSSOP20 封装,PCB 占用面积比较小虽然配备的内核是 Cortex-M0+,但其工作频率高达 48MHz(下图简介中的 32MHz 工作频率好像是笔误),单周期乘除法,软浮点运算,估计运算速度是够用的存储器容量也较大,64KByte Flash 和 8KByte SRAM,代码足够装得下UART 外设有两个,满足使用需求1个 ADC,具有10个通道,满足使用需求3个 DMA 通道,满足使用需求至少5个可用的 16 位定时器,满足使用需求

开发环境:硬件连接

网购了一块 PY32F003F18P 芯片组的开发板(很便宜,良心价),买回来后自己焊接了管脚排针和 SWD 排针,然后用上了 J-Link 仿真器,为 J-Link 仿真器配备了一个 JTAG-SWD 转接板,用四芯杜邦线连接开发板。开发板配备了两个跳线器,一个跳线器连接了 3V3_IN 和 3V3,这样子接可以使用 SWD 的 3.3V 电源,省去了从 Type-C 供电。另一个跳线器把 BOOT0 对地短路,使 MCU 可以从内部 FLASH 启动。BOOT0 是 PA14 管脚,很好找。为了做实验,还另外连接了开发板的 UART2 (PA1:TX,PA0:RX)接到 USB-UART 转接模块上,再连接到上位机。

开发板上的 SWD 排针是5芯的,JTAG-SWD 接了 4 根线,多出来的是 RST 信号线。估摸着这个 RST 线应该是不用接的,于是就不接先。至此,硬件连接就 OK 了。

开发环境:软件配置

使用 USART_IT 例程

购买开发板时带有 PUYA MCU 的资料下载地址,于是从指定的地址下载了 PUYA 的开发包。开发包中有芯片手册和例程。这里选择了 USART_IT 例程,选择这个例程里的 Keil 工程,直接用 Keil uVision 打开,如下图所示。

查看一下这个工程的 Option,如下图所示。需要注意的是这个工程还包含了和其它例程共用的程序,例如 BSP/py32f003xx_Start_Kit中的 .c 文件,这些文件的存储位置不在例程文件夹里。

Project Items 的截图如下,默认使用 ARM Compiler:ARMGCC。先不改这些,继续往下走。

导入 PY32F003 的 DFP Pack

在开始编译工程以前,需要导入 PY MCU 的 DFP pack,这个 pack 包含在了软件包中的 pack 文件夹里,使用 Pack Installer --> Import 进来。Import 完成后,在产品系列中选择 PY32F003x8就好了。Packs Installer 界面右侧的部件显示 DFP 是 “Up to date” 状态。其它的都不修改先,继续!

使用魔术棒配置Target Options

确认 Device 正确

检查 Target 选项

设置 Xtal (外部晶振)的频率为 24MHz,和开发板保持一致ARM Compiler:使用默认的 5.x 版本的编译器Operation system:None选中 “Use MicroLIB”,这个选项会影响到 printf 的编码检查和确认 IROM1 和 IRAM1 的范围

检查和确认 C/C++ 选项

Define PY32F003x8 变量选中 C99选中 “One ELF Section per Function”Include Path 和 Compile control string 不要修改

配置仿真器(Debug)

PY 文档中举例使用的 PY-LINK 仿真器,本例中使用的是 J-LINK,所以仿真器应做相应的修改。其它选项不做修改。

点击仿真器的 Setting,第一次运行时会得到一个错误对话框,提示 J-Link 不认识这颗 MCU,点击继续,选择 Cortex-M0+,继续,得到 J-Link 对 MCU 的识别参数如下图所示。

把 SWD 的速度设置得低一点:1MHz,避免下载时的不可靠Reset 设置成 Normal为了确保下载正确,勾选 “Verify Code Downloaded” 和 “Download to Flash”

Flash Download 选项卡选择 “Erase Sectors”(默认是 “Erase Full Chip”)检查 “RAM for Algorithm” 的值如图所示确认 “Programming Algorithm” 的值,Start 和 Size 值如图所示

Utility 选项卡中 “Use Debug Driver” 和 “Update Target before Debugging”

编译工程

点击 “LOAD” 键一键下载。第一次编译是成功的,但是会提示烧写失败,不要紧,再下载一次就成了。例程的功能如下:

设置 UART2 的参数为 115200/N/8/1设置 UART2 为中断式无阻塞收发启动后,从 UART2 发送 0x01 到 0x0C回送每一次从 UART2 接收到的固定 12 个字节的字节流到对端

实验证明,运行是成功的。

重定向 printf

在 main.c 的 #include “main.h” 下一行添加 #include /* Private user code 一节中增加 fputc 函数(参见后面的 main.c 代码)编译时会出现错误,提示 fputc 函数在 py32f003xx_Start_Kit.c 中和 main.c 中重复定义了。打开 py32f003xx_Start_Kit.c 文件,把 fputc 函数注释掉。重新编译下载,成功了。在上位机的 XCOM 中实现了预期的功能。

main.c 的完整代码

#include "main.h"

#include

/* Private define ------------------------------------------------------------*/

/* Private variables ---------------------------------------------------------*/

UART_HandleTypeDef UartHandle;

uint8_t aTxBuffer[12] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', '\r', '\n'};

uint8_t aRxBuffer[12] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};

/* Private function prototypes -----------------------------------------------*/

/* Private user code ---------------------------------------------------------*/

/*

* Redirect printf to Uart2 (UartHandle refers to USART2)

*/

int fputc(int ch, FILE *f)

{

HAL_UART_Transmit(&UartHandle, (uint8_t *)(&ch), 1, 0xffff);

return ch;

}

/* Private macro -------------------------------------------------------------*/

/* Private function prototypes -----------------------------------------------*/

void USART_Config(void);

void Error_Handler(void);

/********************************************************************************************************

**函数信息 :void main(void)

**功能描述 :main函数

**输入参数 :

**输出参数 :

** 备注 :

********************************************************************************************************/

int main(void)

{

HAL_Init(); // systick初始化

USART_Config(); // USART初始化

printf("PY32F003F18P is ready.\r\n");

HAL_Delay(500);

/*通过中断方式发送数据*/

if (HAL_UART_Transmit_IT(&UartHandle, (uint8_t *)aTxBuffer, 12) != HAL_OK)

{

Error_Handler();

}

while (1)

{

}

}

/********************************************************************************************************

**函数信息 :void USART_Config(void)

**功能描述 :USART初始化

**输入参数 :

**输出参数 :

** 备注 :

********************************************************************************************************/

void USART_Config(void)

{

GPIO_InitTypeDef GPIO_InitStruct;

//====================

// USART2初始化

//====================

__HAL_RCC_USART2_CLK_ENABLE();

__HAL_RCC_GPIOA_CLK_ENABLE();

UartHandle.Instance = USART2;

UartHandle.Init.BaudRate = 115200;

UartHandle.Init.WordLength = UART_WORDLENGTH_8B;

UartHandle.Init.StopBits = UART_STOPBITS_1;

UartHandle.Init.Parity = UART_PARITY_NONE;

UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE;

UartHandle.Init.Mode = UART_MODE_TX_RX;

if (HAL_UART_Init(&UartHandle) != HAL_OK)

{

Error_Handler();

}

/**USART2 GPIO Configuration

PA0 ------> USART2_TX

PA1 ------> USART2_RX

*/

GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1;

GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;

GPIO_InitStruct.Pull = GPIO_PULLUP;

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;

GPIO_InitStruct.Alternate = GPIO_AF9_USART2;

HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

/* 使能NVIC */

HAL_NVIC_SetPriority(USART2_IRQn, 0, 1);

HAL_NVIC_EnableIRQ(USART2_IRQn);

}

/********************************************************************************************************

**函数信息 :Error_Handler(void)

**功能描述 :错误执行函数

**输入参数 :

**输出参数 :

** 备注 :

********************************************************************************************************/

void Error_Handler(void)

{

while (1)

{

}

}

/********************************************************************************************************

**函数信息 :void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)

**功能描述 :USART错误回调执行函数,输出错误代码

**输入参数 :

**输出参数 :

** 备注 :

********************************************************************************************************/

void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)

{

printf("Uart Error, ErrorCode = %d\r\n", huart->ErrorCode);

}

/********************************************************************************************************

**函数信息 :void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)

**功能描述 :USART发送回调执行函数

**输入参数 :

**输出参数 :

** 备注 :

********************************************************************************************************/

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *UartHandle)

{

if (HAL_UART_Receive_IT(UartHandle, (uint8_t *)aRxBuffer, 12) != HAL_OK)

{

Error_Handler();

}

}

/********************************************************************************************************

**函数信息 :void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

**功能描述 :USART接收回调执行函数

**输入参数 :

**输出参数 :

** 备注 :

********************************************************************************************************/

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)

{

/*通过中断方式接收数据*/

if (HAL_UART_Transmit_IT(UartHandle, (uint8_t *)aRxBuffer, 12) != HAL_OK)

{

Error_Handler();

}

}

#ifdef USE_FULL_ASSERT

/**

* @brief Reports the name of the source file and the source line number

* where the assert_param error has occurred.

* @param file: pointer to the source file name

* @param line: assert_param error line source number

* @retval None

*/

void assert_failed(uint8_t *file, uint32_t line)

{

/* User can add his own implementation to report the file name and line number,

tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

}

#endif /* USE_FULL_ASSERT */

最终的代码中做了一些小修改,使程序可读性更强些:

初始化完成后,打印一条 Ready 消息把初次发送的二进制数修改为 1,2,3 ... 9,0,回车,换行,共 12 个可打印字符上位机的 XCOM 发送 10 个数字,选中“发送新行”,也就是加入回车和换行符,凑够 12 个字符总结

导入 DFP 的 pack 以后,使用 Keil MDK 可以正确配置 PY32F003x8空芯片的初次烧写会失败,但是后续烧写会成功。怀疑是不是初次烧写执行了类似 “Unlock Flash” 的操作,就像 J-Link 的 ARM-Flash 程序一样的功能?使用 1MHz 的 SWD 烧写速度,会出现小概率的烧写失败的情况,这时重新烧写一般都会成功。实验中没有出现反复多次烧写不成功的情况。后续再尝试降低一些 SWD 的速率看看J-Link V8 不识别 PY 单片机,只能用 Cortex-M0+ 系列产品代替,代替后可以正常烧写本例中用到的 HAL 库函数和 STM32 系列的相同,GPIO_InitStruct 的定义和操作方法和 STM32 系列也完全相同PY32F003x8 的 HAL_Receive_IT 函数和 HAL_Transmit_IT 函数的语法和作用,至少从功能上和 STM32 的相同。HAL_NVIC_xxx 函数接口和 STM32 也相同。也难怪,都是 Cortex-M0+ 的构架,代码通用也挺好的PY32F003x8 的 UART 运行在 115200 波特率没问题。开发板上的 SWD RST 可以不接初次尝试,可能对打算应用 PUYA 单片机的朋友有所借鉴,谬误之处,不吝指出。

相关推荐

🔥2025必玩魔塔手游推荐✨
手机优酷缓存的视频文件怎么查找?3种方法轻松搞定!
如何删除电脑里的微信聊天记录图片(如何删除电脑里的微信聊天记录图片和文字)
win10系统字体粗细在哪设置

本文标签