嵌入式开发学习之--串口通讯(下)

news2025/1/10 1:24:52

提示:本篇来做一个关于串口的输入输出实验。

文章目录

  • 前言
  • 一、项目概况
    • 1.1、项目需求
    • 1.2、项目来源
    • 1.3、开发环境
    • 1.4、项目意义
    • 1.5、项目效果展示
  • 二、开发步骤
    • 2.1、涉及硬件电路
    • 2.2、项目代码
      • 2.2.1、串口配置
  • 总结


前言

  前一篇文章我们介绍了串口的几种类型以及串口标准库的一些配置参数,这一篇结合之前的摩斯密码代码来实战一下。


提示:以下是本篇文章正文内容,下面案例可供参考

一、项目概况

1.1、项目需求

  1. 通过电脑串口助手输入消息,机器通过蜂鸣器来转换成摩斯码输出。
  2. led灯同步蜂鸣器状态,响时红灯亮,不响时不亮。
  3. 输入消息字长一次不得超过50个字节(含空格)
  4. 当执行一条语句时,会返回“发送中”字样,结束后会返回“发送完成”字样,如在发送期间,又通过串口发送了一条指令,则不执行,并返回“人工作息全忙,请稍后”字样。

1.2、项目来源

  作者脑洞。

1.3、开发环境

  软件:keil5;
  硬件:野火挑战者开发板。
  调试工具:串口助手。

1.4、项目意义

  1.使摩斯密码机更加自由的输出;
  2.除了按键以外,又解锁了一种新的与机器交互的方式;
  3.锻炼自己代码架构,分层,扩展能力。

1.5、项目效果展示

轻映录屏 2022-12-27 14-15-42

二、开发步骤

2.1、涉及硬件电路

  如图,本次实验涉及了蜂鸣器,led灯,串口三块,前两个之前已经说过了,主要就是对PA11,PH10引脚的拉高拉低操作,这次重点介绍串口。

在这里插入图片描述

在这里插入图片描述

  如图,这是一个usb转串口的模块,通过ch340g可以把串口消息转换成usb消息,这里转换之后的我们暂时不去管,只看之前的,通过右下角可以看到是通过PA9和PA10分别连接rx,tx的串口信号线。
在这里插入图片描述

  接着我们查找stm32的数据手册,可以看到PA9,PA10确实可以复用成串口1,至此,底层的配置逻辑就很清楚了,将PA9,PA10复用成串口1,然后通过串口1收发数据实现最终应用。

在这里插入图片描述

2.2、项目代码

  代码以之前的摩斯密码机为框架,添加串口相关逻辑。

2.2.1、串口配置

  usart.h

  代码如下(示例):

#ifndef __DEBUG_USART_H
#define	__DEBUG_USART_H

#include "stm32f4xx.h"
#include <stdio.h>



/*******************************************************/
#define DEBUG_USART                             USART1
#define DEBUG_USART_CLK                         RCC_APB2Periph_USART1
#define DEBUG_USART_BAUDRATE                    115200  

#define DEBUG_USART_RX_GPIO_PORT                GPIOA
#define DEBUG_USART_RX_GPIO_CLK                 RCC_AHB1Periph_GPIOA
#define DEBUG_USART_RX_PIN                      GPIO_Pin_10
#define DEBUG_USART_RX_AF                       GPIO_AF_USART1
#define DEBUG_USART_RX_SOURCE                   GPIO_PinSource10

#define DEBUG_USART_TX_GPIO_PORT                GPIOA
#define DEBUG_USART_TX_GPIO_CLK                 RCC_AHB1Periph_GPIOA
#define DEBUG_USART_TX_PIN                      GPIO_Pin_9
#define DEBUG_USART_TX_AF                       GPIO_AF_USART1
#define DEBUG_USART_TX_SOURCE                   GPIO_PinSource9

#define DEBUG_USART_IRQHandler                  USART1_IRQHandler
#define DEBUG_USART_IRQ                 				USART1_IRQn
/************************************************************/

void Debug_USART_Config(void);
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch);
void Usart_SendString( USART_TypeDef * pUSARTx, char *str);

void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch);

  直接用野火的串口配置,可以看到串口的配置参数,有串口号,引脚,波特率等等。

  usart.c

#include "usart.h"


 /**
  * @brief  ÅäÖÃǶÌ×ÏòÁ¿ÖжϿØÖÆÆ÷NVIC
  * @param  ÎÞ
  * @retval ÎÞ
  */
static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
  /* ǶÌ×ÏòÁ¿ÖжϿØÖÆÆ÷×éÑ¡Ôñ */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  
  /* ÅäÖÃUSARTΪÖжÏÔ´ */
  NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
  /* ÇÀ¶ÏÓÅÏȼ¶Îª1 */
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  /* ×ÓÓÅÏȼ¶Îª1 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  /* ʹÄÜÖÐ¶Ï */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  /* ³õʼ»¯ÅäÖÃNVIC */
  NVIC_Init(&NVIC_InitStructure);
}


void Debug_USART_Config(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  USART_InitTypeDef USART_InitStructure;
		
  RCC_AHB1PeriphClockCmd(DEBUG_USART_RX_GPIO_CLK|DEBUG_USART_TX_GPIO_CLK,ENABLE);

  RCC_APB2PeriphClockCmd(DEBUG_USART_CLK, ENABLE);
  

  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;  
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_PIN  ;  
  GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);


  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_PIN;
  GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
  

  GPIO_PinAFConfig(DEBUG_USART_RX_GPIO_PORT,DEBUG_USART_RX_SOURCE,DEBUG_USART_RX_AF);


  GPIO_PinAFConfig(DEBUG_USART_TX_GPIO_PORT,DEBUG_USART_TX_SOURCE,DEBUG_USART_TX_AF);
  

  USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;

  USART_InitStructure.USART_WordLength = USART_WordLength_8b;

  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(DEBUG_USART, &USART_InitStructure); 
	
	NVIC_Configuration();
  
	USART_ITConfig(DEBUG_USART, USART_IT_RXNE, ENABLE);
	
  USART_Cmd(DEBUG_USART, ENABLE);
}

void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
	USART_SendData(pUSARTx,ch);
		
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}

void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{
	unsigned int k=0;
  do 
  {
      Usart_SendByte( pUSARTx, *(str + k) );
      k++;
  } while(*(str + k)!='\0');
  
  while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET)
  {}
}

void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch)
{
	uint8_t temp_h, temp_l;
	temp_h = (ch&0XFF00)>>8;
	temp_l = ch&0XFF;
	
	USART_SendData(pUSARTx,temp_h);	
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
	
	USART_SendData(pUSARTx,temp_l);	
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}

void Usart_Data_Process(uint8_t ch)
{
	if(morse_code.index<50){
		if(ch!='\n'){
			morse_code.morse_in[morse_code.index++]=ch;
		}else{
			if(morse_code.out_status==UNFINISH){
				memcpy(morse_code.morse_out,morse_code.morse_in,sizeof(morse_code.morse_in));
				morse_code.read_status=FINISH;
				morse_code.index=0;
				memset(morse_code.morse_in,0,sizeof(morse_code.morse_in));
			}else{
				printf("È˹¤×÷Ϣȫ棬ÇëÉÔºó...\r\n");
				morse_code.index=0;
				memset(morse_code.morse_in,0,sizeof(morse_code.morse_in));				
			}
		}
	}else{
		if(morse_code.out_status==UNFINISH){
			morse_code.read_status=FINISH;
			memcpy(morse_code.morse_out,morse_code.morse_in,sizeof(morse_code.morse_in));
			morse_code.index=0;
			memset(morse_code.morse_in,0,sizeof(morse_code.morse_in));
			
		}else{
			morse_code.index=0;
			memset(morse_code.morse_in,0,sizeof(morse_code.morse_in));			
		}
		
	}
}


int fputc(int ch, FILE *f)
{
		USART_SendData(DEBUG_USART, (uint8_t) ch);
		
		while (USART_GetFlagStatus(DEBUG_USART, USART_FLAG_TXE) == RESET);		
	
		return (ch);
}

int fgetc(FILE *f)
{
		while (USART_GetFlagStatus(DEBUG_USART, USART_FLAG_RXNE) == RESET);

		return (int)USART_ReceiveData(DEBUG_USART);
}

  通过 Debug_USART_Config(void)初始化串口配置,然后可以分别通过Usart_SendByte()和Usart_SendString()发送一个字节和一个字符串;再就是需要注意的是fgetc()和fputc()两个函数,分别重定义了c语言中的scanf,printf到串口1,如果要改到其它口,则需要把DEBUG_USART改成其它口。 关于接收信息处理的函数Usart_Data_Process(),将获取到的字符存储,以‘“\n”作为结束。

main.c

int main(void)
{
	uint8_t * info_p;
	uint8_t information[]= "I LOVE YOU";	//´´½¨Ç鱨
	BEEP_GPIO_Config();
	Debug_USART_Config();
	LED_GPIO_Config();
	info_p=morse_code.morse_out;
	while(1){
	if(morse_code.read_status==FINISH){
		morse_code.read_status=UNFINISH;
		morse_code.out_status=FINISH;	
		printf("·¢ËÍÖÐ...\r\n");
//		printf("%s\r\n",morse_code.morse_out);
		beep_out_morse_data(info_p,strlen((const char *)info_p));	
		morse_code.out_status=UNFINISH;	
		printf("·¢ËÍÍê³É£¡\r\n");

	}

将串口得到的数据给到之前的蜂鸣器输出函数。并在之前打印消息。


总结

  这代码需求是完成了的,但是有一些地方仍然存在不足,需要注意。
  1、中断内用printf()打印消息,中断执行时间有限,最好只做一些置标志位的操作,而不要有大动作。
  2、串口协议过于简单,该消息只是以“\n”作为结束标志,而没有对整体数据是否失真做进一步判断,要知道数据传输的过程可能存在干扰,丢包等等各种因素的。
  3、本篇文章中在执行消息的同时获取到了新的需要执行的指令,会直接丢掉直到自己执行完成才能接收下一条,这也是很不好的用户体验,一般来说类似项目应该有一个缓冲区,将数据存储其中,执行完成一条后再去缓冲区读取下一条语句去执行。甚至如果有多台设备同时执行的情况,可以考虑线程池的思路,以后有机会可以展示一下。
  

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

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

相关文章

Linux Shell 编程,运算符,条件与分支,循环

Linux Shell 编程&#xff0c;运算符&#xff0c;条件与分支&#xff0c;循环1.Shell运算符2.判断语句3.for循环4.while循环1.Shell运算符 学习如何在shell中进行各种运算操作 案例&#xff1a;计算&#xff08;57&#xff09;3的值&#xff1a; #!/bin/bash res$(((57)*3)) …

游戏物体GameObject

在unity中所有游戏物体都是GameObject&#xff0c;这也是编程中的对象。 创建物体 在hierarchy窗口中&#xff0c;右击可以创建一个物体&#xff0c;当然也可以创建空物体。 物体属性 创建完物体后&#xff0c;一般可以在此处用这些工具来改变物体。 移动&#xff0c;旋转和…

戴尔科技集团助力中国石油大学打造现代数据中心

小的时候      总是幻想着      能够躺在床上上课      没想到现在竟然实现了      没错,对于当代大学生尤其是19级、20级来说,大学生活似乎是个虚无缥缈的词汇,因为相比与在校生活,在家上网课的时间可能会更长,一不留神就上了三年的“家里蹲”。      即使…

安装配置高度安全的匿名操作系统,利用暗网情报数据抓取工具获取普通人根本查看不到的信息

安装配置高度安全的匿名操作系统,利用暗网情报数据抓取工具获取普通人根本查看不到的信息。 Whonix匿名操作系统,Whonix 是一个专注于匿名,隐私和安全的操作系统。它基于Tor匿名网络,Debian GNU / Linux和隔离安全性。DNS泄漏是不可能的,即使具有root权限的恶意软件也无法…

“多点”开花,独立走向新零售

12月7日&#xff0c;亚洲最大的数字零售服务商多点Dmall正式向港交所递交招股说明书&#xff0c;在零售行业逐渐向线上线下一体化、店仓一体模式迈进之时&#xff0c;多点Dmall成为很多传统零售商转型路上的首选合作伙伴&#xff0c;给予了资本市场一定想象空间。 但也有观点认…

【Lua】ToLua逻辑热更新

1 前言 Lua基础语法 中系统介绍了 Lua 的语法体系&#xff0c;本文将进一步介绍 Unity3D 中基于 ToLua 实现逻辑热更新。 逻辑热更新是指&#xff1a;在保持程序正常运行的情况下&#xff0c;在后台修改代码逻辑&#xff0c;修改完成并推送到运行主机上&#xff0c;主机无缝接入…

HarmonyOS原子化服务最新概念、呈现形式与触发方式

一、HarmonyOS原子化服务最新概念 总体介绍&#xff1a; 原子化服务&#xff08;Ability&#xff09;是由HarmonyOS服务开放平台接入的一种技能&#xff0c;为华为智慧服务、智慧搜索、服务直达、智慧语音以及智慧视觉等流量入口提供服务技能。原子化服务以轻量化的呈现形式&…

高级网络应用复习——TCP与UDP,ACL列表, 防火墙,NAT复习与实验(带命令)

作者简介&#xff1a;一名在校云计算网络运维学生、每天分享网络运维的学习经验、和学习笔记。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 目录 前言 一.知识点总结 1.传输层的协议 &#xff08;1&#xff09;TCP 和…

「实操」结合图数据库、图算法、机器学习、GNN 实现一个推荐系统

本文是一个基于 NebulaGraph 上图算法、图数据库、机器学习、GNN 的推荐系统方法综述&#xff0c;大部分介绍的方法提供了 Playground 供大家学习。 基本概念 推荐系统诞生的初衷是解决互联网时代才面临的信息量过载问题&#xff0c;从最初的 Amazon 图书推荐、商品推荐&…

TCP的三次握手四次挥手详解

想要了解TCP的三次握手和四次挥手&#xff0c;首先要了解TCP的头部结构 TCP的头部结构如下 16位源端口号&#xff1a; 客户端地址信息16位目标端口号&#xff1a; 服务端地址信息32位序列号&#xff1a; 请求报文端数据流子节开始的位置&#xff08;比如位1024&#xff5e;2…

【20天快速掌握Python】day15-网络编程

1.网络通信的概念 简单来说&#xff0c;网络是用物理链路将各个孤立的工作站或主机相连在一起&#xff0c;组成数据链路&#xff0c;从而达到资源共享和通信的目的。 使用网络的目的&#xff0c;就是为了联通多方然后进行通信&#xff0c;即把数据从一方传递给另外一方。 前…

以分页场景谈MVC设计模式

一 、需求场景 需要实现一个分页组件&#xff0c; 可以方便的进行分页操作。 二、分析需求 从分页需求出发&#xff0c;分析潜在的元素&#xff0c; 虽然只包含一个大的分页功能&#xff0c;但是潜在的元素 包含&#xff1a;上一页 下一页 首页 尾页 当前页 等等。 为什么包含…

【Oauth2】SpringBoot整合Oauth2实现认证授权

SpringBoot整合Oauth2实现认证授权 应用场景 OAuth2.0 协议的使用场景主要为&#xff1a;第三方登录和开放接口的调用 第三方登录就是使用微信等第三方的方式来登录一个应用或者网站&#xff0c;比如用微信账号登录gitee。 而开发接口的调用&#xff0c;则比如说微信、美团…

TP5的消息队列

1.首先查看项目中是否已经有think-queue目录&#xff1a;/vendor/topthink/ 微信截图_20200909142126.png 如果没有&#xff0c;则用composer安装&#xff08;安装composer参考&#xff1a;http://www.runoob.com/w3cnote/composer-install-and-usage.html &#xff09;&#x…

Qt文档阅读笔记-Qt, QML, Widgets…What Is The Difference?

Qt, QML, Widgets…What Is The Difference? 本节主要介绍了开发Qt程序最关机的几个组建。 Qt是使用C和一些C的框架设计编写出来的。 Qt Qt是一个开源的框架。 Qt作为一个框架&#xff0c;包含了许多组件&#xff0c;这些组建又在指定的模块中&#xff0c;Qt基础组件在&…

干货| 小游戏赛道变现指南

随着羊了个羊等小游戏的爆火&#xff0c;不少人发现了小游戏赛道的巨大潜力&#xff0c;也想要在此赛道有所尝试。但是很多游戏赛道的新人对于小游戏变现问题存有疑问&#xff0c;今天就来跟大家分享一下小程序游戏赛道的变现途径&#xff01; 近期FinClip 官方正在举行小游戏…

全志 芯片 Linux MIPI CSI摄像头接口开发指南 VIN DVP CSI MIPI V4l2

1 前言 1.1 文档简介 介绍 VIN&#xff08;video input&#xff09;驱动配置&#xff0c;API 接口和上层使用方法。 1.2 目标读者 camera 驱动开发、维护人员和应用开发人员。 1.3 适用范围 ​ 表 1-1: 适用产品列表 内核版本驱动文件Linux-4.9drivers/media/platform/s…

nacos源码分析-服务注册(服务端)

安装Nacos源码 上一篇文章我们了解了《Nacos服务注册》客户端源码&#xff0c;本篇文章我们来看一下服务注册Nacos服务端的源码执行情况。首先需要下载Nacos源码&#xff0c; https://github.com/alibaba/nacos/releases/tag/1.4.3 &#xff0c; 解压之后使用IDEA工具导入即可…

Web3中文|为什么去中心化存储对NFT元数据很重要

图中文字&#xff1a;哦&#xff0c;看&#xff0c;FTX用Web2 API托管了所有在其平台上铸造的NFT&#xff0c;现在所有这些NFT的元数据都被破坏了&#xff0c;并且链接到了一个重组的网站。 这本不应该发生。但对于任何不考虑元数据和如何存储元数据的NFT项目来说&#xff0c;…

docker(5):Dockerfile

目录Dockerfile介绍Dockerfile常用指令案例&#xff1a;构建tomcat镜像Dockerfile介绍 Dockerfile 是一个用来构建镜像的文本文件&#xff0c;文本内容包含了一条条构建镜像所需的指令和说明&#xff0c;每条指令都会创建一个新的镜像层并对镜像进行提交。 Dockerfile 一般分…