STM32 freertos 使用软件模拟串口uart

如题,为什么要这样做?

最近做的一个项目上使用了74HC595作为指示灯板使用;

这个灯板与驱动板是通过排线连接,排线约25cm长;

在实验室测试一切正常,发到客户手上使用就出现了某个LED跳动情况;

跳动原因:传输线收到干扰。

这种显示方式抗干扰能力非常差且没有校验功能,满足不了需求;

因为传输线是必须要有的,所以只能通过增加校验的方式来处理干扰。

解决方法:

  1. 指示灯板增加MCU;
  2. 驱动板与灯板改为UART通讯增加校验功能;

因为驱动板与灯板连接的IO口没有UART外设功能,只能通过软件模拟UART使用;

代码

代码来自于:发个软件UART代码吧 (amobbs.com 阿莫电子技术论坛)

#include "SimComIO.h"
#include <string.h>



//为了提高接收的准确率和纠错性能,采用3倍采样率,定时中断的频率是波特率的3倍。例如:需要波特率9600bps的话,需要1/(9600*3)=34.72us的定时中断。


//#define SC_RXD 6
//#define SC_RXD_SET_INPUT() 	{DDRD&=(~BIT(SC_RXD));}
//#define SC_RXD_GET() 				((PIND&(BIT(SC_RXD))))
#define SC_RXD_GET() 				(HAL_GPIO_ReadPin(SIM_UART_RX_GPIO_Port, SIM_UART_RX_Pin))


//#define SC_TXD 7
//#define SC_TXD_0() 					{PORTD&=(~BIT(SC_TXD));}
//#define SC_TXD_1() 					{PORTD|=BIT(SC_TXD);}
//#define SC_TXD_SET_OUTPUT() {DDRD|=BIT(SC_TXD);}
#define SC_TXD_0() 					{HAL_GPIO_WritePin(SIM_UART_TX_GPIO_Port, SIM_UART_TX_Pin, GPIO_PIN_RESET);}
#define SC_TXD_1() 					{HAL_GPIO_WritePin(SIM_UART_TX_GPIO_Port, SIM_UART_TX_Pin, GPIO_PIN_SET);}

uint8_t sc1_rxd_scan_ct=0;
uint8_t sc1_rxd_scan_next_time=0;
uint8_t sc1_rxd_scan_step=0;
uint8_t sc1_rxd_dat;
uint8_t sc1_rxd_ready=0;
uint8_t sc1_rxd_tmpdat;


volatile uint8_t sc_txd_ready=0;//模拟串口变量
volatile uint8_t sc_txd_bit_pt=0;
volatile uint8_t sc_txd_data=0;
volatile uint8_t sc_txd_ct=0;



// typedef sim_uart_def{
	// uint8_t sc1_rxd_scan_ct;
	// uint8_t sc1_rxd_scan_next_time;
	// uint8_t sc1_rxd_scan_step;
	// uint8_t sc1_rxd_dat;
	// uint8_t sc1_rxd_ready;
	// uint8_t sc1_rxd_tmpdat;
	// uint8_t sc_txd_ready;//模拟串口变量
	// uint8_t sc_txd_bit_pt;
	// uint8_t sc_txd_data;
	// uint8_t sc_txd_ct;
// }TYPE_SIM_UART_DEF;

// TYPE_SIM_UART_DEF su1,su2,su3,su4,su5,su6;

// void SC_RxdSrv(TYPE_SIM_UART_DEF *pSU);
// void SC_TxdSrv(TYPE_SIM_UART_DEF *pSU);


void SC_Recv_Pro(uint8_t dat){

}

void SC_RxdSrv(void){
       
        if(sc1_rxd_scan_step==0){
                if(!SC_RXD_GET()){
                        sc1_rxd_scan_step=1;
                        return;
                }
        }
        if(sc1_rxd_scan_step==1){
                if(!SC_RXD_GET()){
                        sc1_rxd_scan_step=2;                //rxd start bit ok,goto next step
                        sc1_rxd_scan_ct=0;
                        sc1_rxd_scan_next_time=3;
                        sc1_rxd_tmpdat=0;
                        return;
                }
                else{
                        sc1_rxd_scan_step=0;        //rxd start bit is avalid
                        return;
                }
        }
        if(sc1_rxd_scan_step>=2){
                sc1_rxd_scan_ct++;
                if(sc1_rxd_scan_ct<sc1_rxd_scan_next_time) return;
                sc1_rxd_scan_ct=0;
               
                if(sc1_rxd_scan_step<10){
                       
                        sc1_rxd_tmpdat>>=1;
                        if(SC_RXD_GET()){
                                sc1_rxd_tmpdat|=0x80;
                        }
                        sc1_rxd_scan_step++;       
                        return;
                }
                if(sc1_rxd_scan_step==10){
                        if(SC_RXD_GET()){
                                sc1_rxd_dat=sc1_rxd_tmpdat;
                                sc1_rxd_ready=1;

                                //Receive a byte OK       
                                #if 0
                                 sc_txd_data=sc1_rxd_dat;
                                 sc_txd_ready=1;
                                 #endif
                                 #if 0
                                 Test_Uart1(sc1_rxd_dat);
                                 #endif

                                 SC_Recv_Pro(sc1_rxd_dat);
                        }
                        sc1_rxd_scan_step=0;
                        return;
                }
               
               
        }

}



void SC_TxdSrv(void){
        sc_txd_ct++;
        if(sc_txd_ct<3) return;
        sc_txd_ct=0;
        if(sc_txd_ready){                                                        //Data Ready
                if(sc_txd_bit_pt<10){
                        if(sc_txd_bit_pt==0){
                                SC_TXD_0();                                //Start BIT
                       
                        }
                        else{
                               
                                if(sc_txd_bit_pt>=9){
                                        SC_TXD_1();                        //End BIT
                                }
                                else{                                                        //数据位
                                        if((sc_txd_data>>(sc_txd_bit_pt-1))&0x01){       
                                                SC_TXD_1();
                                        }
                                        else{
                                                SC_TXD_0();
                                        }
                                }                       
                        }
                }               
                if(sc_txd_bit_pt>10){               
                        sc_txd_bit_pt=0;                        //发送完后延时两个时钟,复位各标志
                        sc_txd_ready=0;               
                }
                else{
                        sc_txd_bit_pt++;                        //位指针自加               
                }
        }
       

}


void SC_send_char(uint8_t b){
         sc_txd_data=b;
         sc_txd_ready=1;
         while(sc_txd_ready==1);

}
void SC_send_str(uint8_t *str){
         while((*str)!=0){
                 SC_send_char(*str);
                str++;
         }

}

void SC_send_arr(uint8_t *arr,uint8_t len){
         while(len--){
                 SC_send_char(*arr);
         }
}


void init_SimComIO(void){
	
//	SC_TXD_SET_OUTPUT();
	SC_TXD_1();
//	SC_RXD_SET_INPUT();
	
}


// #pragma interrupt_handler ISR_T1:8
// void ISR_T1(void)
// {
// //compare occured TCNT1=OCR1A

        // SC_RxdSrv();
        // SC_TxdSrv();
// }

移植

定时器

        波特率9600,3倍采样34.72us,使用定时器2完成

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  /* USER CODE BEGIN Callback 0 */

  /* USER CODE END Callback 0 */
  if (htim->Instance == TIM4) {
    HAL_IncTick();
  }
  /* USER CODE BEGIN Callback 1 */
  if (htim->Instance == TIM2) {
//    HAL_GPIO_TogglePin(SIM_UART_TX_GPIO_Port, SIM_UART_TX_Pin);
		SC_TxdSrv();
//		SC_RxdSrv();
  }
  /* USER CODE END Callback 1 */
}

示波器观察IO引脚反转周期,符合需求。 

Freertos

HAL_TIM_Base_Start_IT(&htim2);    

在开启定时器的时候,整个任务调度接近瘫痪。

问题分析:

freertos心跳是1ms,35us这样频繁中断会影响调度的。

现在的问题是:freertos 能用35us这样的中断吗?如果能应该怎么用?

于是chatgpt开始对话,问题主要是问gpt freertos中这种35us 频繁中断应该怎么使用?

没有得到想要的答案;

网络上没得到答案就咨询身边大佬吧;

转义:

就是咱们这个发送也不是连续不断发送,实际使用中1S发送1帧就行了。

使用

taskENTER_CRITICAL();
//code
taskEXIT_CRITICAL(); 

    加入代码测试,发现问题:进入临界区之后定时器2中断不进了,被关了。

于是想,那就使用停止调度的呗

vTaskSuspendAll();         /* 开启调度锁 */    
        printf("任务vTaskLed1正在运行\r\n");   
        if(!xTaskResumeAll())      /* 关闭调度锁,如果需要任务切换,此函数返回pdTRUE,否则返回pdFALSE */
        {
            taskYIELD ();
        }    

测试还是不行。

二问大佬:

于是查代码,freertos在哪里管理中断的;

taskENTER_CRITICAL();

进这个接口查源码

最后查到了下面那个地方

打开cubemx 找修改优先级的地方

上面是修改优先级的地方,那定时器2的优先级是多少呢?

发现这个优先级是灰色的,是与freertos的 

是绑定的,比如你将 LIBRARY MAX SYSCALL INTERRUPT PRIORITY修改为6,定时器的优先级也会跟着修改为6;

那好办,在代码中手动修改定时器2优先级

HAL_NVIC_SetPriority(TIM2_IRQn, 4, 0);
	HAL_NVIC_GetPriority(TIM2_IRQn, TIM2_PriorityGroup, &TIM2_PreemptPriority, &TIM2_SubPriority);
	printf(" HAL_NVIC_GetPriority TIM2_PriorityGroup:%d, TIM2_PreemptPriority:%d,TIM2_SubPriority:%d  \r\n" ,TIM2_PriorityGroup,TIM2_PreemptPriority,TIM2_SubPriority);

验证修改成功。

验证

SC_send_char(0x55);

发现使用上面代码会卡在这个while里面,单步调试查看 sc_txd_ready 变量已经变为0了,为什么还会卡在这里? 

void SC_send_char(uint8_t b){
         sc_txd_data=b;
         sc_txd_ready=1;
         while(sc_txd_ready==1);

}

解决方法

加入volatile 修饰符;

volatile uint8_t sc_txd_ready=0;

总结

        通过上面学习使用方法与故障排除,STM32 freertos 使用软件模拟串口uart已经正常可以使用了。通过这个测试也对freertos 有了进一步认识。

感谢

babyos 作者 提供的帮助;

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot 中 Service 层依赖注入问题

目录 问题描述 产生错误 问题原因 解决方法 手动注入方法 1、使用工具集 hutool&#xff0c;引入 Maven 依赖 2、编写 SpringUtil 工具类 问题描述 Controller 层方法为 static 静态&#xff0c;引入 Service 层时使用 Autowired 注解自动装配&#xff0c;Controller层方…

2017年认证杯SPSSPRO杯数学建模D题(第二阶段)教室的合理设计全过程文档及程序

2017年认证杯SPSSPRO杯数学建模 D题 教室的合理设计 原题再现&#xff1a; 某培训机构租用了一块如图&#xff08;见附件&#xff09;所示的场地&#xff0c;由于该机构开设了多种门类的课程&#xff0c;所以需要将这块场地通过加入一些隔墙来分割为多个独立的教室和活动区。…

手把手教学:AD09制作BOM及小技巧

BOM&#xff08;Bill of Material&#xff09;物料清单&#xff0c;是以数据格式来描述产品结构的文件&#xff0c;即生产一件产品所需的子零件及其产品中零件数量的完全组合。这里生成BOM表用作对你制作的pcb板进行成本预估和制作生产资料文件。同时也是样品制作时&#xff0c…

pytest参数化

一、pytest.mark.parametrize介绍 pytest.mark.parametrize(argnames, argvalues, indirectFalse, idsNone)参数说明&#xff1a; argnames: 一个或多个参数名&#xff0c;用逗号分隔的字符串&#xff0c;如"arg1,arg2,arg3"&#xff0c;参数名与用例入参数一致。 a…

TensorFlow2实战-系列教程2:神经网络分类任务

&#x1f9e1;&#x1f49b;&#x1f49a;TensorFlow2实战-系列教程 总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在Jupyter Notebook中进行 本篇文章配套的代码资源已经上传 1、Mnist数据集 下载mnist数据集&#xff1a; %matplotlib inline from pathlib imp…

shell常用命令,参数传递,函数,挂载磁盘

1、ls 功能&#xff1a;显示文件和目录的信息ls 以默认方式显示当前目录文件列表 ls -a 显示所有文件包括隐藏文件 ls -l 显示文件属性&#xff0c;包括大小&#xff0c;日期&#xff0c;符号连接&#xff0c;是否可读写及是否可执行 ls -lh 显示文件的大小&#xff0c;以…

BOSS 直聘:日增10亿数据的历史库,如何通过OceanBase节省70%存储成本?

BOSS 直聘是在全球范围内首创互联网“直聘”模式的在线招聘产品&#xff0c;目前已经成为了中国最大的招聘平台。本文谈到的 BOSS 直聘的业务场景主要是通过数据库对招聘过程中的聊天记录信息进行存储&#xff0c;数据量极大&#xff0c;且每天都有 5 亿到 10 亿的增量数据。和…

Linux入门攻坚——14、实战软件安装-搭建Python3.8环境-2

上一篇解决了openssl和pip问题&#xff0c;这一篇来解决sqlite问题 创建app时出现错误&#xff0c;模块_sqlite3找不到&#xff0c;查询sqlite相关的包&#xff1a; 在python2.6的lib-dynload路径下&#xff0c;有_sqlite3.so&#xff0c;这个应该就是Python需要的sqlite模块&a…

Redis 实际项目中的整合,记录各种用法

Redis缓存餐厅数据 我们来看主要的流程 很简单,就是在数据库和接口之间加了一层缓冲,在redis之前其实还可以加其他的缓存 例如 nginx的缓存 接下来,就是结合我的业务,来做缓存 我这里的业务逻辑是,按了分类的按钮,分别以不同的 分类为一组缓存数据 所以,这里的缓存粒度是分类…

一个人永远无法赚到认知以外的钱

做交付、做产品&#xff0c;从来都不是一件容易的事情。在营销和服务的过程中&#xff0c;我充分见证了人生百态。 前一段时间&#xff0c;小灰创建了一个自媒体陪伴群&#xff0c;群里邀请了各个领域的自媒体大佬&#xff0c;每周都会在群里进行分享&#xff0c;大家的学习气氛…

推荐家庭关系三姑六婆计算器微信小程序源码

亲戚关系计算器微信小程序源码是一款为避免遇到亲戚却不知道该怎么称呼时遇到的尴尬情况而开发的&#xff0c;由于社会节奏的快速发展&#xff0c;现在的关系不像以前一样经常联系和维护&#xff0c;导致了有些自己家的一些亲戚也疏远了很多。 演示地 址 &#xff1a; runrunco…

httprunnerV4.X的基本使用详解

目录 1、httprunner概述 1.1、httprunner的优点 2、httprunner的安装 3、基本命令的使用 3.1、生成脚手架 3.2、将har文件转换为测试用例文件 3.3、执行测试用例 3.4、为项目创建虚拟环境&#xff0c;然后安装httprunner库 3.4、执行测试用例生成测试报告 4、httprun…

北斗卫星为野外科考人员提供安全保障

北斗卫星为野外科考人员提供安全保障 自第二次青藏高原综合科学考察研究启动以来&#xff0c;青海不断提升科考服务保障能力&#xff0c;推动科考全程信息化&#xff0c;有效促进科考成果转化。 为保障科考人员的人身安全&#xff0c;青海省青藏科学考察服务中心开发了基于北…

【Docker】附录一:常见问题总结

作者主页&#xff1a; 正函数的个人主页 文章收录专栏&#xff1a; Docker 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01; 常见问题总结 一、镜像相关 如何批量清理临时镜像文件&#xff1f; 答&#xff1a;可以使用 docker image prune 命令。 如何查看镜像支持…

9.异步爬虫

异步爬虫可以理解为非只单线程爬虫 我们下面做个例子&#xff0c;之前我们通过单线程爬取过梨视频 https://blog.csdn.net/potato123232/article/details/135672504 在保存视频的时候会慢一些&#xff0c;为了提升效率&#xff0c;我们使用异步爬虫爬取 目录 1 线程池 2 …

使用vs2022将.net8的应用程序发布为一个单独文件

在使用.NetCore3.1时&#xff0c;可以通过设置以下工程配置文本来将项目发布为一个单独的应用程序文件&#xff1a; <Project Sdk"Microsoft.NET.Sdk.WindowsDesktop"><PropertyGroup><TargetFramework>netcoreapp3.1</TargetFramework><…

Jmeter性能测试: 基于JDK 21 安装 Jmeter 5.6.3

目录 一、实验 1.环境 2.JDK下载 3.Jmeter下载 4.Windows安装JDK 21 5.Windows安装Jmeter 5.6.3 6.Linux安装JDK 21 7.Linux安装Jmeter 5.6.3 二、问题 1. Linux 的profile、bashrc、bash_profile文件有哪些区别 一、实验 1.环境 &#xff08;1&#xff09;主机 表…

C语言之指针的地址和指向的内容总结(八十四)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

计算机网络:体系结构知识点汇总

文章目录 一、计算机网络概述1.1概念及功能1.2组成和分类1.3性能指标 二、体系结构与参考模型2.1分层结构、协议、接口、服务2.2OSI参考模型2.3TCP/IP参考模型 一、计算机网络概述 1.1概念及功能 计算机网络就是通过各个节点&#xff0c;这个节点包括终端的电脑&#xff0c;手…

opencv#32 可分离滤波

滤波的可分离性 就是将一个线性滤波变成多个线性滤波&#xff0c;这里面具体所指的是变成x方向的线性滤波和y方向的线性滤波。无论先做x方向的滤波还是y方向滤波&#xff0c;两者的叠加结果是一致的&#xff0c;这个性质取决于滤波操作是并行的&#xff0c;也就是每一个图像在滤…