STM32—串口使用总结

发布者:JoyfulSpirit5最新更新时间:2021-08-06 来源: eefocus关键字:STM32  串口  使用总结 手机看文章 扫描二维码
随时随地手机看文章

一.仅向上位机打印调试信息

单纯利用串口向上位机打印调试信息,程序如下:


void USART1_Init( uint32_t btl )

{

GPIO_InitTypeDef  GPIO_InitStruct;

USART_InitTypeDef USART_InitStruct;

RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1, ENABLE );

GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;

GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;//Tx

GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init( GPIOA, &GPIO_InitStruct );

GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;//Rx

GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;

GPIO_Init( GPIOA, &GPIO_InitStruct );

USART_InitStruct.USART_BaudRate = btl;

USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

USART_InitStruct.USART_Mode = USART_Mode_Tx;

USART_InitStruct.USART_Parity = USART_Parity_No;

USART_InitStruct.USART_StopBits = USART_StopBits_1;

USART_InitStruct.USART_WordLength = USART_WordLength_8b;

USART_Init( USART1, &USART_InitStruct );

USART_Cmd( USART1, ENABLE );

}


//串口重定向,直接利用printf函数输出调试信息

int fputc( int ch, FILE *f )

{

USART_SendData( USART1, ( uint8_t )ch );

while( USART_GetFlagStatus( USART1, USART_FLAG_TXE )!=SET );

return ch;

}


记得包含头文件,勾选Use MicroLIB,以使用printf等函数

在这里插入图片描述

二.与上位机交互信息

相比于上面的单向通信,有时候需要从上位机接收信息,然后进行反馈,这个时候就使用到串口的中断了。

上位机向单片机发送字符串,接收后再发送给上位机:



void USART1_Init( uint32_t btl )

{

/* 结构体声明 */

GPIO_InitTypeDef  GPIO_InitStruct;

USART_InitTypeDef USART_InitStruct;

NVIC_InitTypeDef NVIC_InitStruct;

/* 打开时钟 */

RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );

RCC_APB2PeriphClockCmd( RCC_APB2Periph_USART1, ENABLE );

/* GPIO配置 */

GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;

GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;//Tx

GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init( GPIOA, &GPIO_InitStruct );

GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;//Rx

GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;

GPIO_Init( GPIOA, &GPIO_InitStruct );

/* 串口配置 */

USART_InitStruct.USART_BaudRate = btl;

USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

USART_InitStruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;//收发模式

USART_InitStruct.USART_Parity = USART_Parity_No;

USART_InitStruct.USART_StopBits = USART_StopBits_1;

USART_InitStruct.USART_WordLength = USART_WordLength_8b;

USART_Init( USART1, &USART_InitStruct );

/* 中断配置 */

NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;

NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=3 ;

NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;

NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStruct);

USART_ITConfig( USART1, USART_IT_RXNE, ENABLE );//接收寄存器非空触发中断

/* 使能串口 */

USART_Cmd( USART1, ENABLE );

}



volatile uint8_t n=0;

//接收缓冲区

uint8_t USART1_Rx_Buf[100];

void USART1_IRQHandler( void )

{

if( USART_GetITStatus( USART1, USART_IT_RXNE )==SET )

{

USART1_Rx_Buf[n]=USART_ReceiveData( USART1 );

n++;

}

USART_ClearFlag( USART1, USART_IT_RXNE );

}


每从上位机中接收一字节的数据,都将数据存储在串口的接收缓冲区USART1_Rx_Buf[100]中。


三.作为驱动接口

一些模块的驱动接口就是串口,这个时候就需要单片机从模块中读取指定格式的数据,比如GPS模块,将定位信息从串口发出,单片机解析串口数据,显示在上位机中。

usart3用来与GPS模块通信,从GPS模块接收数据,认为10ms内的数据属于一次数据,所以就需要定时器来控制时间。

usart3:


//串口1中断服务程序

//注意,读取USARTx->SR能避免莫名其妙的错误   

u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.

//接收状态

//bit15, 接收完成标志

//bit14, 接收到0x0d

//bit13~0, 接收到的有效字节数目

u16 USART_RX_STA=0;       //接收状态标记   

  

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 


}


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;//接收错误,重新开始

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

}

else //还没收到0X0D

{

if(Res==0x0d)USART_RX_STA|=0x4000;

else

{

USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;

USART_RX_STA++;

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

}  

}

}     

     } 


time7:



extern vu16 USART3_RX_STA;


//定时器7中断服务程序     

void TIM7_IRQHandler(void)

{

if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET)//是更新中断

{    

USART3_RX_STA|=1<<15; //标记接收完成

TIM_ClearITPendingBit(TIM7, TIM_IT_Update  );  //清除TIM7更新中断标志    

TIM_Cmd(TIM7, DISABLE);  //关闭TIM7 

}     

}

 

//通用定时器7中断初始化

//这里时钟选择为APB1的2倍,而APB1为42M

//arr:自动重装值。

//psc:时钟预分频数

//定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.

//Ft=定时器工作频率,单位:Mhz 

//通用定时器中断初始化

//这里始终选择为APB1的2倍,而APB1为36M

//arr:自动重装值。

//psc:时钟预分频数  

void TIM7_Int_Init(u16 arr,u16 psc)

{

NVIC_InitTypeDef NVIC_InitStructure;

TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;


RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);//TIM7时钟使能    

//定时器TIM7初始化

TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值

TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值

TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式

TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位

 

TIM_ITConfig(TIM7,TIM_IT_Update,ENABLE ); //使能指定的TIM7中断,允许更新中断

TIM_Cmd(TIM7,ENABLE);//开启定时器7

NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn;

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

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

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

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

}


四.结合DMA接收数据帧

当单片机从串口上接收数据时,一般我们都是接收一个字节数据,进入一次中断,在中断中处理数据或者做标记,这种方法虽然简单,但是对于大量数据的情况,频繁地进入中断处理数据就占有了CPU的宝贵资源。


这个时候我们可以使用DMA来接收数据,DMA接收数据的好处就是省CPU资源,DMA仅仅在初始化的时候占用一下CPU资源,其他操作都是在DMA的控制器来完成的。


使用背景:单片机从USART2中接收传感器传回的数据帧(12字节),传感器每秒上报一次数据,要求单片机可以收到完整数据并且可以对数据进行处理。


程序框架:以往串口接收数据都是判断数据寄存器非空的,一字节一字节地接收,现在使用DMA,使每一次数据寄存器的值都自动传给内存指定地址(也就是指定的数据缓冲区),当串口接收完一帧数据后,会触发空闲中断,这意味着一帧数据的接收完成,我们只需要在数据缓冲区中对数据进行处理即可。


代码:


uint8_t USART2_Rx_Buf[12];


void USART2_Config( void )

{

/* 声明各结构体 */

GPIO_InitTypeDef GPIO_InitStruct;

USART_InitTypeDef USART_InitStruct;

NVIC_InitTypeDef NVIC_InitStruct;

DMA_InitTypeDef DMA_InitStruct;

/* 打开时钟 */

RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );

RCC_APB1PeriphClockCmd( RCC_APB1Periph_USART2, ENABLE );

RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE );

/* GPIO配置 */

GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;

GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;                 //USART2_Tx:PA2

GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init( GPIOA, &GPIO_InitStruct );

GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;

GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;                //USART2_Rx:PA3

GPIO_Init( GPIOA, &GPIO_InitStruct );

/* 串口配置 */

USART_DeInit( USART2 );

USART_InitStruct.USART_BaudRate = 9600;                //波特率:9600

USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

USART_InitStruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;

USART_InitStruct.USART_Parity = USART_Parity_No;

USART_InitStruct.USART_StopBits = USART_StopBits_1;

USART_InitStruct.USART_WordLength = USART_WordLength_8b;

USART_Init( USART2, &USART_InitStruct);

/* 中断配置 */

NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2 );

NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn;

NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;

NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;

NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;

NVIC_Init( &NVIC_InitStruct );

USART_ITConfig( USART2, USART_IT_IDLE, ENABLE );      //空闲中断

USART_DMACmd( USART2, USART_DMAReq_Rx, ENABLE );   //开启DMA接收

/* 配置DMA传输数据 USART2对应DMA1的通道6 方向:外设到存储器*/

    DMA_InitStruct.DMA_PeripheralBaseAddr = USART2_BASE+0x04;              // 设置DMA源地址:串口数据寄存器地址*/

DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)USART2_Rx_Buf;           // 内存地址(要传输的变量的指针)

DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;                        // 方向:从外设到内存

DMA_InitStruct.DMA_BufferSize = 12;                                    // 传输大小 一帧数据12字节     

[1] [2]
关键字:STM32  串口  使用总结 引用地址:STM32—串口使用总结

上一篇:STM32—PID控制在直流电机中的应用
下一篇:STM32—TIMx实现编码器四倍频

推荐阅读最新更新时间:2024-11-12 09:29

stm32变量的定义
一、最近在玩stm32,用库(V3.5.0)开发,被 stm32的变量定义搞的晕头转向,决心将其弄清楚。 在 stdint.h 文件里,我们可以清楚的看到: typedef signed char int8_t; typedef signed short int int16_t; typedef signed int int32_t; typedef signed __int64 int64_t; typedef unsigned char uint8_t; typedef unsigned short int uint16_t; typedef unsigned
[单片机]
<font color='red'>stm32</font>变量的定义
stm32 boot0 boot1的启动方式
STM32 三种启动模式对应的存储介质君是芯片内置的,它们是: 1.用户闪存 =芯片内置的Flash 2.SRAM=芯片内置的RAM区,就是内存了。 3.系统存储器=芯片内部一块特定的区域,芯片出厂时在这个区域预置了一段bootloader,就是同事的ISP升级程序,这个区域的内容在芯片出现后没偶人能够修改或拆除,即它是一个ROM; 在每个STM32 的芯片上都有两个管脚BOOT0和BOOT1,这两个管脚在芯片复位时电平状态决定了芯片复位后从哪个区域开始执行程序,BOOT1=X BOOT0=0 从用户闪存(flash)启动,这时正常模式 较多情况下使用这种模式BOOT1=1 BOOT0=1 从内置SRAM(内存)启动,这种模
[单片机]
<font color='red'>stm32</font> boot0 boot1的启动方式
基于机智云物联网平台的空气质量多功能检测系统设计
摘要: 目前市面上的空气质量检测仪通常只能检测少数几种气体,面对空气中多种有害气体,就要使用不同的检测仪器。针对现有检测系统功能不全等问题,开发了由STM32单片机、各类传感器、LCD显示屏和WiFi通信模块组成的多功能检测系统,并利用机智云物联网平台传输数据到用户终端。该检测系统能够实现对空气中的CO、CO2、PM2.5、TVOC、甲醛的含量以及温湿度进行实时监测,并具有超标报警功能。 1引言 空气中的有害物质直接影响人们的心身健康,室内空气的危害气体主要有CO、CO2、PM2.5、甲醛等 。温湿度对人的舒适度有较大影响,低湿度环境对人有多种不利影响 。因此,对空气中的有害气体及环境的温湿度进行检测是必要的,对人员密集、
[单片机]
基于机智云物联网平台的空气质量多功能检测系统设计
STM32之GPIO及第一个STM32程序(跑马灯)
今天来说一说,GPIO,对于我这个新手来说,GPIO就好比我在学习开车之前得学会如何开门一样,由此可以看出这对于我学习STM32 的重要性,好废话不多说,先总结一下STM32F103ZE的开发板里总共有7组IO口,每组IO口有16个IO,即这块板子总共有112个IO口分别是GPIOA~GPIOG。 GPIO的工作模式主要有八种:4种输入方式,4种输出方式,分别为输入浮空,输入上拉,输入下拉,模拟输入;输出方式为开漏输出,开漏复用输出,推挽输出,推挽复用输出。对应的为: (1)GPIO_Mode_AIN 模拟输入 (2)GPIO_Mode_IN_FLOATING 浮空输入 (3)GPIO_Mode_IPD 下拉输入 (4)GPIO_
[单片机]
stm32串口发送数据,丢失字节问题分析
STM32 串口 发送 必须 先检测 状态,否则 第一个 字节 无法 发出,发送完毕,必须检测发送状态是否完成,否则,发送不成功, 使用stm32f10x调试串口通讯时,发现一个出错的现象,硬件复位重启之后,发送测试数据0x01 0x02 0x03 0x04..接收端收到的数据为:0x02 0x03 0x04,第一个数据丢失。换成发送别的数值的数据,如0x06 0x0ff,则接收到0x0ff,0x06丢失。错误依旧。 故障排除过程: 1、刚开始怀疑是接收端的错误,我是使用电脑串口,运行串口辅助调试工具接收,换成其他软件后,发现故障依旧,而且电脑软件一直是开启状态,不像和电脑软件有关。 2、使用单步调试,单步运行各个发送指令,都正常
[单片机]
stm32库中地址映射
一、预备知识 在编写ARM9裸机的程序时,读写某个寄存器可用如下代码实现: 例如,要读写UART_ULCON1寄存器的值,查找ARM9的用户手册就可已得到该寄存器地址。 #define UART_ULCON1 (volatile unsigned int *)(0x50004000) 写寄存器: *UART_ULCON1 = 0X00FF; 读寄存器: unsigned int temp; temp = *UART_ULCON1; 二、stm32库中地址映射 在stm32用户手册中找不到绝对的寄存器地址,需要进行换算。例如要找GPIOA中GPIOA_CRL寄存器地址: 第一步:在stm32f10x_reference文档中可查找到
[单片机]
STM32 USART串口初始化
1、初始化串口时钟以及串口IO端口时钟: 使用RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState); 备注:为什么要使能IO口时钟? 参照用户手册 P165-P166 使用串口这个外设的时候需要将串口的IO属性配置为图示; 使用串口是否需要打开复用时钟,请见http://blog.csdn.net/u012411027/article/details/44217313,看到这位的解释才恍然大悟的。 2、串口参数设置(波特率、数据长度、停止位、校验位、发送接收模式、硬件控制流) 具体初始化及初始化方式如图:
[单片机]
STM32 DMA彻底研究
typedef struct { u32 DMA_PeripheralBaseAddr; u32 DMA_MemoryBaseAddr; u32 DMA_DIR; u32 DMA_BufferSize; u32 DMA_PeripheralInc; u32 DMA_MemoryInc; u32 DMA_PeripheralDataSize; u32 DMA_MemoryDataSize; u32 DMA_Mode; u32 DMA_Priority; u32 DMA_M2M; } DMA_InitTypeDef; DMA_InitTypeDef 定义于文件“stm32f10x_dma.h” DMA_PeripheralBaseA
[单片机]
热门资源推荐
热门放大器推荐
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
  • 学习ARM开发(16)
    ARM有很多东西要学习,那么中断,就肯定是需要学习的东西。自从CPU引入中断以来,才真正地进入多任务系统工作,并且大大提高了工作效率。采 ...
  • 学习ARM开发(17)
    因为嵌入式系统里全部要使用中断的,那么我的S3C44B0怎么样中断流程呢?那我就需要了解整个流程了。要深入了解,最好的方法,就是去写程序 ...
  • 学习ARM开发(18)
    上一次已经了解ARM的中断处理过程,并且可以设置中断函数,那么它这样就可以工作了吗?答案是否定的。因为S3C44B0还有好几个寄存器是控制中 ...
  • 嵌入式系统调试仿真工具
    嵌入式硬件系统设计出来后就要进行调试,不管是硬件调试还是软件调试或者程序固化,都需要用到调试仿真工具。 随着处理器新品种、新 ...
  • 最近困扰在心中的一个小疑问终于解惑了~~
    最近在驱动方面一直在概念上不能很好的理解 有时候结合别人写的一点usb的例子能有点感觉,但是因为arm体系里面没有像单片机那样直接讲解引脚 ...
  • 学习ARM开发(1)
  • 学习ARM开发(2)
  • 学习ARM开发(4)
  • 学习ARM开发(6)
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved