UART基本介绍:
通用异步收发器UART他的功能非常强大
我们只使用UART的全双工异步通信功能,使用中断接收数据。
UART_RX:串行数据输入。
UART_TX:串行数据输出。
硬件支持:
连接串口(RS232)实现在超级终端里输入输出
软件支持:
超级终端,teraterm-4.75
1、配置UART,我们使用UART2
(1)设置波特率为115200,设置数据字段长为8字,使用1个停止位,无基偶校验,UART Clock disabled,打开发送和接收使能
以上基本设置使用一个函数进行封装设置:
函数内部实现:
设置数据字段长为8字:UART2->CR1 |= 0x00;
使用1个停止位: UART2->CR3 |= 0x00;
使用基数位校验:UART2->CR1 |= 0x00;
UART Clock disabled、打开发送和接收使能等等
(2)打开接收中断,当接收发生或者溢出发生时候,产生接收中断
(3)UART使能
UART2->CR1 &= (uint8_t)(~0x02);
(4)全局中断使能
enableInterrupts();
总体UART配置函数实现如下代码所示:
1 static void UART2_Config(void) 2 { 3 /* EVAL COM (UART) configuration -----------------------------------------*/ 4 /* USART configured as follow: 5 - BaudRate = 115200 baud 6 - Word Length = 8 Bits 7 - One Stop Bit 8 - Odd parity 9 - Receive and transmit enabled 10 - UART Clock disabled 11 */ 12 UART2_Init((uint32_t)115200, UART2_WORDLENGTH_8D,UART2_STOPBITS_1, UART2_PARITY_NO, 13 UART2_SYNCMODE_CLOCK_DISABLE, UART2_MODE_TXRX_ENABLE); 14 15 /* Enable the UART Receive interrupt: this interrupt is generated when the UART 16 receive data register is not empty */ 17 UART2_ITConfig(UART2_IT_RXNE_OR, ENABLE); 18 19 /* Enable the UART Transmit complete interrupt: this interrupt is generated 20 when the UART transmit Shift Register is empty */ 21 UART2_ITConfig(UART2_IT_TXE, ENABLE); 22 23 /* Enable UART */ 24 UART2_Cmd(ENABLE); 25 26 /* Enable general interrupts */ 27 enableInterrupts(); 28 }
2、UART输出功能
如果直接使用C语言的printf函数,只会在编译器的Terminal-I/O中输出,不会在我们想要的超级终端里面输出,所以需要对输出函数做重定向;
实现每次想要输出的时候,将信息打印到超级终端中,故重定向putchar (int c)函数,在函数内使用UART的传送数据功能就可以了,即将要输出的信息写入UART的数据寄存器
1 #define PUTCHAR_PROTOTYPE int putchar (int c) 2 ... 3 /** 4 * @brief Retargets the C library printf function to the UART. 5 * @param c Character to send 6 * @retval char Character sent 7 */ 8 PUTCHAR_PROTOTYPE 9 { 10 /* Write a character to the UART2 */ 11 UART2_SendData8(c); 12 /* Loop until the end of transmission */ 13 while (UART2_GetFlagStatus(UART2_FLAG_TXE) == RESET); 14 15 return (c); 16 }
3、UART输入功能
输入功能实际上是字符串处理过程的实现,在超级终端中输入内容实际上是在UART的数据寄存器里写内容,所我们只需要去数据寄存器里面读取并处理字符串即可;
- 处理函数功能
首先我们得定义一个支持终端回显的函数uart_GetStr,其中功能包括:
(1)当有我们在终端里敲键盘的时候会立马有正确的内容显示;
(2)当按下特殊按键的时候会有正确的反应;比如backspace会删除一个字符;return会表示输入完毕进入发送;
(3)对于其他特殊案件处理不了应当屏蔽;比如不想实现delete功能,删除刚刚读入的delete字符,并不会回显;
- 函数实现:
uart_GetStr传入的第一个参数是指向接收数据数组的指针,第二个参数表示是否允许回显;
几个有用变量:
__IO uint8_t ReciveBuff = 0; //save the current char
uint8_t RxBuffer[32] = {0}; //save the input string
__IO uint8_t RxCounter = 0; //the length of valid string
所以,RxBuffer就是uart_GetStr函数的第一个参数,在uart_GetStr函数内部会对每一个字符进行处理,正确的字符才放入RxBuffer中;
1 //==================================================================================== 2 //Function Name | dbg_GetStr 3 //Description | Get string via UART port. 4 //Input | *p_recv_buff : pointer to receive data buffer 5 // | b_echo_on : echo back on or off 6 //Output | detect terminal(0x0d character) :TRUE or FALSE 7 //Remark | 8 //==================================================================================== 9 uint8_t uart_GetStr(uint8_t *p_recv_buff, bool b_echo_on) 10 { 11 uint8_t b_end = 0; 12 int i; 13 static uint8_t len = 0; 14 static uint8_t pos = 0; 15 static uint8_t esc_seq = 0; 16 uint8_t c; 17 18 // Get a character. 19 if((c = ReciveBuff) == 0){ 20 return 0; 21 } 22 // echo back 23 if(b_echo_on){ 24 printf("%c",c); //show the input 25 } 26 27 //Check 28 switch(esc_seq){ 29 // Normal 30 case 0: 31 // Return(Terminate) 32 if(c == 0x0d){ 33 p_recv_buff[len] = 0; 34 len = pos = 0; // clear 35 if(b_echo_on){ 36 printf(" "); 37 } 38 b_end = 1; 39 } 40 // Back Space 41 else if(c == 0x08){ 42 if(len){ 43 if(b_echo_on){ 44 printf(" "); 45 printf("%c",0x08); // BS 46 } 47 len--; // update length info. 48 pos--; // update pos info 49 } 50 } 51 // ESC 52 else if(c == 0x1b){ 53 esc_seq = 1; 54 } 55 // DEL 56 else if(c == 0x7f){ 57 if(len){ 58 DeleteChar(pos, len, &p_recv_buff[0]); 59 len --; // update length info 60 if(b_echo_on){ 61 printf("%s",&p_recv_buff[pos]); 62 printf(" "); 63 printf("%c",0x08); // BS 64 // move cursor to character end. 65 for(i = 0; i < len - pos; i++){ 66 printf("%c",0x1b); // ESC 67 printf("%c",'['); 68 printf("%c",'D'); 69 } 70 } 71 } 72 } 73 // Other 74 else{ 75 p_recv_buff[pos] = c; 76 len++; // update length info 77 pos++; // update pos info 78 } 79 break; 80 // ESC SEQ -> 1st 81 case 1: 82 if(c == '['){ 83 esc_seq = 2; // Next seq. 84 } 85 else{ 86 esc_seq = 0; // not support(to normal) 87 } 88 break; 89 //ESC SEQ -> 2nd 90 case 2: 91 if(c=='D'){ 92 if(pos){ 93 pos--; // "<-" key 94 } 95 } 96 else if(c=='C'){ 97 if(pos < len){ 98 pos++; // "->" key 99 } 100 } 101 esc_seq = 0; // To normal 102 break; 103 } 104 105 return (b_end); 106 }
uart_GetStr函数值为0或者为1,只有在终端中输入回车时才会返回1,其他情况均返回0;
有了这一特点,我们在使用这个函数的时候,只需要获取返回值,当返回0时,把处理过后的、用户正确输入的字符串打印出来看;
- 运行流程
(1)当终端中有输入的时候,即数据寄存器不为空,从而触发接收中断;
(2)在接收中断中,我们每次从数据寄存器UART2->DR中读取一个字节,赋值到ReciveBuff;
(3)然后调用uart_GetStr函数,对当前输入字符进行处理;(即每一个输入调用一次处理函数)
(4)将uart_GetStr函数的返回值赋值给RT;
1 INTERRUPT_HANDLER(UART2_RX_IRQHandler, 21) 2 { 3 4 /* Read one byte from the receive data register and send it back */ 5 6 ReciveBuff = (UART2_ReceiveData8() & 0x7F); //get input char 7 8 RT = uart_GetStr(RxBuffer, TRUE); //operate the char 9 10 }
(5)在主函数里死等,直到RT的值变为1(表示有return发生,即一次输入完毕);
(6)当RT=1时,主函数输出正确的输入字符串RxBuffer(这里可以实现更强大的功能,此处只是简单实现验证功能,可以对正确字符串做判断后实现更强大的功能)
1 void main(void) 2 { 3 /* Clock configuration -----------------------------------------*/ 4 CLK_Config(); 5 6 /* TIM4 configuration -----------------------------------------*/ 7 TIM4_Config(); 8 9 10 /* UART2 configuration -----------------------------------------*/ 11 UART2_Config(); 12 13 14 while(1){ 15 printf(" please input something: "); 16 17 while(1){ //wait until input something 18 19 if(RT) // return occurred 20 { 21 RT = 0; //clear flag 22 23 printf("your put is:"); 24 printf("%s ",RxBuffer); //verify the input 25 26 /*-- operate the string --*/ 27 /*------------------------*/ 28 29 //memset(RxBuffer,0,32); //clear the buffer 30 break; //operation finished 31 } 32 33 } 34 35 /*-- or operate the string here --*/ 36 if(strcmp(RxBuffer, "Hello") == 0) 37 { //case sensitive 38 printf(" Hello World !! "); 39 } 40 /*--------------------------------*/ 41 42 } 43 44 }
- 说明:
这里的打印和上面的回显虽然都是输出到终端,但是不同的是,回显是为了让用户知道自己是在实时输入内容的,而打印是把用户本次输入的字符串处理之后再一并返回给终端,方便用户检查自己输入的内容;
如用户输入:ABCED'backspace''backspace'DE
在终端中实时回显的就是字符串:ABCED'backspace''backspace'DE
而打印的字符串是处理过后的字符串,为:ABCDE
把uart_GetStr的第二个参数分别设置为TRUE和FALSE后观察终端输入操作的不同,这样就能明白他们的不同了。