STM32串口环形缓冲区

发布者:影子猎人最新更新时间:2019-04-09 来源: eefocus关键字:STM32  串口  环形缓冲区 手机看文章 扫描二维码
随时随地手机看文章

1:概述


1.1:本篇实现串口驱动,实现printf函数的重定向,实现串口的中断接受和发送,效仿modbus协议中的3.5T超时机制,判断是否接受完毕;


1.2:如果串口仅仅是实现一个控制台,打印一些debug数据,使用printf函数(串口发送数据忙等待),如果是需要用串口进行外设设备的控制,比如串口GPRS模块,需使用串口中断进行控制,因为受限于串口的传输速率,如果使用忙等待发送数据,会阻塞主程序中的其它任务;


1.3:使用sysclk作为超时定时器,sysclk的中断优先级需高于串口中断;


1.4:除过使用3.5T超时时间判断接受数据是否完成外,还可使用ASCLL码的形式,利用字符操作库函数,自定义串口通信协议;


1.5:开发板:stm32f103zert   软件环境:KEIL MKD5


1.6:115200 波特率,间隔1ms发送字符串 "PassWord",主程序将接收到"PassWord"后返回"123456rn",测试主程序响应迅速,不丢帧;


2:代码


main.c



#include "stm32f10x.h"

#include "usart_init.h"

#include "timer.h"

#include "Sys_Driver.h"

 

unsigned int baud[5] = {9600,14400,19200,56000,115200};

unsigned int T_35 = 0;     //串口发送3.5个字节的时间,由波特率得出,单位为ms

 

int main(void)

{

T_35 = 3.5*(10000000/baud[4]);     

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);     //设置中断优先级分组

Usart_Init(baud[4]);  //串口初始化 

SysTick_Init(INT_1US,SysTick_CLKSource_HCLK_Div8);  //sysclk初始化,设置1ms中断

timer_set(&usart_timer,T_35);   //定义一个串口计时器 



while(1)

{

if(Start_Receive_Flag == 1)

{

if(timer_expired(&usart_timer) == 1)   //3.5T时间到达,一条串口数据接受成功

{

Start_Receive_Flag = 0;

Usart_Handle_Func();

}

}

}

}


usart_init.c



#include "usart_init.h"

 

 

u8 Usart_Receive_Ok = 0;

u8 Start_Receive_Flag = 0;

 

u8 Usart_TX_Buff[TX_RE_BUFF_LENGTH];

u8 Usart_RX_Buff[TX_RE_BUFF_LENGTH];

COMx_Define CYCLE;

 

 

/*******************printf()函数重定向***************/

//发送函数

int fputc(int ch, FILE *f)

{

USART_SendData(USART2, (unsigned char) ch);

while (!(USART2->SR & USART_FLAG_TXE));

return (ch);

}

 

//接受函数

int fgetc(FILE *f)  

{

while (!(USART2->SR & USART_FLAG_RXNE));

return USART_ReceiveData(USART2);

}

 

 

void Usart_Init(u32 baud)

{

GPIO_InitTypeDef GPIO_InitStructure;

USART_InitTypeDef USART_InitStructure;

NVIC_InitTypeDef NVIC_InitStructure;

 

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);   //使能USART2,GPIOA时钟

RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); //使能USART2,GPIOA时钟


//USART2_TX   PA.2

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; 

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

GPIO_Init(GPIOA, &GPIO_InitStructure); 

   

  //USART2_RX   PA.3

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;

GPIO_Init(GPIOA, &GPIO_InitStructure); 

 

  //USART2 NVIC 配置

  NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;

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

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

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

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

  

  //USART 初始化设置

USART_InitStructure.USART_BaudRate = baud;        

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(USART2, &USART_InitStructure);                   //初始化串口

USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);  //开启发送完成中断和接收完成中断  

USART_ITConfig(USART2,USART_IT_TC,ENABLE);  //开启发送完成中断和接收完成中断

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

}

 

void Usart_Send_Byte(u8 data)

{

Usart_TX_Buff[CYCLE.TX_write] = data;      //装发送缓冲区

if(++CYCLE.TX_write == TX_RE_BUFF_LENGTH)

CYCLE.TX_write = 0;


if(CYCLE.TX_busy == 0)                     //发送空闲

{

CYCLE.TX_busy = 1;

USART_SendData(USART2,Usart_TX_Buff[CYCLE.TX_read]);  //想串口发送数据,触发中断

if(++CYCLE.TX_read == TX_RE_BUFF_LENGTH)

CYCLE.TX_read = 0;

}

}

 

/*串口发送函数*/

void Usart_Send_Data(u8 *ptr,u8 num)

{

if(num == 0)                                   //发送字符串

{

for(;*ptr!='';ptr++)

{

Usart_Send_Byte(*ptr);

}

}

else                                           //发送num字节数据

{

for(;num>0;num--)

{

Usart_Send_Byte(*ptr++);

}

}

}

 

/*串口接收函数,当接收到可用的数据帧时,对接收到的数据进行处理,main函数中调用*/

void Usart_Handle_Func(void)

{

u8 i = 0;

u8 Buff[40];

while(CYCLE.RX_read != CYCLE.RX_write)

{

Buff[i++] = Usart_RX_Buff[CYCLE.RX_read];

if(++CYCLE.RX_read == TX_RE_BUFF_LENGTH)

CYCLE.RX_read = 0;

}

Buff[i] = '';

// Usart_Send_Data(Buff,0);

// Usart_Send_Data("rn",0);

if(memcmp(Buff,"PassWord",8) == 0)

{

Usart_Send_Data("123456rn",0);

}

}

 

///*中断处理函数*/

void USART2_IRQHandler(void)

{

u8 data;

if(USART_GetITStatus(USART2,USART_IT_TC) == SET)    //发送完成中断

{

USART_ClearITPendingBit(USART2,USART_IT_TC);

if(CYCLE.TX_read != CYCLE.TX_write)

{

USART_SendData(USART2,Usart_TX_Buff[CYCLE.TX_read]);

if(++CYCLE.TX_read == TX_RE_BUFF_LENGTH)

CYCLE.TX_read = 0;

}

else

{

CYCLE.TX_busy = 0;                                 //缓冲区数据发送完成,串口总线空闲,可以开始发送新的数据

}

}

else

if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)  //接收完成中断

{

USART_ClearITPendingBit(USART2,USART_IT_RXNE);

data = USART_ReceiveData(USART2);

Usart_RX_Buff[CYCLE.RX_write] = data;

if(++CYCLE.RX_write == TX_RE_BUFF_LENGTH)

CYCLE.RX_write = 0;

timer_reset(&usart_timer);   //开始计时

Start_Receive_Flag = 1;

}

}

关键字:STM32  串口  环形缓冲区 引用地址:STM32串口环形缓冲区

上一篇:stm32软件触发的按键长按与短按区分
下一篇:STM32使用HAL库实现串口通讯——理论原理详细讲解

推荐阅读最新更新时间:2024-03-16 16:27

嵌入式STM32学习笔记(3)——pwm波及呼吸灯
写pwm波函数可以调用stm32固件库函数直接生成,也可以通过中断来写pwm波;下面就介绍这两种方法,这里先说一下呼吸灯,其原理就是让LED灯由暗变亮再由亮变暗循环,类似呼吸的效果,亮-暗是一个大周期,而LED灯亮或暗是由其刷新的占空比决定,高电平时间占比长则亮,反之则暗; stm32 的定时器除了 TIM6 和 7。其他的定时器都可以用来产生 PWM 输出。其中高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出。而通用定时器也能同时产生多达4路的 PWM 输出,这样, STM32 最多可以同时产生 30 路 PWM 输出。关于映射及原理大家可查手册吧,这里不做具体叙述了;个人见解:很多知识用到再仔细
[单片机]
嵌入式<font color='red'>STM32</font>学习笔记(3)——pwm波及呼吸灯
stm32 hal i2c 库读写sd3088时钟
前一版本的修正。 sd3008在每次通信开始0.5s做一次总线复位,不必考虑stm32的i2c Bug问题。 而且HAL库,是不是也应考虑到软件上补充这个Bug? 使用HAL_I2C_Mem_Read/Write,使得代码非常好看。模拟I2C方式,一字长蛇阵模样。 使用Freertos,读取时间、保存数据到用户SRAM,需要考虑Mutex对RTC时钟资源进行保护。 * SD3088时钟芯片 读写 * 文件 sd3088.h * http://git.oschina.net/maizhi/small-pellet-sove-control-system *作者 于 *版本 v1.1 */ #incl
[单片机]
STM32字符转整型处理
/* USER CODE BEGIN Header */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include main.h #include usart.h #include gpio.h /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include stdl
[单片机]
<font color='red'>STM32</font>字符转整型处理
基于51单片机SPI器件的串口控制
0 引 言 串行外设接口(Serial Peripheral Interface,SPI)是一种高速同步串行输入/输出端口,近年来广泛应用于移位寄存器、D/A转换器、A/D转换器、串行E2PROM、LED显示驱动器等外部设备的扩展。SPI接口可以共享,便于组成带多个SPI接口器件的系统。其传送速率可编程,连接线少,具有良好的扩展性。 1 SPI接口介绍 SPI是摩托罗拉公司推出的一种同步串行通信接口,用于微处理器、微控制器和外围扩展芯片之间的串行连接,现已发展成为一种工业标准。目前,各半导体公司推出了大量的带有SPI接口的具有各种各样功能的芯片,如RAM,E2PROM,FLASH ROM,A/D转换器、D/A转换器、LE
[单片机]
基于51单片机SPI器件的<font color='red'>串口</font>控制
STM32学习笔记— SPI通信异常分析
SPI,全称为 Serial Peripheral Interface(串行外设接口),是一种用于短距离通信的同步串行通信接口,主要应用在嵌入式系统。 SPI的应用场合很广,显示模组、时钟芯片、存储芯片、温度传感器等众多器件都有使用SPI接口通信。这些器件通常作为从设备,STM32作为主设备来控制它们。 STM32 SPI基础内容 绝大部分STM32芯片都有多个SPI外设,它可与外部SPI器件进行半双工/全双工同步串行通信。 1. SPI特性 三条线全双工、双线单工同步传输 支持 8 位或 16 位传输帧格式选择 支持主模式或从模式操作 可编程的时钟极性和相位 支持 MSB 或 LSB 数据顺序 支持DMA
[单片机]
<font color='red'>STM32</font>学习笔记— SPI通信异常分析
STM32 YMODEM实现bootloader
这几天一直在尝试学STM32 bootloader,在网上查阅了一番,发现实现方法不计其数。于是自己有了想动手实现一番的欲望。 下面请听我细细道来,我选用的芯片是STM32F103ZE系类,该芯片是512k,每页是2k的。 接下来就是你要重点了: 其实bootloader说白了,就是在原有APP程序 再加上另一段程序---bootloader,这个bootloader可以对你flash进行擦写操作。 那么关于STM32具体实现BOOTLOADER步骤是怎么样的呢? 下面我就具体几个细节之处谈谈: 1,如何实现在APP程序跳转到BOOTLOADER程序. 2,既然APP程序可以跳转到bootloader,那么反之也是可以的。
[单片机]
STM32高级控制定时器1学习
高级控制定时器(Tim1)是由一个16位的自动装载计数器组成,它由一个可编程预分频器驱动。 用途在于:测量输入信号的脉冲宽度(输入捕获),或者产生输出波形(输出比较,PWM,嵌入死区时间的互补PWM等)。 使用定时器预分频器和RCC时钟控制预分频器,可以实现脉冲宽度和波形周期从几个微秒到几个毫秒的调节。 具体如下: 16位上下,自动装载计数器。 16位可编程预分频器,计数器时钟频率的分频率的分频系数为1-65535之间任意数值 4个独立通道: 输入捕获 输出比较 PWM生成 单脉冲模式输出 死区时间可编程的互补输出 使用外部信号控制定时器和定时器互连的同步电路 在指定数目的计数器周期之后更新定时器寄存器
[单片机]
基于STM32实现串口的两个分案解析
首先总结一下串口232,422,485 串口232:可双向传输,全双工,最大速率20Kbps,负逻辑电平,-15V~-3V逻辑“1”,+3V~+15V逻辑“0”。 串口422:可双向传输,4线全双工,2线单工。 串口485:可双向传输,4线全双工,2线单工,最大速率10Mb/s,差分信号,发送端:+2V~+6V逻辑“1”,-2V~-6V逻辑“0”,接收端:+200mV逻辑“1”,-200mV逻辑“0”。 对于串口的实现有以两个方案: 方案一,和原子的《例说STM32》一样,首先接收,然后处理,没有消息验证处理,这样就会出现消息覆盖,消息出错后死机,无法明确区分命令,无法及时应答握手信号。方案二,借鉴uC/OSII的消息队列,进
[单片机]
基于<font color='red'>STM32</font>实现<font color='red'>串口</font>的两个分案解析
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

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

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