×
单片机 > 单片机程序设计 > 详情

12. 串口实验

发布时间:2020-06-05 发布时间:
|

一。单片机通信的知识

1. 通信的两种方式


2. 串行通信的三种传输方式

半双工数据发送和接收数据不能同时传输,全双工发送和接收互不影响,数据传输可以同时进行。


3. 串行通信的通信方式

对于同步通信,除了一条数据线以外,还需要一条时钟线,用于传输同步时钟信号,数据的每个位传输都是随着时钟传输。

异步通信没有时钟信号,这就需要通信双方事先要进行约定,比如UART通信要事先约定好波特率,才能保证通信数据的正确。

4. 常见的串行通信接口

5. STM32的串口通信接口



6. UART异步通信方式引脚接线方法



PC机上的DB9接口电平为232电平,不能直接跟单片机上TTL信号连接,两者的电平不兼容。

7. UART异步通信方式的引脚

对于大容量的STM32最多有5个串口。

RXD: 数据输入引脚,接收数据。

TXD: 数据输出引脚,发送数据。

二。UART异步通信的特点






 



奇偶校验位:比如传输8位数据,其中有3个1,如果是偶校验,因为前面有3个1,就需要在奇偶校验位加一个1,使传输的1为偶数个,如果前面传输的数据有4个1,就在奇偶校验位补0。奇校验则相反,如果前面的数据中有四个1,就需要在奇校验位补一个1,是1成为奇数个,如果前面有奇数个1,就在奇偶校验位补0。


设置奇偶校验是为了提高数据传输的准确率。

三。UART框图


1. 理解数据传输和接收过程。

2. 发送和接收共用一个波特率发生器。

对于STM32F103R8T6只有3个USART,串口1,串口2,串口3.

串口1的时钟来源于PCLK2

串口2-串口4的时钟来源于PCLK1.

PCLKx进来后进入USARTDIV进行分频。分频值的大小由USART_BRR寄存器进行配置。

时钟可以进行整数的分频,还可以进行小数的分频,比如可以 /36,还可以 /36.5(前面只是举了一个例子,实际上小数必须是1/16的整数倍),分频后再除以16,产生的时钟进入发送器时钟或接收器时钟。



SR寄存器发送接收数据过程中的标志位。




CR1寄存器一部分为发送接收使能位,另外还有中断使能位,可以编写中断函数,在中断函数中判断是哪个中断发生了。


四。串口常用的寄存器

1. SR状态寄存器


RXNE:读数据寄存器非空,DR寄存器已经收到数据置1

TC:发送完成,数据发送完成后置1

2. DR寄存器


只用到了位0--位8

3. BRR寄存器

波特率寄存器


用到低16位,低4位USARTDIV的小数部分

位4-位15,USARTDIV的整数部分


PCLK1用于串口2,3,4,5,为36M

PCLK2用于串口1,为72M

4. CR1寄存器

控制寄存器,主要有发送和接收使能,以及相关的中断使能



RE:接收使能

TE:发送使能

RXNEIE:接收缓冲区非空中断使能,在接收到完整的数据后如果在这里使能了中断,就可以产生中断。 

五。串口操作常用的相关库函数

六。 串口配置的一般步骤




中断的通道在stm32f10x.h中

USART1的中断通道为 USART1_IRQn。

中断文件的格式在启动文件 startup_stm32f10x_hd.s 中。

七。 串口几个重要函数

1.void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct); 串口初始化函数

作用:初始化串口的一些重要参数,包括波特率,奇偶校验位,停止位等

2.void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);

作用:开启相应的串口的中断。

3.void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);

作用:使能相应的串口

4.void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);

作用:串口发送数据,往串口发送一个数据

5.uint16_t USART_ReceiveData(USART_TypeDef* USARTx);

作用:串口接收数据

6.void USART_DeInit(USART_TypeDef* USARTx);

作用:串口复位

7.void USART1_IRQHandler(void)                 //串口1中断服务函数

{

}

通过以上几个函数就可以实现数据的发送和接收

中断状态的获取,复位相关的一些函数

1. FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);

2. void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG);

3. ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);

4. void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);

八。串口配置的一般步骤

1)  串口时钟使能,GPIO 时钟使能,需要使能GPIOA和USART1的时钟

2)  串口复位

3)  GPIO 端口模式设置

4)  串口参数初始化

5)  开启中断并且初始化 NVIC(如果需要开启中断才需要这个步骤)

6)  使能串口

7)  编写中断处理函数

1. 配置RXD,TXD引脚

发送TXD(PA9)设置为推挽复用输出

接收RXD(PA10)设置为浮空或带上拉输入

在misc.c文件中有中断分组的配置函数

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)

例:串口初始化

void My_USART1_Init(void)

{

GPIO_InitTypeDef GPIO_InitStru;

USART_InitTypeDef USART_InitStru;

NVIC_InitTypeDef  NVIC_InitStru;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//USART1时钟来自PCLK2

GPIO_InitStru.GPIO_Mode= GPIO_Mode_AF_PP;  //推挽复用输出

GPIO_InitStru.GPIO_Pin = GPIO_Pin_9;

GPIO_InitStru.GPIO_Speed = GPIO_Speed_10MHz;

GPIO_Init(GPIOA,&GPIO_InitStru);

GPIO_InitStru.GPIO_Mode= GPIO_Mode_IN_FLOATING;  //浮空输入

GPIO_InitStru.GPIO_Pin = GPIO_Pin_10;

GPIO_InitStru.GPIO_Speed = GPIO_Speed_10MHz;

GPIO_Init(GPIOA,&GPIO_InitStru);

USART_InitStru.USART_BaudRate = 115200;  //波特率为115200

USART_InitStru.USART_HardwareFlowControl = USART_HardwareFlowControl_None;  //硬件流控制        为无

USART_InitStru.USART_Mode = USART_Mode_Rx|USART_Mode_Tx; //工作模式为发送和接收

USART_InitStru.USART_Parity = USART_Parity_No;  //没有奇偶校验

USART_InitStru.USART_StopBits = USART_StopBits_1; //一个停止位

USART_InitStru.USART_WordLength = USART_WordLength_8b;  //字长为8(因为没有奇偶校验)

USART_Init(USART1,&USART_InitStru);

USART_Cmd(USART1,ENABLE);  //使能串口1

USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);  //设置串口中断的类型,这里选择接收缓冲区非空        产生中断

NVIC_InitStru.NVIC_IRQChannel = USART1_IRQn;  //定义在哪个通道,在顶层文件stm32f10x.h文件中         定义

NVIC_InitStru.NVIC_IRQChannelCmd = ENABLE;

NVIC_InitStru.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级 0-3

NVIC_InitStru.NVIC_IRQChannelSubPriority = 1;  //子优先级 0-3 

NVIC_Init(&NVIC_InitStru);  //设置中断抢占优先级和响应优先级

}

中断服务函数的格式在启动文件startup_stm32f10x_hd.s中定义

void USART1_IRQHandler(void)

 {

u8 res;

 

if( USART_GetITStatus(USART1,USART_IT_RXNE)) //判断是否是接收到数据产生的中断

{

res = USART_ReceiveData(USART1);  //接收数据

USART_SendData(USART1,res);  //重新发送回这个数据

}

 }

//初始化IO,串口1

//bound:波特率为入口参数

void uart_init(u32 bound){

    //GPIO端口设置

    GPIO_InitTypeDef GPIO_InitStructure;

    USART_InitTypeDef USART_InitStructure;

    NVIC_InitTypeDef NVIC_InitStructure;

 

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);

    //使能USART1,GPIOA的时钟

    USART_DeInit(USART1);  //复位串口1

    //USART1_TX   PA.9

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出

    GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA9

   

    //USART1_RX  PA.10

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入

    GPIO_Init(GPIOA, &GPIO_InitStructure);  //初始化PA10

   //USART 初始化设置

USART_InitStructure.USART_BaudRate = bound;//波特率一般设置为9600;

USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式

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; //收发模式

USART_Init(USART1, &USART_InitStructure); //初始化串口


#if EN_USART1_RX  //如果使能了接收 

//注:在usart.h头文件中定义了 

//#define EN_USART1_RX 1 //使能(1),禁止(0)串口1中断接收

   //Usart1 NVIC 配置

    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能

    NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化NVIC寄存器

   

    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启接收中断,接收数据时产生中断

#endif

    USART_Cmd(USART1, ENABLE);                    //使能串口

}

九。串口实验

串口信号是按照从低到高的顺序发送数据

例如发送数据0xAA(1010 1010)



1. 每一位的时长为8.60us,程序设定的波特率为115200,也就是每秒发送115200位,也就是每一位时长为8.68us,跟测量值基本一致。

2. 串口信号发送一开始先发送一位的低电平,然后是数据,发送顺序是从低到高,0101 0101,也就是数据0xAA。

发送数据0x6A (0110  1010),示波器波形



十。串口实验讲解

1. 在uart.h中定义了一个接收缓冲区

extern u8  USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 

extern 的作用:告诉你这个变量是在外部定义的,在这里只是一个声明,其他文件如果包含了uart.h这个头文件后就可以使用这个变量。

在uart.c中定义了这个接收缓冲区

u8  USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符

2.串口的初始化

void uart_init(u32 bound){

  //GPIO端口设置

  GPIO_InitTypeDef GPIO_InitStructure;

  USART_InitTypeDef USART_InitStructure;

  NVIC_InitTypeDef NVIC_InitStructure;

 

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能      USART1,GPIOA时钟

  

   //USART1_TX   GPIOA.9

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出

  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9

   

  //USART1_RX  GPIOA.10初始化

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入

  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  

  //Usart1 NVIC 配置

  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能

NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器

  

       //USART 初始化设置

USART_InitStructure.USART_BaudRate = bound;//串口波特率

USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式

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; //收发模式

  USART_Init(USART1, &USART_InitStructure); //初始化串口1

  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断

  USART_Cmd(USART1, ENABLE);                    //使能串口1 

}

3. 串口中断函数

void USART1_IRQHandler(void)                 //串口1中断服务程序

{

    u8 Res;

     if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d                0x0a结尾)

    {

       Res =USART_ReceiveData(USART1); //读取接收到的数据

       if((USART_RX_STA&0x8000)==0)//接收未完成

      {

          if(USART_RX_STA&0x4000)//判断上次是否接收到了0x0d

          {

              if(Res!=0x0a)USART_RX_STA=0;//如果上次已经接收到了0x0d,但这次接收的数据不是0x0a,则接收               错误,重新开始

             else USART_RX_STA|=0x8000; //接收完成了 

          }

          else //如果上次没有接收到0X0D

         {

                 if(Res==0x0d)USART_RX_STA|=0x4000;  //判断这次接收的数据是不是0x0d,如果是的话,把                         bit14位置1

                 else         //如果这次接收的数据不是0x0d

                 {

                       USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ; //把数据存取接收缓冲区

                       USART_RX_STA++;  //接收的数据个数+1

                        if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新                                           开始接收  

                  }  

           }

      }    

   } 

在中断服务函数中定了一个小的协议


接收到的数据储存在USART_RX_BUF的数组中,这个数组最多接收200个字节。

接收的数据必须以 0x0D 和 0x0A 为结尾。



每接收到一个数据都在USART_RX_STA中的bit10-0把数据的个数+1

每接收到一个数据都要判断是不是接收到了0x0D, 接收到0x0D时不会把数据个数+1,如果接收到了要把bit14设置为1,然后判断下一个数据是不是0x0A,如果是的话就把结束标识符bit15也设置为1.如果不是0x0A就要重新开始接收。

比如接收到的数据有50个,这50个数据存放在接收缓冲区数组中,在bit13-0中的数据为50,为数据长度,bit14为1,bit15为1.




4. 主程序

不停的分析USART_RX_STA这个变量的最高位,如果最高位为1,表示已经完成了一次接收,知道接收到的数据有50个,然后把数据接收缓冲区中的前50个数据发送出去。也就是发送了什么数据就返回什么数据。同时在发送完以后把这些标志位全部清零,以便下一次接续接收数据。

while(1)

{

     if(USART_RX_STA&0x8000)  //判断最高位是不是1,是1的话表示接收完成

     {   

         len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度

         printf("\r\n您发送的消息为:\r\n\r\n");  //打印一串数据到串口

         for(t=0;t《len;t++)

        {

              USART_SendData(USART1, USART_RX_BUF[t]);//向串口1发送数据

              while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束

        }

        printf("\r\n\r\n");//插入换行

        USART_RX_STA=0;  // 处理完成后把USART_RX_STA清零,以便于下次接收

     }else

    {

         times++;

         if(timesP00==0)

        {

             printf("\r\n战舰STM32开发板 串口实验\r\n");  // '\r'是回车,'\n'是换行

             printf("正点原子@ALIENTEK\r\n\r\n");

        }

        if(times 0==0)printf("请输入数据,以回车键结束\n");  

        if(times0==0)LED0=!LED0;//闪烁LED,提示系统正在运行.

         delay_ms(10);   

    }

}  

5. printf函数

发送数据到串口1,是因为在uart.c中定义了以下代码

#if 1

#pragma import(__use_no_semihosting)             

//标准库需要的支持函数                 

struct __FILE 

int handle; 

}; 

FILE __stdout;       

//定义_sys_exit()以避免使用半主机模式    

_sys_exit(int x) 

x = x; 

//重定义fputc函数 

int fputc(int ch, FILE *f)

{      

    while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   

    USART1->DR = (u8) ch;      

    return ch;

}

#endif 

对这部分代码不需要理解,如果把printf函数改为串口2,只需要重新定义fputc函数,里面改成串口2就可以了。


printf函数的格式


1 一般格式

   printf(格式控制,输出表列)

   例如:printf("i=%d,ch=%c\n",i,ch);

   说明:

   (1)“格式控制”是用双撇号括起来的字符串,也称“转换控制字符串”,它包括两种信息:

         ①格式说明:由“%”和格式字符组成,它的作用是将输出的数据转换为指定的格式输出。

         ②普通字符,即需要原样输出的字符。

   (2)“输出表列”是需要输出的一些数据,可以是表达式

   (3) printf函数的一般形式可以表示为

         printf(参数1,参数2,……,参数n)

         功能是将参数2~参数n按参数1给定的格式输出

2 格式字符(9种)

    (1)d(或i)格式符。用来输出十进制整数,有以下几种用法:

        ①%d,按整型数据的实际长度输出。

        ②%md,m为指定的输出字段的宽度。如果数据的位数小于m,则左端补以空格,若大于m,则按实际位数输出。

        ③%ld(%mld 也可),输出长整型数据。

         例如:long a=123456;

               printf("%ld",a);

 (2)o格式符,以八进制数形式输出整数。格式:%o,%mo,%lo,%mlo都可。

     (3)x(或X)格式符,以十六进制数形式输出整数。格式:%x,%mx,%lx,%mlx都可。

     (4)u格式符,用来输出unsigned型数据,即无符号数,以十进制数形式输出。格式:%u,%mu,%lu都可。

       参见:li4-3.c

     (5)c格式符,用来输出一个字符。格式:%c,%mc都可。

     (6)s格式符,用来输出一个字符串。格式:%s,%ms,%-ms,%m.ns,%-m.ns都可。

     (7)f格式符,用来输出实数(包括单、双精度),以小数形式输出。格式:%f,%m.nf,%-m.nf都可。

       注意:单精度实数的有效位数一般为7位,双精度为16位。

     (8)e(或E)格式符,以指数形式输出实数。格式:%e,%m.ne,%-m.ne都可。

     (9)g(或G)格式符,用来输出实数,它根据数值的大小,自动选f格式或e格式(选择输出时占宽度较小的一种)。

3 说明

(1)除了X、E、G(用大写字母表示)外,其他格式字符必须用小写字母;

(2)“格式控制”字符串内可以包含转义字符;

(3)如果想输出字符“%”,则应该在“格式控制”字符串中用连续两个%表示,如:

     printf("%f%%",1.0/3);

(4)格式字符表参见下表

                                             表1 printf格式字符

格式字符

说     明

d,i

以带符号的十进制形式输出整数(正数不输出符号)

o

以八进制无符号形式输出整数(不输出前导符0)

x,X

以十六进制无符号形式输出整数(不输出前导符0x),用x则输出十六进制数的a~f时以小写形式输出,用X时,则以大写字母输出

u

以无符号十进制形式输出整数

c

以字符形式输出,只输出一个字符

s

输出字符串

f

以小数形式输出单、双精度数,隐含输出6位小数

e,E

以指数形式输出实数

g,G

选用%f或%e格式中输出宽度较短的一种格式,不输出无意义的0

表2   printf的附加格式说明字符

  





『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
C8051F020的UART