1. 串口的基本概念
在STM32的参考手册中,串口被描述成通用同步异步收发器(USART),它提供了一种灵活的方法与使用工业标准NRZ异步串行数据格式的外部设备之间进行全双工数据交换。USART利用分数波特率发生器提供宽范围的波特率选择。它支持同步单向通信和半双工单线通信,也支持LIN(局部互联网),智能卡协议和IrDA(红外数据组织)SIR ENDEC规范,以及调制解调器(CTS/RTS)操作。它还允许多处理器通信。还可以使用DMA方式,实现高速数据通信。
USART通过3个引脚与其他设备连接在一起,任何USART双向通信至少需要2个引脚:接受数据输入(RX)和发送数据输出(TX)。
RX: 接受数据串行输入。通过过采样技术来区别数据和噪音,从而恢复数据。
TX: 发送数据输出。当发送器被禁止时,输出引脚恢复到它的I/O端口配置。当发送器被激活,并且不发送数据时,TX引脚处处于高电平。在单线和智能卡模式里,此I/O口被同时用于数据的发送和接收。
2. 串口的如何工作的
一般有两种方式:查询和中断。
(1)查询:串口程序不断地循环查询,看看当前有没有数据要它传送。如果有,就帮助传送(可以从PC到STM32板子,也可以从STM32板子到PC)。
(2)中断:平时串口只要打开中断即可。如果发现有一个中断来,则意味着要它帮助传输数据——它就马上进行数据的传送。同样,可以从 PC到STM3板子,也可以从STM32板子到PC。
3.
4. 串口配置
我(1) RCC配置;
(2) GPIO配置;
(3) USART配置;
(4) NVIC配置;
(5) 发送/接收数据。
在RCC配置中,我们除了常规的时钟设置以外,要记得打开USART相对应的IO口时钟,USART时钟,还有管脚功能复用时钟。
在GPIO配置中,将发送端的管脚配置为复用推挽输出,将接收端的管脚配置为浮空输入。
在USART的配置中,通过USART_InitTypeDef结构体对USART进行初始化操作,按照自己所需的功能配置好就可以了。注意,在超级终端的设置中,需要和这个里面的配置相对应。由于我是采用中断接收数据的方式,所以记得在USART的配置中药打开串口的中断,同时最后还要打开串口。
在NVIC的配置中,主要是USART1_IRQChannel的配置,和以前的笔记中讲述的中断配置类似,不会配置的可以参考以前的笔记。
全部配置好之后就可以开始发送/接收数据了。发送数据用USART_SendData()函数,接收数据用USART_ReceiveData()函数。具体的函数功能可以参考固件库的参考文件。根据USART的配置,在发送和接收时,都是采用的8bits一帧来进行的,因此,在发送的时候,先开辟一个缓存区,将需要发送的数据送入缓存区,然后再将缓存区中的数据发送出去,在接收的时候,同样也是先接收到缓存区中,然后再进行相应的操作。
注意在对数据进行发送和接收的时候,要检查USART的状态,只有等到数据发送或接收完毕之后才能进行下一帧数据的发送或接收。采用USART_GetFlagStatus()函数。
同时还要注意的是,在发送数据的最开始,需要清除一下USART的标志位,否则,第1位数据会丢失。因为在硬件复位之后,USART的状态位TC是置位的。当包含有数据的一帧发送完成之后,由硬件将该位置位。只要当USART的状态位TC是置位的时候,就可以进行数据的发送。然后TC位的置零则是通过软件序列来清除的,具体的步骤是“先读USART_SR,然后写入USART_DR”,只有这样才能够清除标志位TC,但是在发送第一帧数据的时候,并没有进行读USART_SR的操作,而是直接进行写操作,因此TC标志位并没有清空,那么,当发送第一帧数据,然后用USART_GetFlagStatus()检测状态时返回的是已经发送完毕(因为TC位是置1的),所以程序会马上发送下一帧数据,那么这样,第一帧数据就被第二帧数据给覆盖了,所以看不到第一帧数据的发送。
#include "stm32f10x_lib.h" #include <stdio.h> #define uchar unsigned char #define uint unsigned int #define ulong unsigned long void RCC_Configuration(void); void GPIO_Configuration(void); void NVIC_Configuration(void); void USART1_Configuration(void); void delay_nus(u32 us);
int fputc(int ch, FILE *f) { USART_SendData(USART1, (u8)ch); /* Loop until the end of transmission */ while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) { }
return ch; }
void wellcome() { printf(" *************************************\r\n"); printf(" ******** hello,eeworld! ********\r\n"); printf(" ******** this is mengzhilv! ********\r\n"); printf(" ******** Copyright @ 2011 ********\r\n"); printf(" *************************************\r\n"); delay_nus(100000); }
int main(void) { u16 i; #ifdef DEBUG debug(); #endif RCC_Configuration(); //配置系统时钟 NVIC_Configuration();//配置 NVIC 和 Vector Table GPIO_Configuration(); //配置使用的GPIO口 USART1_Configuration(); wellcome(); while (1) { if(USART_GetFlagStatus(USART1, USART_IT_RXNE) == SET) { i = USART_ReceiveData(USART1); printf(" %c",i&0xff); } } }
void RCC_Configuration(void) { ErrorStatus HSEStartUpStatus; RCC_DeInit();//将外设 RCC寄存器重设为缺省值 RCC_HSEConfig(RCC_HSE_ON); //设置外部高速晶振(HSE) HSEStartUpStatus = RCC_WaitForHSEStartUp();//等待 HSE 起振 if(HSEStartUpStatus == SUCCESS) { FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);//预取指缓存使能 FLASH_SetLatency(FLASH_Latency_2);//设置代码延时值 FLASH_Latency_2 2 延时周期 RCC_HCLKConfig(RCC_SYSCLK_Div1); //设置 AHB 时钟(HCLK) RCC_SYSCLK_Div1 AHB 时钟 = 系统时钟 RCC_PCLK2Config(RCC_HCLK_Div2);//设置高速 AHB 时钟(PCLK2)RCC_HCLK_Div2 APB1 时钟 = HCLK / 2 RCC_PCLK1Config(RCC_HCLK_Div2);//设置低速 AHB 时钟(PCLK1) RCC_HCLK_Div2 APB1 时钟 = HCLK / 2 RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);// PLLCLK = 8MHz * 9 = 72 MHz 设置 PLL 时钟源及倍频系数 RCC_PLLCmd(ENABLE);//使能或者失能 PLL while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) //等待指定的 RCC 标志位设置成功 等待PLL初始化成功 { } RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //设置系统时钟(SYSCLK) 设置PLL为系统时钟源
//等待PLL成功用作于系统时钟的时钟源 // 0x00:HSI 作为系统时钟 // 0x04:HSE作为系统时钟 // 0x08:PLL作为系统时钟 while(RCC_GetSYSCLKSource() != 0x08) { } } RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);//使能或者失能 APB2 外设时钟 }
void GPIO_Configuration(void) { //定义一个GPIO结构体 GPIO_InitTypeDef GPIO_InitStructure; //LED 端口设置 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; GPIO_Init(GPIOB, &GPIO_InitStructure); //USART1_TX GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); //USART1_RX GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void USART1_Configuration(void) { USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; /* Configure USART1 */ USART_Init(USART1, &USART_InitStructure); /* Enable the USART1 */ USART_Cmd(USART1, ENABLE); }
void NVIC_Configuration(void) { #ifdef VECT_TAB_RAM /* Set the Vector Table base location at 0x20000000 */ NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); #else /* VECT_TAB_FLASH */ /* Set the Vector Table base location at 0x08000000 */ NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0); #endif }
void delay_nus(u32 us) { unsigned char i; while(us--) { i=7; //1us while(i--); } }
#ifdef DEBUG void assert_failed(u8* file, u32 line) { while (1) { } } #endif
|