stm32 普通IO口模拟串口通信

普通IO口模拟串口通信

  • 串口通信协议  

  1. 串口传输 默认 波特率9600 1起始位 1停止位 其他0 数据位是8位(注意图上的给错了)。
  2. 传输时,从起始位开始,从一个数据的低位(LSB)开始发送,如图从左向右的顺序,对电平拉高或拉低,最后停止位时拉高。
  3. 波特率大小,改变延时时间即可。例如9600 波特率    根据公式 : 1/9600=0.000104s(大致) 也就是说每发送1bit延时104us (下面我用9600波特率来说,代码用的是19200)
  4. 串口发送       将电平拉低 延时104us(视为 起始位 0   传输数据正式开始)  其中数据我发送的是16进制数据(8bit  一字节  例如10001000)  将想要发的数据按照二进制的‘0’‘1’高低电平的方式,每发送1bit 延时104us   直到发送完到终止位 将电平拉高视为一包数据传输结束。(根据需求更改即可)
  5. 串口接收    (稍微麻烦一些) 两种方法:第一种可以用定时中断,每隔104us开启一次定时中断,中断函数内进行高低电平判断,将这些bit存储最后转换成需要的数据。第二种,用外部中断处理函数,外部中断设置同时开启上升沿下降沿,思路:根据上升下降的电平跳变分析。比如说,触发外部中断后检测电平高低,记录一下当前时间,然后再进入外部中断后 计算出总共几个bit   (两个沿跳变之间的时间 =现在记录的时间 — 之前记录的时间        bit=这个时间/104us)  ,知道这个就可以转换数据了。
  6. 定时中断逻辑相对外部中断而言简单好写,但是数据多的时候准确率下降很多,容易丢数据(因为定时中断毕竟用计时开启中断,不可能时间准确每104us开启一次,数据一多时间误差大,自然丢包。可以尝试每发一串数据,重新计时校准一次)。外部中断较为准确,检测的高低电平跳变较为明显唯一,一个跳变就是一个数据,只是分析情况比较多。
  //IO模拟串口初始化 1 void IRrec_Init(){
 2
 3
 4     EXTI_InitTypeDef EXTI_InitStructure;
 5     NVIC_InitTypeDef NVIC_InitStructure;
 6     GPIO_InitTypeDef GPIO_InitStructure;
 7
 8     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE); //使能GPIOC时钟
 9
10
11     //IR TX C9 使能
12     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
13     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
14     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
15     GPIO_Init(GPIOC, &GPIO_InitStructure);
16     GPIO_SetBits(GPIOC, GPIO_Pin_9);//  引脚拉高
17
18
19     //IR RX C8
20     RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
21     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
22     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
23     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
24     GPIO_Init(GPIOC, &GPIO_InitStructure);
25     GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource8);
26
27     EXTI_InitStructure.EXTI_Line=EXTI_Line8;
28     EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
29     EXTI_InitStructure.EXTI_Trigger=
30     EXTI_Trigger_Rising_Falling;
31     EXTI_InitStructure.EXTI_LineCmd=ENABLE;
32     EXTI_Init(&EXTI_InitStructure);
33
34     NVIC_InitStructure.NVIC_IRQChannel=EXTI9_5_IRQn;
35     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
36     NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
37     NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
38     NVIC_Init(&NVIC_InitStructure);
39
40 }         
 1 void IR_SendByte(u8 val)//发送bit位
 2 {
 3     u16 i;
 4
 5     IRSEND=0;//拉低 开始传输
 6     SysTick_Delay_Us(53);//波特率根据延时在设置  19200波特率
 7
 8     for(i=0;i<8;i++)
 9     {
10         if(val&0x1)
11         {
12             IRSEND=1;
13         }
14         else
15         {
16             IRSEND=0;
17         }
18
19         val>>=1;
20         SysTick_Delay_Us(53);
21     }
22
23     IRSEND=1;
24     SysTick_Delay_Us(53);
25 }
26
27
28 void IR_SendStr(u8*st,u16 len){//在这填入16位数据即可
29     int i=0;
30     while ((len--)!=0)
31     {
32         IR_SendByte(st[i]);
33         i++;
34     }
35
36 }
  1 u8 IRREC_RX_BUF[64]={0};    //接收缓冲,最大64个字节.
  2     //接收到的数据长度
  3     u8 IRREC_RX_CNT=0;
  4
  5 char rebit=stopbit;//记录接收一个字节的二进制位所处何种位置
  6 u8 Recev[8]={0};//记录接收的一个字节的二进制流
  7
  8 static volatile unsigned long long m_rx_previous_time = 0;//上一次进入中断时间
  9 static volatile unsigned char m_rx_begin_f = 0;//开始一个字节的接收标志 0-无数据开始接收 1-有数据开始接收
 10 void EXTI9_5_IRQHandler(void){
 11
 12     if(EXTI_GetITStatus(EXTI_Line8)!=RESET)
 13     {
 14         unsigned char skip_index = 0;
 15         unsigned char i = 0;
 16         unsigned char temp_bin = 0;//用于记录二进制值
 17         unsigned long long current_time = sys_micros();//记下此刻时间
 18         unsigned short interval_time = current_time - m_rx_previous_time;//计算一个状态持续的时长
 19         m_rx_previous_time = current_time;//为下次计算时长做准备
 20
 21         //当前未开始一个字节的接收且此时为下降沿
 22         if(rebit == 10)//10 当数据不合法时或者结束传输时 rebit值设为10
 23         {
 24             if(!PCin(8))//下降沿
 25             {
 26                 rebit = 0;//记下开始接收
 27                 m_rx_begin_f = 1;
 28                 debug_led(1,LED_TOGGLE);
 29             }
 30         }
 31         //已经开始接收
 32         else
 33         {
 34             //上一状态为起始位
 35             if(!rebit)//起始位0
 36             {
 37                 //计算二进制数据的个数
 38                 skip_index = (interval_time/50)-1;
 39
 40                 //个数合法
 41                 if(skip_index <= 9)
 42                 {
 43                     //根据状态保持时间更新接收值
 44                     for(i = 0;i < skip_index;i++)
 45                     {
 46                         Recev[i] = 0;
 47
 48                     }
 49
 50                     //更新接收二进制位的下标
 51                     rebit = skip_index;
 52
 53                 }
 54                 //不合法-重新接收
 55                 else
 56                 {
 57                     rebit = 10;
 58                     m_rx_begin_f = 0;
 59
 60
 61                 }
 62             }
 63             //上一状态为数据位
 64             else
 65             {
 66                 //计算二进制数据的个数
 67                 skip_index = interval_time/50;
 68
 69                 //数据不合法-重新接收
 70                 if((skip_index+rebit) > 9)//所处位置+数据个数 判断数据是否超10 合法判断
 71                 {
 72                     //printf("skip_index %d   rebit=%d \r\n",skip_index,rebit);
 73                     rebit = 10;
 74                     m_rx_begin_f = 0;
 75                     debug_led(3,LED_TOGGLE);
 76                 }
 77                 //数据合法
 78                 else
 79                 {
 80                     //当前为高电平
 81                     if(PCin(8))
 82                     {
 83                      temp_bin = 1;//0
 84                     }
 85                     else
 86                     {
 87                      temp_bin = 0;//1 change
 88                     }
 89                     debug_led(2,LED_TOGGLE);
 90                     for(i = 0;i < skip_index ;i++)//根据几个数据 给予相应的位
 91                     {
 92                         Recev[rebit+i+1] = temp_bin;//change +1
 93                         rebit++;
 94
 95                     }
 96                 }
 97
 98                 //数据已接收至结束位
 99                 if(rebit >= 8 )//=8>?
100                 {
101                     if(IRREC_RX_CNT < 64)
102                     {
103                         IRREC_RX_BUF[IRREC_RX_CNT++]     = (Recev[7] << 7) |(Recev[6] << 6)| (Recev[5] << 5)| (Recev[4] << 4)| (Recev[3] << 3) |(Recev[2] << 2) |(Recev[1] << 1) |Recev[0];
104
105                     }
106                     rebit = 10;
107                     m_rx_begin_f = 0;
108
109
110                 }
111
112             }
113         }
114
115
116         #endif
117         EXTI_ClearITPendingBit(EXTI_Line8);//清除中断挂起标志位
118
119     }
120
121 }

用于知识梳理积累,写的比较随意

有问题可以发邮箱联系我   [email protected]

原文地址:https://www.cnblogs.com/zhy-alive/p/11284232.html

时间: 2025-01-12 06:55:44

stm32 普通IO口模拟串口通信的相关文章

51单片机GPIO口模拟串口通信

51单片机GPIO口模拟串口通信 标签: bytetimer终端存储 2011-08-03 11:06 6387人阅读 评论(2) 收藏 举报 本文章已收录于: 分类: 深入C语言(20) 作者同类文章X 1 #include "reg52.h" 2 #include "intrins.h" 3 #include "math.h" 4 #include "stdio.h" 5 sbit BT_SND =P1^5; 6 sbit

STM32的IO口的8种配置

STM32的IO口的8种配置 1 STM32的输入输出管脚有下面8种可能的配置:(4输入+2输出+2复用输出) ① 浮空输入_IN_FLOATING ② 带上拉输入_IPU ③ 带下拉输入_IPD ④ 模拟输入_AIN ⑤ 开漏输出_OUT_OD ⑥ 推挽输出_OUT_PP ⑦ 复用功能的推挽输出_AF_PP ⑧ 复用功能的开漏输出_AF_OD 1.1 I/O口的输出模式下,有3种输出速度可选(2MHz.10MHz和50MHz),这个速度是指I/O口驱动电路的响应速度而不是输出信号的速度,输出信

STM32的IO口

STM32 的 IO 口 可以由软件配置成如下 8 种模式: 1.输入浮空 2.输入上拉 3.输入下拉 4.模拟输入 5.开漏输出 6.推挽输出 7.推挽式复用功能 8.开漏复用功能 每个 IO 口可以自由编程,但 IO 口寄存器必须要按 32 位字被访问.STM32 的很多 IO 口都是 5V 兼容的,这些 IO 口在与 5V 电平的外设连接的时候很有优势,具体哪些 IO 口是 5V 兼容的,可以从该芯片的数据手册管脚描述章节查到(I/O Level 标 FT 的就是 5V 电平兼容的). S

STM32中IO口的8中工作模式

该文摘自:http://blog.csdn.net/kevinhg/article/details/17490273 一.推挽输出:可以输出高.低电平,连接数字器件:推挽结构一般是指两个三极管分别受两个互补信号的控制,总是在一个三极管导通的时候另一个截止.高低电平由IC的电源决定.         推挽电路是两个参数相同的三极管或MOSFET,以推挽方式存在于电路中,各负责正负半周的波形放大任务,电路工作时,两只对称的功率开关管每次只有一个导通,所以导通损耗小.效率高.输出既可以向负载灌电流,也

教你如何在51单片机上模拟串口通信!!!

我们可以不使用单片机本身带有的串口,而自己用程序去模拟一个串口并达到和本身的串口具有同样的功能, 首先,我们需要用到CH340串口模块,大家可以上某宝自行购买. 正面: 反面: 然后我们需要了解一下这串口模块上的引脚: 5V  :与VCC短路为5V TLL输出(电源和信号输出都是5V) VCC:可以与3.3V和5V用跳帽连接 3.3V:与VCC短路为3.3V TLL输出(电源和信号输出都是3.3V) TXD:发送数据端口(与单片机上的接收引脚用杜邦线连接) RXD:接收数据端口(与单片机上的发送

51单片机IO口模拟UART串口通信

#include <reg52.h>#include "main.h"#include "smartcard.h"#include "stdio.h" typedef enum { false, true }bool; #if 0sbit PIN_RXD = P1^0; //接收发送同一个引脚定义sbit PIN_TXD = P1^0; //接收发送同一个发送引脚定义sbit PIN_CLK = P3^1; //智能卡时钟引脚定义sb

关于STM32的IO口速率问题

输入模式可以不用配置速度,但是输出模式必须确定最大输出频率.当STM32的GPIO端口设置为输出模式时,有三种速度可以选择:2MHz.10MHz和50MHz,这个速度是指I/O口驱动电路的速度,是用来选择不同的输出驱动模块,达到最佳的噪声控制和降低功耗的目的.高频的驱动电路,噪声也高,当你不需要高的输出频率时,请选用低频驱动电路,这样非常有利于提高系统的EMI性能.注意:GPIO的引脚速度是指I/O口驱动电路的响应速度而不是输出信号的速度,输出信号的速度与你的程序有关.

STM32的IO口配置

每个GPI/O端口有两个32位配置寄存器(GPIOx_CRL,GPIOx_CRH),两个32位数据寄存器(GPIOx_IDR和GPIOx_ODR),一个32位置位/复位寄存器(GPIOx_BSRR),一个16位复位寄存器(GPIOx_BRR)和一个32位锁定寄存器(GPIOx_LCKR). GPIO端口的每个位可以由软件分别配置成多种模式. 1.输入浮空 2.输入上拉 3.输入下拉 4.模拟输入 5.开漏输出 6.推挽式输出 7.推挽式复用功能 8.开漏复用功能 每个I/O端口位可以自由编程,然

关于stm32的IO口的封装

前言:STM32的PA,PB各有16脚,用不上那么多,该怎样处理呢? 注:unchar 是 unsigned char 关于输出同时操作8位 1 //***************************************************************/ 2 #define part_a_o PBout(0)// */ 3 #define part_b_o PBout(1)// */ 4 #define part_c_o PBout(2)// */ 5 #define p