图3. UART的波特率公式
实验
本实验配置UART的基本发送与接收功能,配置时钟速率为48MHz,波特率为9600,数据长度为8位,不使用校验及自动流控制,设置PA9为TX引脚,PA10为RX引脚。通过串口调试工具观察数据的传输,发送数据与接收数据相同。
配置系统时钟 clock_init()
如图4所示,高速外部时钟(HSE)的频率范围为4 ~ 24MHz,实验所使用的晶振为12M,要使系统时钟为48MHz,则配置PLL输出48MHz做系统时钟,操作时钟控制寄存器(RCC_CR)的HSEON位使能HSE,等待HSERDY位拉高(即HSE时钟被释放),设置PLL配置寄存器(RCC_PLLCFGR)中的PLLSRC位为1,并根据公式配置对应参数,PLL配置公式如图5所示。配置闪存访问控制寄存器(FLASH_ACR)启用闪存预取,配置时钟配置寄存器(RCC_CFGR)设置分频并配置PLL输出做系统时钟。void clock_init()
{
/* Enable HSE. */
RCC->CR |= RCC_CR_HSEON_MASK;
while ( RCC_CR_HSERDY_MASK != (RCC->CR & RCC_CR_HSERDY_MASK ) ) /* Wai
ting HSE ready. */
{
}
/* F_clko = F_refin * N/(M * P), F_refin = 12M, 12*8/(1*2) = 48. */
RCC->PLLCFGR = RCC_PLLCFGR_PLLSRC(1) /* HSE clock is used as PLL input clock. */
| RCC_PLLCFGR_PLLDN(7) /* N = DN + 1 = 7 + 1 = 8. */
| RCC_PLLCFGR_PLLDM(1) /* M = DM + 1 = 1 + 1 = 2. */
| RCC_PLLCFGR_PLLDP(0) /* P = DP + 1 = 0 + 1 = 1. */
| RCC_PLLCFGR_PLLLDS(1) /* PLL lock detector accuracy select. */
| RCC_PLLCFGR_PLLICTRL(3) /* 10uA. */
;
/* Enable PLL. */
RCC->CR |= RCC_CR_PLLON_MASK;
while( 0u == ( RCC->CR & RCC_CR_PLLRDY_MASK ) ) /* Waiting PLL ready. */
{
}
/* Enable the FLASH prefetch. */
RCC->AHB1ENR |= RCC_AHB1ENR_FLITFEN_MASK; /* Enable the access to FLASH. */
FLASH->ACR = FLASH_ACR_LATENCY(1u) /* Setup divider: 1 for 48Mhz. */
| FLASH_ACR_PRFTBE_MASK /* Enable flash prefetch. */
;
/* Setup the dividers for each bus. */
RCC->CFGR = RCC_CFGR_HPRE(0) /* Div=1 for AHB freq. */
| RCC_CFGR_PPRE1(0x0) /* Div=1 for APB1 freq. */
| RCC_CFGR_PPRE2(0x0) /* Div=1 for APB2 freq. */
| RCC_CFGR_MCO(7) /* Use PLL/2 as output. */
;
/* Switch the system clock source to PLL. */
RCC->CFGR = ( (RCC->CFGR & ~RCC_CFGR_SW_MASK ) | RCC_CFGR_SW(2) ); /* Use PLL as SYSCLK. */
/* Wait till PLL is used as system clock source. */
while ( (RCC->CFGR & RCC_CFGR_SWS_MASK ) != RCC_CFGR_SWS(2) )
{
}
}
图4. MM32F0140部分时钟树
图5. PLL配置公式
启用UART外设时钟 enable_clock()
UART1的UART1_TX与UART1_RX复用引脚为PA9与PA10,因此初始化GPIOA与UART1的外设时钟,UART1在APB2上,GPIOA在AHB上。void enable_clock()
{
/* Enable UART1 clock. */
RCC->APB2ENR |= RCC_APB2_PERIPH_UART1;
/* Enable GPIOA clock. */
RCC->AHB1ENR |= RCC_AHB1_PERIPH_GPIOA;
}
配置引脚 pin_init()
由于UART的TX与RX引脚配置为复用功能配置,如图6所示,PA9, PA10的UART1_TX与UART1_RX均使用AF1,对端口复用功能高位寄存器(GPIO_AFRH)的端口9、端口10对应位赋值。void pin_init()
{
/* Setup PA9, PA10. */
GPIOA->CRH &= ~GPIO_CRH_MODE9_MASK;
GPIOA->CRH |= GPIO_PinMode_AF_PushPull; /* PA9 multiplexed push-pull output. */
GPIOA->AFRH &= ~GPIO_AFRH_AFR_MASK;
GPIOA->AFRH |= (GPIO_AF_1 << GPIO_CRH_MODE9_SHIFT); /* Use AF1. */
GPIOA->CRH &= ~GPIO_CRH_MODE10_MASK;
GPIOA->CRH |= GPIO_PinMode_In_Floating; /* PA10 floating input. */
GPIOA->AFRH |= (GPIO_AF_1 << GPIO_CRH_MODE10_SHIFT); /* Use AF1. */
}
图6. 部分引脚复用表格
UART初始化 uart_init()
初始化UART需要配置:时钟频率、波特率、数据长度、停止位、传输模式及是否使用校验。
如图7所示,UART全局控制寄存器(UART_GCR)的TXEN位与RXEN位控制传输模式,两位均置1表示传输模式为TX与RX,AUTOFLOWEN位控制是否使用自动流控制,UARTEN位控制UART的使能。
如图8所示,UART通用控制寄存器(UART_CCR)的SPB1、SPB0位控制停止位位数,CHAR位控制数据宽度,PSEL位选择采用奇校验还是偶校验,PEN位控制校验使能;UART波特率寄存器(UART_BRR)与UART分数波特率寄存器(UART_FRA)分别存储UART分频器除法因子的整数与小数。void uart_init()
{
/* Clear the corresponding bit to be used. */
UART1->CCR &= ~( UART_CCR_PEN_MASK | UART_CCR_PSEL_MASK | UART_CCR_SPB0_MASK | UART_CCR_SPB1_MASK | UART_CCR_CHAR_MASK );
UART1->GCR &= ~( UART_GCR_AUTOFLOWEN_MASK | UART_GCR_RXEN_MASK | UART_GCR_TXEN_MASK );
/* WordLength. */
UART1->CCR |= UART_CCR_CHAR_MASK;
/* XferMode. */
UART1->GCR |= (UART_XferMode_RxTx << UART_GCR_RXEN_SHIFT);
/* Setup baudrate, BOARD_DEBUG_UART_FREQ = 48000000u, BOARD_DEBUG_UART_BAUDRATE = 9600u. */
UART1->BRR = (BOARD_DEBUG_UART_FREQ / BOARD_DEBUG_UART_BAUDRATE) / 16u;
UART1->FRA = (BOARD_DEBUG_UART_FREQ / BOARD_DEBUG_UART_BAUDRATE) % 16u;
/* Enable UART1. */
UART1->GCR |= UART_GCR_UARTEN_MASK;
}
图7. MM32F0140 UART_GCR寄存器
图8. MM32F0140 UART_CCR寄存器部分位
UART发送数据 uart_putchar()
通过读取UART当前状态寄存器(UART_CSR)获取当前状态,当发送缓冲区为空时,可进行数据发送,将发送数据传入UART发送数据寄存器(UART_TDR),定义发送数据函数uart_putchar(),变量“c”为需要发送的数据。void uart_putchar(uint8_t c)
{
while ( 0u == ( UART_STATUS_TX_EMPTY & (UART1->CSR) ) ) /* Waiting tx buffer empty. */
{}
UART1->TDR = (uint8_t)c;
}
UART接收数据 uart_getchar()
通过读取UART当前状态寄存器(UART_CSR)获取当前状态,当接收缓冲接收了一个完整字节的数据时,可读取UART接收数据寄存器(UART_RDR)获取接收数据,定义接收数据函数uart_getchar(),该函数返回接收的数据。uint8_t uart_getchar(void)
{
while ( 0u == ( UART_STATUS_RX_DONE & (UART1->CSR) ) ) /* Waiting rx buffer receives a complete byte of data. */
{}
return (uint8_t)(UART1->RDR & 0xff);
}
UART输出字符串 uart_putbuffer()
使用UART发送函数编写发送字符串函数。void uart_putbuffer(uint8_t *str)
{
while ((*str) != '