GPIO模拟UART串口发送和接收

这篇具有很好参考价值的文章主要介绍了GPIO模拟UART串口发送和接收。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 串口通讯协议

通用异步收发器(Universal Asynchronous Receiver/Transmitter),通常称作UART,是一种串行、异步、全双工的通信协议,在嵌入式领域应用的非常广泛。
数据通讯格式:
gpio模拟串口,单片机,fpga开发,嵌入式硬件
空闲位:
  UART协议规定,当总线处于空闲状态时信号线的状态为‘1’即高电平,表示当前线路上没有数据传输。

起始位:
  每开始一次通信时发送方先发出一个逻辑”0”的信号(低电平),表示传输字符的开始。因为总线空闲时为高电平所以开始一次通信时先发送一个明显区别于空闲状态的信号即低电平。

数据位:
  起始位之后就是我们所要传输的数据,数据位可以是5、6、7、8,9位等,构成一个字符(一般都是8位)。如ASCII码(7位),扩展BCD码(8位)。先发送最低位,最后发送最高位,使用低电平表示‘0’高电平表示‘1’完成数据位的传输。

奇偶校验位:
  数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验),以此来校验数据传送的正确性。校验位其实是调整个数,串口校验分几种方式:
1、无校验(no parity)。
2、奇校验(odd parity):如果数据位中“1”的数目是偶数,则校验位为“1”,如果“1”的数目是奇数,校验位为“0”。
3、偶校验(even parity):如果数据为中“1”的数目是偶数,则校验位为“0”,如果为奇数,校验位为“1”。
4、mark parity:校验位始终为1(不常用)。
5、parity:校验位始终为0(不常用)。

停止位:
  它是一个字符数据的结束标志。可以是1位、1.5位、2位的高电平。 由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备之间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟的机会。停止位个数越多,数据传输越稳定,但是数据传输速度也越慢。

波特率:
数据传输速率使用波特率来表示。单位bps(bits per second),常见的波特率9600bps、115200bps等等,其他标准的波特率是1200,2400,4800,19200,38400,57600。举个例子,如果串口波特率设置为9600bps,那么传输一个比特需要的时间是1/9600≈104.2us。

2. GPIO模拟UART(9600bps)

2.1. GPIO模拟发送
a. 阻塞式发送:
利用delay()函数进行数据模拟发送(delay 104us):
优点:程序编写简单,适合单纯只有数据发送的情况
缺点:阻塞式发送,CPU占用高,发送时候无法进行其他操作

void gpio_uart_send(unsigned char *data, unsigned char data_len)
{
     unsigned char i = 0, j = 0;
     unsigned char start_flag = 0;
     for (i; i < data_len; i++)
     {
         if (0 == start_flag)
         {
             PORTA &= ~(0x01 << gpio_cfg_data.tx_pin);
             sys_delay_us(gpio_uart_delay_time);
             start_flag = 1;
         }

         for (j = 0; j < gpio_cfg_data.bits; j++)
         {
             if ((data[i] >> j) & 0x01)
             {
                 PORTA |= (0x01 << gpio_cfg_data.tx_pin);
            }
            else
            {
                PORTA &= ~(0x01 << gpio_cfg_data.tx_pin);
            }
             sys_delay_us(gpio_uart_delay_time);
         }

        PORTA |= (0x01 << gpio_cfg_data.tx_pin);
        sys_delay_us(gpio_uart_delay_time);

         start_flag = 0;
     }
}

b. 使用硬件定时器方式发送(推荐使用该方式)
优点:发送过程中不阻塞,可以执行其它任务
缺点:占用一个us级别硬件定时器资源,程序编写有一定逻辑

  1. 初始化一个104us的硬件定时器(以下使用一个51内核单片机为例)
static void __timer0_init(void)
{
    TMR0 = 52; // 时间计算 1/16000000 * 2 * (255-52) * 4 ≈ 104us(16M主频,2T指令周期,255溢出时间,时钟4分频)
    T0IF = 0; //清空T0软件中断
    // T0IE = 1; //开定时器/计数器0中断 
}
  1. 配置GPIO为上拉输出
    // 将TX引脚配置成输出模式,并上拉
    PORTA |= (1 << cfg->tx_pin);
    TRISA &= (1 << cfg->tx_pin);
    WPUA |= (1 << cfg->tx_pin);
  1. 定时器中断中去发送数据
void gpio_uart_send_bit(void)
{
    static unsigned char data_len = 0, bit_cnt = 0;
    static unsigned char bit_start_flag = 1;
    if (1 == bit_start_flag) {
        PORTA &= ~(0x01 << gpio_cfg_data.tx_pin);
        bit_start_flag = 0;
    } else if (bit_cnt < 8) {
        if ((send_data.data[data_len] >> bit_cnt) & 0x01) {
            PORTA |= (0x01 << gpio_cfg_data.tx_pin);
        } else {
            PORTA &= ~(0x01 << gpio_cfg_data.tx_pin);
        }
        bit_cnt++;
    } else {
        bit_start_flag = 1;
        bit_cnt = 0;
        PORTA |= (0x01 << gpio_cfg_data.tx_pin);
        data_len++;
    }
    if (data_len == send_data.data_len) {
        T0IE = 0; //发送完毕,关闭定时器T0
        data_len = 0;
        send_data_cache.finish_flag = 1;
    }
}
  1. 发送api函数
void gpio_uart_send(unsigned char *data, unsigned char data_len)
{
    if (data_len > 5) {
        data_len = 5;
    }

    if (1 == send_data.finish_flag) {
        memcpy(&send_data.data[0], data, data_len);
        send_data.data_len = data_len;
        send_data.finish_flag = 0;
    } else { //缓存一个数据
        memcpy(&send_data_cache.data[0], data, data_len);
        send_data_cache.data_len = data_len;
        send_data_cache.finish_flag = 0;
    }

    if (0 == T0IE) { // 当前未发送
        __timer0_init();
        T0IE = 1; //开定时器/计数器0中断
    }
}

2.2. GPIO模拟接收
GPIO模拟接收也需要一个us级的硬件定时器以及一个GPIO外部中断文章来源地址https://www.toymoban.com/news/detail-793843.html

  1. 配置接口GPIO为上拉输入,并且开启GPIO中断
    // 将RX引脚配置成输入模式,并上拉
    PORTA &= (1 << cfg->rx_pin);
    TRISA |= (1 << cfg->rx_pin);
    WPUA |= (1 << cfg->rx_pin);

    // 开启PA2中断
    INTEDG = 0; //使能PA2下降沿中断
    INTF = 0;   // 清PA2中断标志位
    INTE = 1;   // 使能PA2中断
  1. 配置定时器52us进入一次中断
static void __timer2_init(void) 
{
	//T2CON = 0; //Bit[1,0]=00,T2时钟分频 1:1
			//Bit[6-3]=0000,T2输出时钟分频1:1 
	T2CON = 0B00000001; //Bit[1,0]=01,T2时钟分频 1:4
			//Bit[6-3]=0000,T2输出时钟分频1:1 
	//T2CON = 0X79; //Bit[1,0]=01,T2时钟分频 1:4
			//Bit[6-3]=1111,T2输出时钟分频1:1 6
	TMR2 = 0;  //TMR2赋初值
	PR2 = 104; //设置TMR2输出比较值定时52us=(1/16000000)*2*4*104(PR2)
											//16M-2T-4分频 

	TMR2IF = 0;//清TMER2中断标志
	TMR2IE = 1;//使能TMER2的中断
	TMR2ON = 1;//使能TMER2启动
	PEIE = 1;    //使能外设中断
}
  1. GPIO中断处理:在接收到下降沿中断来临的时候,开启定时器中断
    if (INTE && INTF) { // RA2 INT中断
        INTF = 0; 
        RA6 = RA2;
        if (RA2) {  //RA2为高电平时,开启下降沿中断
            INTEDG = 0; 
        } else {    //RA2为低电平时,开启上升沿中断
            INTEDG = 1;
            cnt= 0;
            TMR2 = 0; 
            PR2 = 100;
            INTE = 0;//关闭IO中断
        }
    }
  1. 定时器中断处理
//中断函数里面
    if (TMR2IE && TMR2IF) { //定时器52us中断
        TMR2IF = 0;
        TMR2 = 0;
        PR2 = 200; //设置TMR2输出比较值定时104us=(1/16000000)*2*4*200(PR2)
        if (cnt < 0xff) {
            cnt++;
        }
        if ((cnt < 10) && (cnt >= 2)){
            gpio_uart_get_bit(cnt-2, RA2);
        } else if (10 == cnt) {
            INTE = 1;
        } else if (cnt >= 40) {
            cnt = 0xff;
            RA6 = 0;
        }
    }
//中断函数结束
void gpio_uart_get_bit(unsigned char data_bit_cnt, unsigned char data_bit)
{
    unsigned char temp_bit = data_bit;
    if (0 == recive_data.finish_flag) {
        recive_data.data[recive_data.data_len] |= (temp_bit << data_bit_cnt);
    }
    if (7 == data_bit_cnt) {
        recive_data.data_len++;
    }
    if (recive_data.data_len == 5) {
        recive_data.finish_flag = 1;
    }
}

到了这里,关于GPIO模拟UART串口发送和接收的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • GD32F4单片机实现接收超时中断+DMA实现串口的不定长接收和DMA发送

    环形缓冲区+定时器超时中断的方式 优点 环形缓冲区可以接收多帧数据 数据帧超时间隔可以设置 缺点 设备任务比较繁重时,使用中断接收可能会丢失数据。尤其是在长时间关闭中断或者串口中断优先级不高时 频繁进出中断。在使用RTOS的系统中,每收到一个数据就会进行一

    2024年02月15日
    浏览(63)
  • 【个人笔记】51单片机串口通信的字符串接收和发送,串口通信调节数码管显示时钟(串口通信,定时器,数码管)

           目的:利用PROTUES仿真软件、串口调试助手、虚拟串口,搭建单片机与PC通信仿真平台,熟悉单片机串口的配置及与PC机的通信方法;尝试制定通信协议,单片机根据通信协议解析接收到的内容,并根据接收的指令执行相应的操作。 基本功能: 1.时分秒的动态显示。

    2024年02月11日
    浏览(63)
  • 32单片机基础:GPIO输出

    目录 简介: GPIO输出的八种模式 STM32的GPIO工作方式 GPIO支持4种输入模式: GPIO支持4种输出模式: 浮空输入模式 上拉输入模式 下拉输入模式 模拟输入模式: 开漏输出模式:(PMOS无效,就是开漏输出,) 开漏复用输出模式 (P-MOS和N-MOS都有效) 推挽输出模式 推挽复用输出模

    2024年02月21日
    浏览(44)
  • 32单片机基础:GPIO输入

    按键介绍: 两种方式,我们一般用下接的方式。 第一个图:注意点。当按键按下,PA0接地,被置为低电平, 但是一旦按键松手,PA0悬空,引脚电压不确定。所以无论怎么读引脚也不知道知否被按下,所以为了解决这个问题,所以必须要求PA0是上拉输入的模式,这样引脚悬空

    2024年02月22日
    浏览(39)
  • 单片机中GPIO八种工作模式详细分析

    今天给大家讲解一下 GPIO 基础,参考资料: STM32F1xx 官方资料: 《STM32中文参考手册V10》-第8章通用和复用功能IO(GPIO和AFIO) GPIO 是通用输入/输出端口的简称,是 STM32 可控制的引脚。GPIO 的引脚与外部硬件设备连接,可实现与外部通讯、控制外部硬件或者采集外部硬件数据的功

    2024年02月16日
    浏览(36)
  • 嵌入式STM32 单片机 GPIO 的工作原理详解

    STM32的 GPIO 介绍 GPIO 是通用输入/输出端口的简称,是 STM32 可控制的引脚。GPIO 的引脚与外部硬件设备连接,可实现与外部通讯、控制外部硬件或者采集外部硬件数据的功能。 以 STM32F103ZET6 芯片为例子,该芯片共有 144 脚芯片,包括7个通用目的的输入/输出口(GPIO)组,分别为

    2024年02月20日
    浏览(50)
  • 【奇葩瑞萨-004】RX系列单片机的GPIO初始化

    以RX66T单片机和RX130单片机为例。 端口的配置,就是写入端口配置相关寄存器的过程。 就瑞萨RX系列单片机而言,包括PORT寄存器和MPC寄存器。 PDR:GPIO方向寄存器,读/写型 1:输出 0:输入(默认值) PORTm.PDR.BITn对应Pmn。 有些端口只内建了输入逻辑门电路,即便将其PDR位设置

    2024年02月14日
    浏览(41)
  • STM32单片机(三)第二节:GPIO输出练习3(蜂鸣器)

    ❤️ 专栏简介:本专栏记录了从零学习单片机的过程,其中包括51单片机和STM32单片机两部分;建议先学习51单片机,其是STM32等高级单片机的基础;这样再学习STM32时才能融会贯通。 ☀️ 专栏适用人群 :适用于想要从零基础开始学习入门单片机,且有一定C语言基础的的童鞋

    2024年02月11日
    浏览(47)
  • 使用51单片机的GPIO输出占空比可调节的PWM波

    在一些单片机或微控制器中,通用GPIO可以被配置为产生PWM信号。PWM即脉冲宽度调制,是一种用于模拟输出的技术。它可以通过改变输出信号的脉冲宽度来控制电路中的电平,从而实现对电路的控制。 PWM波( Pulse-Width Modulation ),即脉宽调制波,是一种用于控制电子电路中电

    2024年02月01日
    浏览(78)
  • 【单片机】基于STM32的UART串口通信

    简单讲解一下UART通信协议,以及UART能够实现的一些功能,还有有关使用STM32CubeMX来配置芯片的一些操作。实验内容基于 正点原子精英板 开发板,单片机芯片为 STM32F103ZET6 。 在后面我会以我使用的STM32F429开发板来举例讲解(其他STM32系列芯片大多数都可以按照这些步骤来操作

    2024年01月17日
    浏览(79)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包