STM32 freertos 使用软件模拟串口uart

news2024/9/30 7:22:01

如题,为什么要这样做?

最近做的一个项目上使用了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 作者 提供的帮助;

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

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

相关文章

JOSEF约瑟 静态中间继电器 RZY-600D 110VDC 六常开 导轨安装

RZ-D系列中间继电器 系列型号&#xff1a; RZY-004D中间继电器 RZL-004D中间继电器 RZY-022D中间继电器 RZL-022D中间继电器 RZY-112D中间继电器 RZL-112D中间继电器 RZY-202D中间继电器 RZL-202D中间继电器 RZY-002D中间继电器 RZL-002D中间继电器 RZY-060D中间继电器 RZL-060…

SpringCloud-Knife4j文档聚合

在微服务架构下&#xff0c;如果给每个微服务都配置文档&#xff0c;那么每个微服务的接口文档都有自己独立的访问地址&#xff0c;这样要一个个打开每个微服务的文档非常麻烦。一般我们会采用聚合的办法&#xff0c;将所有微服务的接口整合到一个文档中&#xff0c;具体做法有…

【时间序列篇】基于LSTM的序列分类-Pytorch实现 part1 案例复现

系列文章目录 【时间序列篇】基于LSTM的序列分类-Pytorch实现 part1 案例复现 【时间序列篇】基于LSTM的序列分类-Pytorch实现 part2 自有数据集构建 【时间序列篇】基于LSTM的序列分类-Pytorch实现 part3 化为己用 本篇文章是对已有一篇文章的整理归纳&#xff0c;并对文章中…

[ESP32]在Thonny IDE中,如何將MicroPython firmware燒錄到ESP32開發板中?

[ESP32 I MicroPython] Flash Firmware by Thonny(4.1.4) IDE 正常安裝流程&#xff0c;可參考上述影片。然而&#xff0c;本篇文章主要是紀錄安裝過程遇到的bug, 供未來查詢用&#xff0c;也一併供有需要的同好參考。 問題:安裝後&#xff0c;Thonny互動介面顯示一堆亂碼和co…

新建react项目,react-router-dom配置路由,引入antd

提示&#xff1a;reactrouter6.4版本&#xff0c;与reactrouter5.0的版本用法有区别&#xff0c;互不兼容需注意 文章目录 前言一、创建项目二、新建文件并引入react-router-dom、antd三、配置路由跳转四、效果五、遇到的问题六、参考文档总结 前言 需求&#xff1a;新建react项…

python-自动化篇-运维-监控-简单实例-道出如何使⽤Python进⾏系统监控?

如何使⽤Python进⾏系统监控&#xff1f; 使⽤Python进⾏系统监控涉及以下⼀般步骤&#xff1a; 选择监控指标&#xff1a; ⾸先&#xff0c;确定希望监控的系统指标&#xff0c;这可以包括 CPU 利⽤率、内存使⽤情况、磁盘空间、⽹络流量、服务可⽤性等。选择监控⼯具&#x…

tf卡被格式化怎么恢复里面的数据?恢复指南在此

在日常生活中&#xff0c;我们经常使用TF卡来存储各种数据&#xff0c;如照片、视频、文档等。然而&#xff0c;有时候我们会误将TF卡格式化&#xff0c;导致其中的数据丢失。为了挽救这些宝贵的数据&#xff0c;我们需要采取一些措施来进行恢复。本文将为你介绍如何恢复TF卡中…

架构整洁之道——价值维度与编程范式

1 设计与架构究竟是什么 结论&#xff1a;二者没有任何区别&#xff0c;一丁点区别都没有。 架构图里实际上包含了所有底层设计细节&#xff0c;这些细节信息共同支撑了顶层的架构设计&#xff0c;底层设计信息和顶层架构设计共同组成了整个架构文档。底层设计细节和高层架构信…

滑木块H5小游戏

欢迎来到程序小院 滑木块 玩法&#xff1a;点击木块横着的只能左右移动&#xff0c;竖着的只能上下移动&#xff0c; 移动到箭头的位置即过关&#xff0c;不同关卡不同的木块摆放&#xff0c;快去滑木块吧^^。开始游戏https://www.ormcc.com/play/gameStart/260 html <can…

JavaEE 网络编程

JavaEE 网络编程 文章目录 JavaEE 网络编程引子1. 网络编程-相关概念1.1 基本概念1.2 发送端和接收端1.3 请求和响应1.4 客户端和服务端 2. Socket 套接字2.1 数据包套接字通信模型2.2 流套接字通信模型2.3 Socket编程注意事项 3. UDP数据报套接字编程3.1 DatagramSocket3.2 Da…

pip 安装出现报错 SSLError(SSLError(“bad handshake

即使设置了清华源&#xff1a; pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simplepip 安装包不能配置清华源&#xff0c;出现报错: Retrying (Retry(total2, connectNone, readNone, redirectNone, statusNone)) after connection broken by ‘SSLE…

适用于 Windows 的 10 款免费 MP4 转 MP3 转换神器

每当我们观看歌曲或视频剪辑时&#xff0c;我们经常会想到将其转换为 MP3 格式&#xff0c;以便我们可以将其保存在设备上&#xff0c;因为它占用的空间更少。在将 MP4 转换为 MP3 的过程中&#xff0c;第一步也是最重要的一步是选择正确的工具来转换它&#xff0c;如果您想添加…

API网关-Apisix RPM包方式自动化安装配置教程

文章目录 前言一、简介1. etcd简介2. APISIX简介3. apisix-dashboard简介 二、Apisix安装教程1. 复制脚本2. 增加执行权限3. 执行脚本4. 浏览器访问5. 卸载Apisix 三、命令1. Apisix命令1.1 启动apisix服务1.2 停止apisix服务1.3 优雅地停止apisix服务1.4 重启apisix服务1.5 重…

SG-8506CA 可编程晶体振荡器 (SPXO)

输出: LV-PECL频率范围: 50MHz ~ 800MHz电源电压: 2.5V to 3.3V外部尺寸规格: 7.0 5.0 1.5mm (8引脚)特性:用户指定一个起始频率, 7-bit I2C 地址:用户可编程: I2C 接口:基频的高频晶体:低抖动PLL技术应用:OTN, BTS, 测试设备 规格&#xff08;特征&#xff09; *1 这包括初…

链表--543. 二叉树的直径/medium 理解度C

543. 二叉树的直径 1、题目2、题目分析3、复杂度最优解代码示例4、适用场景 1、题目 给你一棵二叉树的根节点&#xff0c;返回该树的 直径 。 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。 两节点之间路径的 长度 …

Python Flask与APScheduler构建简易任务监控

1. Flask Web Flask诞生于2010年&#xff0c;是用Python语言&#xff0c;基于Werkzeug工具箱编写的轻量级、灵活的Web开发框架&#xff0c;非常适合初学者或小型到中型的 Web 项目。 Flask本身相当于一个内核&#xff0c;其他几乎所有的功能都要用到扩展&#xff08;邮件扩展…

案例分享 | 助力数字化转型:嘉为科技项目管理平台上线

嘉为科技项目管理平台&#xff08;一期&#xff09;基于易趋&#xff08;EasyTrack&#xff09;进行实施&#xff0c;通过近一年的开发及试运行&#xff0c;现已成功交付上线、推广使用&#xff0c;取得了良好的应用效果。 1.关于广州嘉为科技有限公司&#xff08;以下简称嘉为…

外卖跑腿系统开发:构建高效、安全的服务平台

在当今快节奏的生活中&#xff0c;外卖跑腿系统的开发已成为技术领域的一个重要课题。本文将介绍如何使用一些常见的编程语言和技术框架&#xff0c;构建一个高效、安全的外卖跑腿系统。 1. 技术选择 在开始开发之前&#xff0c;我们需要选择适合的技术栈。常用的技术包括&a…

idea使用注释时如何不从行首开始

1、File—>setting 2、找到Editor&#xff0c;点Code Style 1.对于java注释设置 点java&#xff0c;然后选择Code Generation,去掉Line comment at first column,选择Add a space at comment start 2.对于xml注释设置 点XML&#xff0c;然后选择Code Generation,去掉Line c…

java-数组(以及jvm的内存分布)

文章目录 数组的基本概念数组的作用数组的创建以及初始化数组的创建数组的初始化 数组的使用数组中元素的访问遍历打印数组 数组是引用类型初始jvm的内存分布基本类型变量和引用类型变量的区别引用变量 认识null 数组的基本概念 数组可以看作是一种类型的集合我们在内存空间上…