USART介绍与UART的使用

UART为串行总线,为全双工通信,即发送的同时又可以接收数据

USART中S意为同步传输,A意为异步传输:

同步通讯:数据线获取高低电平数据

                  CLK时钟来让AB同步进行传输,并规定B只要检测上升沿就会读值

                  CS先把电平拉低,B检测到电平拉低就表示A现在要跟B进行通信了

CS电平拉低之后,A就会在数据总线上把数据发送出来,AB此时约定,只要B检测到CLK只在上升沿时才读值。

 异步通讯:收发双方没有片选信号线,也没有时钟信号线,就只有一根数据传输线(TXD发送管脚,RXD接收管脚,GND让我们双方的电平基准保持一致)。 

        在空闲的时候,规定双方都是高电平,电平突然从高变低,则说明A要发送数据了,电平拉低之后,1bit位的起始位之后开始发送数据(此数据格式一般都用5~8bit位,C语言一个char型正好是8位,所以一般常用8位)双方约定数据位长度保持一致,奇偶校验位校验数据是奇数个还是偶数个,1为奇校验,多数情况下咱们不使用奇偶校验,另外在奇偶校验之前双方也要约定是否存在奇偶校验位且是奇校验还是偶校验,紧接着就是双方约定是1位、1.5位或2位停止位,不同处理器停止位长度不一样,停止位结束再次把电平拉高,回到正常情况,此时一帧数据结束。(一般情况我们用8N1的组合,即8位数据位,No奇偶校验位,1位停止位)

        另外串口的通信频率也要保持一致,否则就会出现发送和接收双方接收1bit数据所占用的时间不同,此时就要保证我们的bps(波特率)保持相同

        总结:收发双方进行通讯的时候要保证1.数据位长度确定;2.有无奇偶校验位,若有则是奇校验还是偶校验;3.停止位的长度多少;4.波特率相同。

        ARM芯片需要RS232或TTL转USB芯片,USB数据的发送和接收用到的是D+和D-这两个接口,开发板出来的TTL电平通过转换芯片转化成为USB的协议,PC机就可以接收了。

        CTS和RTS为硬件流控管脚:RTS指的是发送请求的信号,是一个输出信号,用来指示当前设备已经准备好可以用于接收数据了,CTS指的是发送允许,是一个输入信号,用于判断发送端是否可以发送数据,这两个都是低电平有效的。UART数据传输,传输的是串行数据流,数据在不同的串口中传输的时候经常会出现丢失数据的现象,原因是开发板和PC机通信的传输处理速率不一样,接收缓冲区如果满了,你继续往这里面发的话,就会造成数据的丢失,我们采用流控的方法就可以解决这个问题,当接收端的数据处理不过来的时候就告诉它不要再发送数据了,发送端就会停止发送,直到接收端确认可以继续发送,发送端才会继续发送。

1.UART1挂在APB2总线上,打开APB2总线的时钟,即打开UART1的时钟,因为用到了PA9和PA10因此也要开启GPIOA的时钟。

        PA9是发送功能,此时不能当作普通的GPIOA口的输出,而应该当作复用功能,即复用推完输出功能,而不能是GPIO口的通用推挽输出功能,PA10接收数据可能是高或低,因此肯定不能是上拉输入和下拉输入,不是AD转换所以也不能是模拟量输入,因此只可能是浮空输入。

GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1,ENABLE);

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);
 
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);

GPIO口配置完毕之后下面我们需要配置UART功能。

USART_InitTypeDef USART_InitStructure;

USART_InitStructure.USART_BaudRate = 115200; 
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_Tx | USART_Mode_Rx;
USART_Init(USART1, &USART_InitStructure);

USART_Cmd(USART1, ENABLE);//前面都是初始化,Cmd才是真正的使能

        STM32发送和接收缓冲寄存器实际上只有一个,就是USART_DR数据寄存器 ,只要把数据放到发送缓冲区,数据就会自动发出去,如果需要读数则只需要从接收数据缓冲区读出来就可以了,在main函数中使用USATR_SendData函数则可以通过外设USARTx发送单个数据:

while(1)
{
    USART_SendData(USART1, 0x41);//往UART1里面发送A,A的值为65,即0x41
    Delay(1000000);//每隔1s
}

发送字符串思路:定义一个指针指向这个字符串,把发送字符串改为一个个单独发送字符的思想。

        为了防止发送缓冲区处理数据可以得到妥善处理,我们需要由硬件来判断,硬件会有标志位来标志我们的数据缓冲区是否为空即你当前的数据是否发送完成,库函数中就有标志位检查的函数。

void USART_SendString(const unsigned char *pt)//定义一个指向字符串的指针
{
    while(*pt)
    {
        while(!USART_GetFlagStatus(USART1, USART_FLAG_TXE));//函数为空返回1,不为空则返回0
        //确保发送缓冲区为空,只有为空才发送,不为空则等待,因此用while循环来等待
        USART_SendData(USART1 , *pt);
         while(USART_GetFlagStatus(USART1, USART_FLAG_TC));//等待发送完成
        pt++;
    }
}

那如何能让我们的UART来像printf那样想输出啥就输出啥呢?

C语言重定向功能就可以达到我们的要求!

        咱们都知道printf并不是一个真正的函数,它是一个函数宏,靠的是我们的fputc函数实现的,如果我们自己写一个fputc,当它去调用函数的时候,它会优先调用我们自己的fputc,而不是C库中的,我们只要把fputc重写一下,让fputc不发送到我们的标准输出设备上而把它定向到我们的UART口上,这样就可以

#include <stdio.h>
int fputc (int c,FILE *fp)
{
    while(!USART_GetFlagStatus(USART1, USART_FLAG_TXE));
    USART_SendData(USART1 , c);//通过fputc把字符c通过UART口发送出去
    while(USART_GetFlagStatus(USART1, USART_FLAG_TC));

    return 0;
}

主函数我们就可以写成:

int main(void)
{
    int i = 0;
    
    SysTick_Configuration();

    Uart1_Configuration();
    
    while(1)
    {
        printf("Hello World\n");
        printf("i = %d\n",i++);
        Delay(1000000);
    }
}

目前已经完成了UART的发送,那如何接收呢?重新编写一个fgetc即可

int fgetc(FILE *ft)
{
    while(!USART_GetFlagStatus(USART1, USART_FLAG_RXNE));//接收数据寄存器非空标志位
    rteurn (int)USART_ReceiveData(USART1);//返回 USART1 最近接收到的数据,将char型转换成int型
}

主函数可以写成:

int main(void)
{
    char ch;
    
    SysTick_Configuration();

    Uart1_Configuration();
    
    while(1)
    {
        scanf("%c",&ch);
        printf("%c",ch);
    }
}

 这样虽然可以实现,但是会出现一些奇怪的问题,于是我们这里用更规范的中断的办法来接收:

首先初始化我们UART1的NVIC

void Uart1_NVIC_Init(void)
{
	NVIC_InitTypeDef NVIC_InitStructure;
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);	
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//中断的来源换成我们的UART1
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//中断优先级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
}

此时我们只有UART口一收到数据此时我们就会立刻跳转到我们的异常向量表当中

void USART1_IRQHandler(void)
{
	unsigned char ch;
	while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET)//确实非空收到数据之后再处理
	{
		ch = USART_ReceiveData(USART1);
		printf("%c\n", ch + 1);//将我们收到的数据+1输出出来
	}			
}

对USATR_DR的读操作可以将该位清零,所以因此就不需要我们再在中断的尾端再加一个清除中断标志位的指令了。

int main(void)
{
    SysTick_Configuration();

    Uart1_Configuration();
    Uart1_NVIC_Init();
    
    while(1)
    {
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值