STM32 IO模拟实现软件串口

2020-06-30来源: eefocus关键字:STM32  IO模拟  软件串口

最近项目中STM32的串口资源紧张,于是使用IO口进行模拟串口,现进行整理记录。


实现思路

IO口模拟串口的思路也比较简单,一切按照串口协议进行操作即可。

对于发送,计算好不同波特率对应的延时时间进行数据发送。


对于接收,稍微复杂。通过外部中断检测接收管脚的下降沿,检测到起始信号后开启定时器,定时器按照波特率设定好时间,每隔一段时间进入定时器中断接收数据,完成一个字节后关闭定时器。


测试Demo说明

TXD : PC13

RXD : PB14

波特率:9600 ,1-8-N


Demo功能

接收11个数据,然后把接收到的数据发送出去。


程序实现


#define OI_TXD PCout(13)

#define OI_RXD PBin(14)


#define BuadRate_9600 100


u8 len = 0; //接收计数

u8 USART_buf[11];  //接收缓冲区


enum{

COM_START_BIT,

COM_D0_BIT,

COM_D1_BIT,

COM_D2_BIT,

COM_D3_BIT,

COM_D4_BIT,

COM_D5_BIT,

COM_D6_BIT,

COM_D7_BIT,

COM_STOP_BIT,

};


u8 recvStat = COM_STOP_BIT;

u8 recvData = 0;


void IO_TXD(u8 Data)

{

u8 i = 0;

OI_TXD = 0;  

delay_us(BuadRate_9600);

for(i = 0; i < 8; i++)

{

if(Data&0x01)

OI_TXD = 1;  

else

OI_TXD = 0;

delay_us(BuadRate_9600);

Data = Data>>1;

}

OI_TXD = 1;

delay_us(BuadRate_9600);

}

void USART_Send(u8 *buf, u8 len)

{

u8 t;

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

{

IO_TXD(buf[t]);

}

}

 void IOConfig(void)

 {

GPIO_InitTypeDef  GPIO_InitStructure;

NVIC_InitTypeDef NVIC_InitStructure;

  EXTI_InitTypeDef EXTI_InitStruct;

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC, ENABLE); //使能PB,PC端口时钟 

 

//SoftWare Serial TXD

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;     

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz  

  GPIO_Init(GPIOC, &GPIO_InitStructure);  

  GPIO_SetBits(GPIOC,GPIO_Pin_13);

 

 

//SoftWare Serial RXD

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOB, &GPIO_InitStructure);  


GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);

EXTI_InitStruct.EXTI_Line = EXTI_Line14;

EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;

EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling; //下降沿触发中断

EXTI_InitStruct.EXTI_LineCmd=ENABLE;

EXTI_Init(&EXTI_InitStruct);



NVIC_InitStructure.NVIC_IRQChannel= EXTI15_10_IRQn ; 

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2; 

NVIC_InitStructure.NVIC_IRQChannelSubPriority =2;  

NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;  

NVIC_Init(&NVIC_InitStructure);  

}

 

void TIM4_Int_Init(u16 arr,u16 psc)

{

  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

NVIC_InitTypeDef NVIC_InitStructure;


RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //时钟使能

//定时器TIM4初始化

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(TIM4, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位

TIM_ClearITPendingBit(TIM4, TIM_FLAG_Update);

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


//中断优先级NVIC设置

NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;  //TIM4中断

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  //先占优先级1级

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;  //从优先级1级

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

NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器  

}

 

 

 int main(void)

 {

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级

delay_init();

IOConfig();

  TIM4_Int_Init(107, 71); //1M计数频率

 

  while(1)

{

if(len > 10)

{

len = 0;

USART_Send(USART_buf,11);

}

}

}


void EXTI15_10_IRQHandler(void)

{

if(EXTI_GetFlagStatus(EXTI_Line14) != RESET)

{

if(OI_RXD == 0) 

{

if(recvStat == COM_STOP_BIT)

{

recvStat = COM_START_BIT;

TIM_Cmd(TIM4, ENABLE);

}

}

EXTI_ClearITPendingBit(EXTI_Line14);

}

}


void TIM4_IRQHandler(void)

{  

if(TIM_GetFlagStatus(TIM4, TIM_FLAG_Update) != RESET)

{

TIM_ClearITPendingBit(TIM4, TIM_FLAG_Update);

recvStat++;

if(recvStat == COM_STOP_BIT)

{

TIM_Cmd(TIM4, DISABLE);

USART_buf[len++] = recvData;

return;

}

if(OI_RXD)

{

recvData |= (1 << (recvStat - 1));

}else{

recvData &= ~(1 << (recvStat - 1));

}

  }

}


Demo下载

Demo工程下载地址github。

https://github.com/TonyIOT/SoftWareSerial.git

关键字:STM32  IO模拟  软件串口 编辑:什么鱼 引用地址:http://news.eeworld.com.cn/mcu/ic501619.html 本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:关于STM32 利用IO口模拟串口实现数据通信
下一篇:stm32 浮点数问题

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

关于STM32CubeMx printf重定向,及报错。"FILE" is undefined
PFP *//* USER CODE BEGIN 0 */PUTCHAR_PROTOTYPE{    HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);    return ch;}/* USER CODE END 0 */ 这样写会报错"FILE" is undefined  添加头文件 stdio.h即可
发表于 2020-06-06
【STM32】keil MDK下重定向printf到串口(基于STM32CubeMX)
概述在keil MDK环境下重定向printf与keil C51不同,由于本人使用了STM32CubeMX生成工程模板,HAL_USART_Transmit函数即是模板里串口输出的函数。由于printf最终是调用fputc输出数据,fputc是一个弱引用(weak)函数,覆写即可重定向printf。代码清单extern USART_HandleTypeDef husart1;int fputc(int ch, FILE *f) {    HAL_USART_Transmit(&husart1, (uint8_t *)&ch, 1, 0xFFFF);    return ch
发表于 2020-06-06
STM32CubeMx启动串口调试功能Printf调试
## 概述项目中往往需要调试信息,调试stm32的时候,需要标准库里面的printf函数。在keil MDK环境下重定向printf与keil C51不同,由于本人使用了STM32CubeMX生成工程模板,HAL_USART_Transmit函数即是模板里串口输出的函数。由于printf最终是调用fputc输出数据,fputc是一个弱引用(weak)函数,覆写即可重定向printf。代码清单/* USER CODE BEGIN Includes */#include "FreeRTOS.h"#include "task.h"#include "queue.h"
发表于 2020-06-06
STM32CubeMx启动串口调试功能Printf调试
STM32F1xx HAL库中文版——USART篇
38.1 UART Firmware driver registers structures //串口固件驱动寄存器结构38.1.1 UART_InitTypeDefUART_InitTypeDef被定义在stm32f1xx_hal_uart.h头文件中数据字段:• uint32_t BaudRate 波特率• uint32_t WordLength 字长• uint32_t StopBits 停止位• uint32_t Parity 奇偶校验位• uint32_t Mode 模式• uint32_t HwFlowCtl 硬件流控制• uint32_t OverSampling 过采样字段的文档:• uint32
发表于 2020-06-06
Stm32-输入捕获
输入捕获模式可以用来测量脉冲宽度或者测量频率。STM32 的定时器,除了 TIM6 和 TIM7,其他定时器都有输入捕获功能。STM32 的输入捕获,简单地说就是通过检测 TIMx_CHx 上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的通道的捕获/比较寄存器(TIMx_CCRx)中。1. 相关寄存器介绍1) 捕获/比较模式寄存器 (TIMx_CCMRx) 当在输入捕获模式下使用的时候,对应上图的第二行描述,从图中可以看出,TIMx_CCMR1 明显是针对 2 个通道的配置,低八位[7:0]用于捕获/比较通道 1 的控制,而高八位[15:8]则用
发表于 2020-06-06
Stm32-输入捕获
STM32库函数和寄存器的区别
库函数版和寄存器版的系统时钟设置的区别:**1.**库函数的目的是让用户应用的,而寄存器更加原始库函数的系统时钟是默认设置的,且放在启动文件里。而寄存器版的系统时钟是Stm32_Clock_Init(336,8,2,7);.**2.**库函数的快捷的,但不是每个芯片都有的;寄存器是复杂的,但是每个芯片厂商都有提供系统的寄存器设置信息。分别打开库函数和寄存器版的I/O口设置:库函数:RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);gotoh后先通过assert_param();函数检查格式是否正确同时只要是ENABLE,RCC->AHB1ENR
发表于 2020-06-06
STM32库函数和寄存器的区别
小广播
何立民专栏 单片机及嵌入式宝典

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

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