N32G45x系统上,提供了多组USART/UART,通过用户手册中的总线架构图,可以了解详细的情况:
从上图中可以看到,可以使用的USART/UART有USART1、USART2、USART3、UART4、UART5、UART6、UART7,多达7个。
其中USART1、UART6、UART7挂在APB2上,而USART、USART3、UART4、UART5挂在APB1上,最后APB1、APB2才连接到AHB。
从上面的命名可以看出,N32串口的命名顺序和友商的有一点点不同,部分友商的为0开头,而N32的为1开头。
在这里,有几点基础知识需要先了解一下:
- UART:Universal Asynchronous Receiver/Transmitter,通用非同步收發傳輸器
- USART:Universal Synchronous/Asynchronous Receiver/Transmitter,通用同步非同步收發傳輸器
- AHB:Advanced High-performance Bus, 高速总线,用来接高速外设的
- APB:APB (Advanced Peripheral Bus) 低速总线,用来接低速外设的
相对于SRAM、SDIO这些来说,即使是SPI也属于慢速,更别提USART/UART了,所以USART/UART挂在APB上,再由APB桥接到AHB上。
再上述总线架构图中,可以看到由APB1、APB2。N32G45x系统包含 2 个 AHB2APB 桥,即 AHB2APB1 和 AHB2APB2。其中 APB1 包含 26 个低速 APB 外设, PCLK1 的最高速度为 36MHz;APB2 包含 18 个高速 APB 外设,PCLK2 最高速度等于 72MHz。
正是因为不同的USART/UART,挂在不同的APB上,所以在进行对应功能的复位和时钟初始化的时候,需要使用对应的APB参数,否则功能不正常。后续代码部分,将会具体说明。
同一个USART/UART,往往不止对应到一组IO端口上,而是由多组可供复用使用。
从用户手册中,可以了解到具体情况:
从上面的图可以看出,每一个USART/UART,都有多组IO端口可供复用使用。
但同一时刻,一个USART/UART只能映射到1组IO端口,不能多组同时使用。
在部分友商的系统中,复用不同的IO端口,直接在初始化的时候指定即可。但N32中,不仅要指定,还要在代码中,配置相应的寄存器(AFIO_RMP_CFG, AFIO_RMP_CFGx),所复用功能就和其原来的IO端口断开,重新映射到新的IO端口上了。
从复用对照表中,可以查看具体的USART/UART可复用的IO端口组:
如上图,USART1默认组为PA9、PA10,复用组有一组为PB6、PB7
下面,以实力进行说明,分别使用默认的IO组,以及使用复用的IO组。
当使用默认的IO组时,使用如下的代码:
#define USARTx_NAME "USART1"
#define USARTx USART1
#define USARTx_CLK RCC_APB2_PERIPH_USART1
#define USARTx_GPIO_CLK RCC_APB2_PERIPH_GPIOA
#define USARTx_GPIO GPIOA
#define USARTx_RxPin GPIO_PIN_10
#define USARTx_TxPin GPIO_PIN_9
#define USART_APBxClkCmd RCC_EnableAPB2PeriphClk
#define GPIO_APBxClkCmd RCC_EnableAPB2PeriphClk
GPIO_APBxClkCmd(USARTx_GPIO_CLK | RCC_APB2_PERIPH_AFIO, ENABLE);
USART_APBxClkCmd(USARTx_CLK, ENABLE);
GPIO_InitType GPIO_InitStructure;
GPIO_InitStructure.Pin = USARTx_TxPin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitPeripheral(USARTx_GPIO, &GPIO_InitStructure);
GPIO_InitStructure.Pin = USARTx_RxPin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitPeripheral(USARTx_GPIO, &GPIO_InitStructure);
USART_InitStructure.BaudRate = 115200;
USART_InitStructure.WordLength = USART_WL_8B;
USART_InitStructure.StopBits = USART_STPB_1;
USART_InitStructure.Parity = USART_PE_NO;
USART_InitStructure.HardwareFlowControl = USART_HFCTRL_NONE;
USART_InitStructure.Mode = USART_MODE_RX | USART_MODE_TX;
USART_Init(USARTx, &USART_InitStructure);
USART_Enable(USARTx, ENABLE);
为了方便更换端口,使用龙宏定义来进行一些配置,方便修改更换。
因为GPIOx都挂在APB2上,所以GPIO的CLK定义 USARTx_GPIO_CLK对应APB2上的RCC_APB2_PERIPH_GPIOA。
而USART1也挂在APB2上,所以USART2的CLK定义USARTx_CLK为APB2上的RCC_APB2_PERIPH_USART1。
同理,其各自对应的外设时钟使能调用都为RCC_EnableAPB2PeriphClk()。
如果使用了USART2/3、UART4/5,则其挂在APB1上,则对应的应使用APB1所关联的。
做好基础定义后,后面的步骤就是:
- 使能GPIO时钟
- 使能USART1时钟
- 初始化TX引脚
- 初始化RX引脚
- 定义通讯参数
- 启用USART1
在上述代码中,因为对应的引脚TX:PA9、RX:PA10,都在GPIOA,上,所以GPIO时钟初始化只有一个;TX和RX引脚,也都使用了同一个USARTx_GPIO。如果是不同的GPIO分组,则需要分别设置。
而如果要使用复用的IO组,则只需要在GPIO引脚初始化的部分,添加上复用的调用即可,具体如下:
#define USARTx_NAME "USART1_1"
#define USARTx USART1
#define USARTx_CLK RCC_APB2_PERIPH_USART1
#define USARTx_GPIO_CLK RCC_APB2_PERIPH_GPIOB
#define USARTx_GPIO GPIOB
#define USARTx_RxPin GPIO_PIN_7
#define USARTx_TxPin GPIO_PIN_6
#define USART_APBxClkCmd RCC_EnableAPB2PeriphClk
#define GPIO_APBxClkCmd RCC_EnableAPB2PeriphClk
#define USARTx_RMP GPIO_RMP_USART1
GPIO_APBxClkCmd(USARTx_GPIO_CLK | RCC_APB2_PERIPH_AFIO, ENABLE);
USART_APBxClkCmd(USARTx_CLK, ENABLE);
GPIO_InitType GPIO_InitStructure;
GPIO_InitStructure.Pin = USARTx_TxPin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitPeripheral(USARTx_GPIO, &GPIO_InitStructure);
GPIO_InitStructure.Pin = USARTx_RxPin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitPeripheral(USARTx_GPIO, &GPIO_InitStructure);
GPIO_ConfigPinRemap(USARTx_RMP, ENABLE);
USART_InitStructure.BaudRate = 115200;
USART_InitStructure.WordLength = USART_WL_8B;
USART_InitStructure.StopBits = USART_STPB_1;
USART_InitStructure.Parity = USART_PE_NO;
USART_InitStructure.HardwareFlowControl = USART_HFCTRL_NONE;
USART_InitStructure.Mode = USART_MODE_RX | USART_MODE_TX;
USART_Init(USARTx, &USART_InitStructure);
USART_Enable(USARTx, ENABLE);
主要逻辑和默认的类似,只是添加了GPIO_ConfigPinRemap()调用。
GPIO_ConfigPinRemap的第一个参数,定义在 n32g45x_gpio.h 中,其取值情况,对应着复用的分组情况,例如:
USART1只有一个复用分组:
那么就提供了一个复用映射调用:GPIO_RMP_USART1
而USART2有三组,则提供了三组复用映射调用:
GPIO_RMP1_USART2
GPIO_RMP2_USART2
GPIO_RMP3_USART2
上述表中,00 、01、10、11为二进制,对应0、1、2、3。使用默认组0时,不需要设置复用映射。
USART2、UART4-UART7的复用映射定义如下:
#define GPIO_RMP1_USART2 ((uint32_t)0x44200000)
#define GPIO_RMP2_USART2 ((uint32_t)0x44200008)
#define GPIO_RMP3_USART2 ((uint32_t)0x46200008)
#define GPIO_RMP1_UART4 ((uint32_t)0x40340010)
#define GPIO_RMP2_UART4 ((uint32_t)0x40340020)
#define GPIO_RMP3_UART4 ((uint32_t)0x40340030)
#define GPIO_RMP1_UART5 ((uint32_t)0x40360040)
#define GPIO_RMP2_UART5 ((uint32_t)0x40360080)
#define GPIO_RMP3_UART5 ((uint32_t)0x403600C0)
#define GPIO_RMP2_UART6 ((uint32_t)0x40380200)
#define GPIO_RMP3_UART6 ((uint32_t)0x40380300)
#define GPIO_RMP1_UART7 ((uint32_t)0x403A0400)
#define GPIO_RMP3_UART7 ((uint32_t)0x403A0C00)
取那个,就根据查表所看到的对应组的二进制码确定了。
但USART3的命名,又有一点点差异了。
USART3能使用的为0、1、3,0不用设置,而1、3对应的复用映射为:
#define GPIO_PART_RMP_USART3 ((uint32_t)0x00140010)
#define GPIO_ALL_RMP_USART3 ((uint32_t)0x00140030)
在使用具体的IO引脚的时候,要注意查看数据手册,检测是否会与其他的功能冲突,例如:
如果你使用了UART4复用引脚PA13、PA14,如果你又用了对应的SWDIO、SWCLK,那么就会冲突了。
经过一番研究测试,尝试了部分复用端口:
以下为具体测试的代码:
main.h:包含多组复用配置
#ifndef __MAIN_H__
#define __MAIN_H__
#ifdef __cplusplus
extern "C" {
#endif
#include "n32g45x.h"
#define _UART5_COM_
#ifdef _USART1_COM_
#define USARTx_NAME "USART1"
#define USARTx USART1
#define USARTx_CLK RCC_APB2_PERIPH_USART1
#define USARTx_GPIO_CLK RCC_APB2_PERIPH_GPIOA
#define USARTx_GPIO GPIOA
#define USARTx_RxPin GPIO_PIN_10
#define USARTx_TxPin GPIO_PIN_9
#define USART_APBxClkCmd RCC_EnableAPB2PeriphClk
#define GPIO_APBxClkCmd RCC_EnableAPB2PeriphClk
#endif
#ifdef _USART1_COM_1_
#define USARTx_NAME "USART1_1"
#define USARTx USART1
#define USARTx_CLK RCC_APB2_PERIPH_USART1
#define USARTx_GPIO_CLK RCC_APB2_PERIPH_GPIOB
#define USARTx_GPIO GPIOB
#define USARTx_RxPin GPIO_PIN_7
#define USARTx_TxPin GPIO_PIN_6
#define USART_APBxClkCmd RCC_EnableAPB2PeriphClk
#define GPIO_APBxClkCmd RCC_EnableAPB2PeriphClk
#define USARTx_RMP GPIO_RMP_USART1
#endif
#ifdef _USART2_COM_
#define USARTx_NAME "USART2"
#define USARTx USART2
#define USARTx_CLK RCC_APB1_PERIPH_USART2
#define USARTx_GPIO_CLK RCC_APB2_PERIPH_GPIOA
#define USARTx_GPIO GPIOA
#define USARTx_RxPin GPIO_PIN_3
#define USARTx_TxPin GPIO_PIN_2
#define USART_APBxClkCmd RCC_EnableAPB1PeriphClk
#define GPIO_APBxClkCmd RCC_EnableAPB2PeriphClk
#endif
#ifdef _USART2_COM_01_
#define USARTx_NAME "USART2_01"
#define USARTx USART2
#define USARTx_CLK RCC_APB1_PERIPH_USART2
#define USARTx_GPIO_CLK RCC_APB2_PERIPH_GPIOD
#define USARTx_GPIO GPIOD
#define USARTx_RxPin GPIO_PIN_6
#define USARTx_TxPin GPIO_PIN_5
#define USART_APBxClkCmd RCC_EnableAPB1PeriphClk
#define GPIO_APBxClkCmd RCC_EnableAPB2PeriphClk
#define USARTx_RMP GPIO_RMP1_USART2
#endif
#ifdef _USART3_COM_
#define USARTx_NAME "USART3"
#define USARTx USART3
#define USARTx_CLK RCC_APB1_PERIPH_USART3
#define USARTx_GPIO_CLK RCC_APB2_PERIPH_GPIOB
#define USARTx_GPIO GPIOB
#define USARTx_RxPin GPIO_PIN_11
#define USARTx_TxPin GPIO_PIN_10
#define USART_APBxClkCmd RCC_EnableAPB1PeriphClk
#define GPIO_APBxClkCmd RCC_EnableAPB2PeriphClk
#endif
#ifdef _USART3_COM_01_
#define USARTx_NAME "USART3_01"
#define USARTx USART3
#define USARTx_CLK RCC_APB1_PERIPH_USART3
#define USARTx_GPIO_CLK RCC_APB2_PERIPH_GPIOC
#define USARTx_GPIO GPIOC
#define USARTx_RxPin GPIO_PIN_11
#define USARTx_TxPin GPIO_PIN_10
#define USART_APBxClkCmd RCC_EnableAPB1PeriphClk
#define GPIO_APBxClkCmd RCC_EnableAPB2PeriphClk
#define USARTx_RMP GPIO_PART_RMP_USART3
#endif
#ifdef _USART3_COM_11_
#define USARTx_NAME "USART3_11"
#define USARTx USART3
#define USARTx_CLK RCC_APB1_PERIPH_USART3
#define USARTx_GPIO_CLK RCC_APB2_PERIPH_GPIOD
#define USARTx_GPIO GPIOD
#define USARTx_RxPin GPIO_PIN_9
#define USARTx_TxPin GPIO_PIN_8
#define USART_APBxClkCmd RCC_EnableAPB1PeriphClk
#define GPIO_APBxClkCmd RCC_EnableAPB2PeriphClk
#define USARTx_RMP GPIO_ALL_RMP_USART3
#endif
#ifdef _UART4_COM_
#define USARTx_NAME "UART4"
#define USARTx UART4
#define USARTx_CLK RCC_APB1_PERIPH_UART4
#define USARTx_GPIO_CLK RCC_APB2_PERIPH_GPIOC
#define USARTx_GPIO GPIOC
#define USARTx_RxPin GPIO_PIN_11
#define USARTx_TxPin GPIO_PIN_10
#define USART_APBxClkCmd RCC_EnableAPB1PeriphClk
#define GPIO_APBxClkCmd RCC_EnableAPB2PeriphClk
#endif
#ifdef _UART5_COM_
#define USARTx_NAME "UART5"
#define USARTx UART5
#define USARTx_CLK RCC_APB1_PERIPH_UART5
#define USARTx_GPIO_CLK RCC_APB2_PERIPH_GPIOC
#define USARTx_GPIO_CLK_Rx RCC_APB2_PERIPH_GPIOD
#define USARTx_GPIO GPIOC
#define USARTx_GPIO_Rx GPIOD
#define USARTx_RxPin GPIO_PIN_2
#define USARTx_TxPin GPIO_PIN_12
#define USART_APBxClkCmd RCC_EnableAPB1PeriphClk
#define GPIO_APBxClkCmd RCC_EnableAPB2PeriphClk
#endif
#ifdef _UART5_COM_11_
#define USARTx_NAME "UART5_11"
#define USARTx UART5
#define USARTx_CLK RCC_APB1_PERIPH_UART5
#define USARTx_GPIO_CLK RCC_APB2_PERIPH_GPIOB
#define USARTx_GPIO GPIOB
#define USARTx_RxPin GPIO_PIN_9
#define USARTx_TxPin GPIO_PIN_8
#define USART_APBxClkCmd RCC_EnableAPB1PeriphClk
#define GPIO_APBxClkCmd RCC_EnableAPB2PeriphClk
#define USARTx_RMP GPIO_RMP3_UART5
#endif
#ifdef _UART6_COM_
#define USARTx_NAME "UART6"
#define USARTx UART6
#define USARTx_CLK RCC_APB2_PERIPH_UART6
#define USARTx_GPIO_CLK RCC_APB2_PERIPH_GPIOE
#define USARTx_GPIO GPIOE
#define USARTx_RxPin GPIO_PIN_3
#define USARTx_TxPin GPIO_PIN_2
#define USART_APBxClkCmd RCC_EnableAPB2PeriphClk
#define GPIO_APBxClkCmd RCC_EnableAPB2PeriphClk
#endif
#ifdef _UART6_COM_10_
#define USARTx_NAME "UART6_10"
#define USARTx UART6
#define USARTx_CLK RCC_APB2_PERIPH_UART6
#define USARTx_GPIO_CLK RCC_APB2_PERIPH_GPIOC
#define USARTx_GPIO GPIOC
#define USARTx_RxPin GPIO_PIN_1
#define USARTx_TxPin GPIO_PIN_0
#define USART_APBxClkCmd RCC_EnableAPB2PeriphClk
#define GPIO_APBxClkCmd RCC_EnableAPB2PeriphClk
#define USARTx_RMP GPIO_RMP2_UART6
#endif
#ifdef _UART7_COM_
#define USARTx_NAME "UART7"
#define USARTx UART7
#define USARTx_CLK RCC_APB2_PERIPH_UART7
#define USARTx_GPIO_CLK RCC_APB2_PERIPH_GPIOC
#define USARTx_GPIO GPIOC
#define USARTx_RxPin GPIO_PIN_5
#define USARTx_TxPin GPIO_PIN_4
#define USART_APBxClkCmd RCC_EnableAPB2PeriphClk
#define GPIO_APBxClkCmd RCC_EnableAPB2PeriphClk
#endif
#ifdef _UART7_COM_01_
#define USARTx_NAME "UART7_01"
#define USARTx UART7
#define USARTx_CLK RCC_APB2_PERIPH_UART7
#define USARTx_GPIO_CLK RCC_APB2_PERIPH_GPIOC
#define USARTx_GPIO GPIOC
#define USARTx_RxPin GPIO_PIN_3
#define USARTx_TxPin GPIO_PIN_2
#define USART_APBxClkCmd RCC_EnableAPB2PeriphClk
#define GPIO_APBxClkCmd RCC_EnableAPB2PeriphClk
#define USARTx_RMP GPIO_RMP1_UART7
#endif
#ifdef _UART7_COM_11_
#define USARTx_NAME "UART7_11"
#define USARTx UART7
#define USARTx_CLK RCC_APB2_PERIPH_UART7
#define USARTx_GPIO_CLK RCC_APB2_PERIPH_GPIOC
#define USARTx_GPIO GPIOG
#define USARTx_RxPin GPIO_PIN_1
#define USARTx_TxPin GPIO_PIN_0
#define USART_APBxClkCmd RCC_EnableAPB2PeriphClk
#define GPIO_APBxClkCmd RCC_EnableAPB2PeriphClk
#define USARTx_RMP GPIO_RMP3_UART7
#endif
#ifdef __cplusplus
}
#endif
#endif
main.c
#include <stdio.h>
#include "main.h"
typedef enum
{
FAILED = 0,
PASSED = !FAILED
} TestStatus;
#define countof(a) (sizeof(a) / sizeof(*(a)))
USART_InitType USART_InitStructure;
void RCC_Configuration(void);
void GPIO_Configuration(void);
TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength);
int main(void)
{
RCC_Configuration();
GPIO_Configuration();
USART_InitStructure.BaudRate = 115200;
USART_InitStructure.WordLength = USART_WL_8B;
USART_InitStructure.StopBits = USART_STPB_1;
USART_InitStructure.Parity = USART_PE_NO;
USART_InitStructure.HardwareFlowControl = USART_HFCTRL_NONE;
USART_InitStructure.Mode = USART_MODE_RX | USART_MODE_TX;
USART_Init(USARTx, &USART_InitStructure);
USART_Enable(USARTx, ENABLE);
printf("\n\rUSART Printf Example: retarget the C library printf function to the %s\n\r", USARTx_NAME);
while (1)
{
}
}
void RCC_Configuration(void)
{
GPIO_APBxClkCmd(USARTx_GPIO_CLK | RCC_APB2_PERIPH_AFIO, ENABLE);
#ifdef USARTx_GPIO_CLK_Rx
GPIO_APBxClkCmd(USARTx_GPIO_CLK_Rx | RCC_APB2_PERIPH_AFIO, ENABLE);
#endif
USART_APBxClkCmd(USARTx_CLK, ENABLE);
}
void GPIO_Configuration(void)
{
GPIO_InitType GPIO_InitStructure;
GPIO_InitStructure.Pin = USARTx_TxPin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitPeripheral(USARTx_GPIO, &GPIO_InitStructure);
GPIO_InitStructure.Pin = USARTx_RxPin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
#ifdef USARTx_GPIO_Rx
GPIO_InitPeripheral(USARTx_GPIO_Rx, &GPIO_InitStructure);
#else
GPIO_InitPeripheral(USARTx_GPIO, &GPIO_InitStructure);
#endif
#ifdef USARTx_RMP
GPIO_ConfigPinRemap(USARTx_RMP, ENABLE);
#endif
}
TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength)
{
while (BufferLength--)
{
if (*pBuffer1 != *pBuffer2)
{
return FAILED;
}
pBuffer1++;
pBuffer2++;
}
return PASSED;
}
int fputc(int ch, FILE* f)
{
USART_SendData(USARTx, (uint8_t)ch);
while (USART_GetFlagStatus(USARTx, USART_FLAG_TXDE) == RESET)
;
return (ch);
}
#ifdef USE_FULL_ASSERT
void assert_failed(const uint8_t* expr, const uint8_t* file, uint32_t line)
{
while (1)
{
}
}
#endif
上述代码,是参考官方资料中的Printf的演示来的,可以将printf重定向到USART/UART输出,然后在此基础上,添加了复用设置。
其中UART5的配置,就包含了不同的时钟、不同的GPIO分组设定,可供参考。
编译烧录后,用串口工具连接对应的引脚,将会收到对应的串口输出的信息:
尝试启用main.h中不同的USART/UART分组,然后对照接线,再编译烧录,就能看到更新了。
有多达7组USART/UART使用,如果配合SPI接口的网卡,就能做一个多串口服务器了,想想都挺激动的。