【嵌入式】NXP/LPC使用GPIO+定时器模拟UART串口接收

news2024/9/28 13:23:11

目录

一 项目背景

二 原理说明

三 设计实现--GPIO部分

四 设计实现--定时器部分

五 总结


一 项目背景

        项目需要使用485串口编码器,编码器的数据以波特率9600持续向外发送。接收端计划使用485转换芯片+MCU串口。但是片上的外设资源已经被占用了,没有多余的串口外设。所以这边只能考虑用GPIO中断+定时器中断模拟UART串口的接收。

二 原理说明

【1】UART原理

        UART 为全双工通信,通常需要三条线: TX(发送)、 RX(接收)和 GND(地线)。数据串行一位一位的传送。

发送数据:空闲状态TX处于高电平,将TX拉为低电平,宽度为1bit时间,接着数据按低位到高位依次发送,数据发送完毕后,如果有校验位,则发送奇偶校验位,最后发送停止位,这样一帧数据就发送完成了。(若发送多字节数据时,就连续发送多帧数据即可。)

接收数据:RX线路处于高电平(空闲态)时,当RX检测到下降沿(高电平变为低电平)时,说明线路有数据要传输,按照约定的波特率从低位到高位接收数据,数据接收完毕后,如果有校验位,则接收奇偶校验位,最后接收停止位。

        UART的数据格式如下,其构成为空闲位+起始位+数据位+停止位

        空闲状态为高电平,当接收到第一个起始位时,变为低电平,持续时间为1/9600=104us,接着发送8位数据位,发送结束之后又转为高电平,由停止位过渡回空闲位。

        如下所示为实际逻辑分析仪接收到的数据,结合上面的分析即可明晰:

 

【2】模拟UART串口原理

        模拟UART串口既是对上述的这一串通信码进行解析。

        首先将数据接到MCU的GPIO口,GPIO口设置为下降沿触发中断,当接收到第一个下降沿时证明其进入了起始位,此时打开定时器中断,定时104us(1s/9600)进入中断,每次接收的数据就是一位。进入中断9次之后,说明一帧数据接收完成,进入停止位,此时关闭定时器中断,等待下一帧数据的到来。


三 设计实现--GPIO部分

【1】GPIO初始化:

void initEncoder485(void)
{
	scu_pinmux(0x0A ,2 , (MD_PLN|MD_EZI|MD_ZI), FUNC0);
    GPIO_SetDir(4, 9, 0);  //P4_9作为接收口,设置方向
    NVIC_SetPriority(PIN_INT0_IRQn, ((0x01<<3)|0x01));
    GPIO_IntCmd(0,4,9,1);  //下降沿触发中断
    NVIC_EnableIRQ(PIN_INT0_IRQn);  //使能GPIO中断
}

【2】GPIO中断:

uint8_t ecd485_rcv_stat = 9;  //接收状态初始化为停止位
//一帧数据:0--1--2--3--4--5--6--7--8--9--0

void GPIO0_IRQHandler(void)
{
	if(GPIO_GetIntStatus(0))
	{
		GPIO_ClearInt(1, 0);  //清中断标志
        if((ecd485_rcv_stat == 9) && (GPIO_PinRead(0x4, 9) == 0))  //若当前为停止位且收到数据0的下降沿
        {
            ecd485_rcv_stat = 0;  //状态设置为开始位
            TIM_Cmd(LPC_TIMER0,ENABLE);  //开启定时器
        }
	}
}

【3】附几个GPIO中断有关的库函数:

/*********************************************************************//**
 * @brief		Enable GPIO interrupt
 * @param[in]	pinInt		Pin interrupt number, should be: 0 to 7
 * @param[in]	portNum		Port number to read value, should be: 0 to 7
 * @param[in]	bitValue	Value that contains all bits on GPIO to enable,
 * 				should be in range from 0 to 0xFFFFFFFF.
 * @param[in]	intMode		interrupt mode, should be:
 * 					- 0: Rising edge interrupt mode
 * 					- 1: Falling edge interrupt mode
 * 					- 2: Active-High interrupt mode
 * 					- 3: Active-Low interrupt mode
 * @return		None
 **********************************************************************/
void GPIO_IntCmd(uint8_t pinInt, uint8_t portNum, uint32_t bitValue, uint8_t intMode)
{
	uint8_t  value;
	uint32_t pinSel;

	value = ((portNum&0x7)<<5)|(bitValue&0x1F);
	if (pinInt < 4) { //Using PINTSEL0
			pinSel = LPC_SCU->PINTSEL0;
			pinSel &= ~(0xFF<<(pinInt*8));
			pinSel |= (value<<(pinInt*8));
			LPC_SCU->PINTSEL0 = pinSel;
	} else { //Using PINTSEL1
		pinSel = LPC_SCU->PINTSEL1;
		pinSel &= ~(0xFF<<((pinInt-4)*8));
		pinSel |= (value<<((pinInt-4)*8));
		LPC_SCU->PINTSEL1 = pinSel;
	}
	switch(intMode)
	{
		case 0://rising edge
			LPC_GPIO_PIN_INT->ISEL &= ~(1<<pinInt);
			LPC_GPIO_PIN_INT->IENR |= (1<<pinInt);
			break;
		case 1://falling edge
			LPC_GPIO_PIN_INT->ISEL &= ~(1<<pinInt);
			LPC_GPIO_PIN_INT->IENF |= (1<<pinInt);
			break;
		case 2://active High level
			LPC_GPIO_PIN_INT->ISEL |= (1<<pinInt);
			LPC_GPIO_PIN_INT->IENR |= (1<<pinInt);
			LPC_GPIO_PIN_INT->SIENF |= (1<<pinInt);
			break;
		case 3://active Low level
			LPC_GPIO_PIN_INT->ISEL |= (1<<pinInt);
			LPC_GPIO_PIN_INT->IENR |= (1<<pinInt);
			LPC_GPIO_PIN_INT->CIENF |= (1<<pinInt);
			break;
		default:
			break;
	}
}

/*********************************************************************//**
 * @brief		Get GPIO Interrupt Status
 * @param[in]	pintNum	Pin interrupt number, should be: 0 to 7
 * @return		Function status,	could be:
 * 					- ENABLE	:Interrupt has been generated
 * 					- DISABLE	:Interrupt has not been detected
 **********************************************************************/
FunctionalState GPIO_GetIntStatus(uint32_t pintNum)
{
	return (((LPC_GPIO_PIN_INT->IST)>>pintNum)& 0x1);
}


/*********************************************************************//**
 * @brief		Clear GPIO interrupt status
 * @param[in]	intMode Interrupt mode, should be:
 * 					- 0: Rising edge interrupt mode
 * 					- 1: Falling edge interrupt mode
 * 					- 2: Active-High interrupt mode
 * 					- 3: Active-Low interrupt mode
 * @param[in]	pintNum Pin interrupt number, should be: 0 to 7
 * @return		None
 **********************************************************************/
void GPIO_ClearInt(uint8_t intMode, uint32_t pintNum)
{
	if (!(intMode&(1<<1)))
			LPC_GPIO_PIN_INT->IST = (1<<pintNum);
}


四 设计实现--定时器部分

【1】定时器初始化:

void initTimer0(void)
{
    //Initialize timer 0, prescale count time of 2uS
    TIM_ConfigStruct.PrescaleOption = TIM_PRESCALE_USVAL;
    TIM_ConfigStruct.PrescaleValue	= 53;//国产485编码器(波特率9600,104us/2+1)

    //use channel 0, MR0
    TIM_MatchConfigStruct.MatchChannel = 0;
    //Enable interrupt when MR0 matches the value in TC register
    TIM_MatchConfigStruct.IntOnMatch   = TRUE;
    //Enable reset on MR0: TIMER will reset if MR0 matches it
    TIM_MatchConfigStruct.ResetOnMatch = TRUE;
    //Stop on MR0 if MR0 matches it
    TIM_MatchConfigStruct.StopOnMatch  = FALSE;
    //Toggle MR0.0 pin if MR0 matches it
    TIM_MatchConfigStruct.ExtMatchOutputType =TIM_EXTMATCH_TOGGLE;
    //Set Match value, count value of 1 (1 * 2uS = 2us = 0.000002s --> 500 kHz)
    TIM_MatchConfigStruct.MatchValue   = 1;

    //Set configuration for Tim_config and Tim_MatchConfig
    TIM_Init(LPC_TIMER0, TIM_TIMER_MODE,&TIM_ConfigStruct);
    TIM_ConfigMatch(LPC_TIMER0,&TIM_MatchConfigStruct);

    //preemption = 1, sub-priority = 1
    NVIC_SetPriority(TIMER0_IRQn, ((0x03<<3)|0x01));
    //Enable interrupt for timer 0
    NVIC_EnableIRQ(TIMER0_IRQn);
}

【2】定时器中断(注意:这边加入了通讯码数据头的校验(0x01 0x03 0x04),不需要的可以在接收到缓冲数组之后返回):

#define ENCODER_LEN      9         //国产编码器通讯码长
uint8_t encoder_buf[ENCODER_LEN];
uint8_t timer_read_data = 0;

void TIMER0_IRQHandler(void)
{
    static uint8_t cnt = 0;
    if (TIM_GetIntStatus(LPC_TIMER0, TIM_MR0_INT) == SET) 
	{
        TIM_ClearIntPending(LPC_TIMER0, TIM_MR0_INT);
        
        ecd485_rcv_stat ++;
    }
    
    if(ecd485_rcv_stat == 9)
    {
        TIM_Cmd(LPC_TIMER0,DISABLE);//关闭定时器计数
        encoder_buf[cnt] = timer_read_data;
        if(cnt == 0)  //数据头校验,数据头为0x01 0x03 0x04
        {
            cnt ++;
            if(encoder_buf[0] != 0x01)
                cnt = 0;
        }
        else if(cnt == 1)
        {
            cnt ++;
            if(encoder_buf[1] != 0x03)
                cnt = 0;
        }
        else if(cnt == 2)
        {
            cnt ++;
            if(encoder_buf[2] != 0x04)
                cnt = 0;
        }
        else
        {
            cnt ++;
            if(cnt > 8)
                cnt = 0;
        }
        return ;
    }
    if(GPIO_PinRead(0x4, 9))  //如果GPIO P4.9口检测到高电平,数据移位
        timer_read_data |= (1 << (ecd485_rcv_stat - 1));
    else
        timer_read_data &= ~(1 << (ecd485_rcv_stat - 1));
}


五 总结

        综上,便可以通过GPIO中断+定时器中断配合模拟UART串口进行接收了,debug可以看到接收到的数组:

         本文只列举了模拟串口的接收过程,发送的过程是逆过程,原理上也差不多。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/129243.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

19.删除链表的倒数第N个结点

给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5] 示例 2&#xff1a; 输入&#xff1a;head [1], n 1 输出&#xff1a;[] 示例 3&#…

车辆未冲洗抓拍识别 工地车辆冲洗监测 opencv

车辆未冲洗抓拍识别 工地车辆冲洗监测系统t通过opencvpython可以对进出车辆冲洗情况进行自动识别&#xff0c;发现冲洗不合格自动进行抓拍存档。OpenCV基于C实现&#xff0c;同时提供python, Ruby, Matlab等语言的接口。OpenCV-Python是OpenCV的Python API&#xff0c;结合了Op…

如何对美国服务器响应速度进行优化

决定一个网站加载速度的最大因素之一是服务器的响应时间。服务器响应时间是你的服务器响应用户请求的速度&#xff0c;它可以大大影响你网站的用户体验。本文中&#xff0c;我们将讨论如何确定美国服务器响应时间慢的原因&#xff0c;尤其是如何对美国服务器响应速度进行优化。…

初探Lua脚本

1、什么是Lua Lua脚本是一个由C语言编写的小巧脚本语言&#xff0c;在所有脚本引擎中&#xff0c;Lua的速度是最快的。Lua的核心代码不过一万多行&#xff0c;因为是C语言编写的&#xff0c;因此Lua可以在几乎所有的操作系统和平台进行编译运行 2、Lua适用场景 1&#xff09;…

minio分布式集群部署

minio分布式集群部署 分布式 Minio 可以让你将多块硬盘或者多台服务器组成一个对象存储服务。由于硬盘分布在不同的节点上&#xff0c;分布式 Minio 避免了单点故障。MinioMinio分布式模式可以帮助你搭建一个高可用的对象存储服务&#xff0c;你可以使用这些存储设备&#xff…

七种分布式系统的解决方案,一次性讲给你听!

V-xin&#xff1a;ruyuan0330 获得600页原创精品文章汇总PDF 目录 TB级数据放在一台机器上&#xff1a;难啊&#xff01;到底啥是分布式存储&#xff1f;那啥又是分布式存储系统呢&#xff1f;天哪&#xff01;某台机器宕机了咋办&#xff1f;Master节点如何感知到数据副本消失…

nps内网穿透

nps服务端: linux, 公网ip npc客户端: windows, 内网 文件提取 链接&#xff1a;https://pan.baidu.com/s/1HgujpVoXpLxQ-IgAnI2Izg 提取码&#xff1a;8hyl nps安装 1.上传压缩包到服务器, 解压 2.修改conf文件夹下nps.conf文件 #HTTP(S) proxy port, no startup if em…

vue3 antd项目实战——Form表单使用【v-model数据的双向绑定,form表单嵌套input输入框、Radio单选框】

vue3 ant design vue项目实战——单选框&#xff08;Radio&#xff09;的使用以及Form表单的双向绑定知识调用&#xff08;form表单的源代码附在文章最后&#xff09;场景复现实现需求form表单整体架构的搭建input输入框文本域的嵌套单选组合Radio的嵌套button按钮组合的嵌套fo…

JVM 面试题

✅作者简介&#xff1a;热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏&#xff1a;Java面试题…

C语言:预处理(1)

程序的翻译环境和执行环境 在ANSI C的任何一种实现中&#xff0c;存在两个不同的环境&#xff1a; 第一种是翻译环境&#xff0c;在这个环境中源代码被转换为可执行的机器指令。 第二种是执行环境&#xff0c;它用于实际执行代码。 翻译环境&#xff1a; 组成一个程序的每个…

MySQL 数据库练习题记录01

文章目录前言一、数据库练习题一1.1 表结构1.2 查询所有学生的信息(学号&#xff0c;姓名&#xff0c;性别&#xff0c;班级名称)1.3 查询所有人(包括没有成绩的学生)的课程分数(学号&#xff0c;姓名&#xff0c;性别&#xff0c;班级名称&#xff0c;语文分数&#xff0c;数学…

改进YOLOv5 | 引入密集连接卷积网络DenseNet思想 | 搭建密集连接模块

YOLOv5引入密集连接卷积网络DenseNet思想 CVPR 2017最佳论文 D e n s e N e t DenseNet DenseNet 论文地址:h

SpringBoot快速入门篇

&#x1f49f;&#x1f49f;前言 ​ 友友们大家好&#xff0c;我是你们的小王同学&#x1f617;&#x1f617; 今天给大家打来的是 SpringBoot快速入门篇 希望能给大家带来有用的知识 觉得小王写的不错的话麻烦动动小手 点赞&#x1f44d; 收藏⭐ 评论&#x1f4c4; 小王的主页…

手写 mini 版 Webpack

目录 1. mini 版 Webpack 打包流程 2. 创建 minipack.js 2.1 需要用到的插件库 2.1.1 babylon —— 解析 JavaScript 语法&#xff0c;生产 AST 语法树 2.1.2 babel-traverse —— 对 AST 进行遍历、转换的工具 2.1.3 transformFromAst —— 将 ES6、ES7 等高级的语法&am…

[Verilog]有限状态机设计举例

有限状态机设计举例 摘要&#xff1a;有限状态机&#xff08;FSM&#xff09;是许多数字系统中用来控制系统和数据流路径行为的时序电路。FSM的实例包括控制单元和时序。 本实验介绍了两种类型的FSM&#xff08;Mealy和Moore&#xff09;的概念&#xff0c;以及开发此类状态机的…

Codeforces Round #837 (Div. 2)

A. Hossam and Combinatorics 题目链接&#xff1a;Problem - A - Codeforces 样例输入&#xff1a; 2 5 6 2 3 8 1 6 7 2 8 3 2 10样例输出&#xff1a; 2 4题意&#xff1a;给定一个有n个元素的数组&#xff0c;然后让我们求出有多少对(i,j)满足|a[i]-a[j]|max|a[p]-q[q]…

Hudi学习01 -- Hudi简介及编译安装

文章目录Hudi简介Hudi概述Hudi特性Hudi使用场景Hudi编译安装安装Maven编译hudi修改pom文件修改源码兼容hadoop3解决spark模块依赖的问题hudi编译命令Hudi简介 Hudi概述 Apache Hudi (Hadoop Upserts Delete and Incremental) 是下一代流数据湖平台。Apache Hudi 将核心仓库和…

并发编程中用到的几种常见锁

没有加锁而造成的数据竞争 任务&#xff1a;使用10个线程&#xff0c;同时对一个count加100000&#xff1b;最后我们期望的结果是100000&#xff1b; 实验代码&#xff1a; #include <stdio.h> #include <pthread.h> #include <unistd.h> #include <ti…

git项目 拉项目 提交 上传 保姆级教程

git 项目拉取提交 下载git https://git-scm.com/ 拉取代码 打开需要存代码的位置 右键 git bash打开git客户端 输入命令 git clone [复制的地址]上传代码 修改代码 方法一&#xff1a;命令行 打开对应的文件夹&#xff0c;右键打开git bash 拉取最新代码&#xff08;选…

React学习07-React扩展知识

setState setState更新状态的2种写法: setState(stateChange, [callback])------对象式的setState stateChange为状态改变对象(该对象可以体现出状态的更改)callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用 setState(updater, [callback])-…