Clion开发STM32——移植FreeModbus

news2024/9/25 16:30:49

STM32型号 :STM32H743VIT6
FreeModbus版本 :1.6
使用工具:stm32cubeMX,Clion
使用STM32作从机,模式:RTU

网上用keil的比较多,用Clion的比较少,如果你也用Clion,那么希望本文可以给你提供些许参考。

1 下载官网源码

官网地址:https://www.embedded-experts.at/en/freemodbus/about/
demo是移植例程,但是里面没有stm32的。
modbus是源码.
在这里插入图片描述
看一下modbus文件,我这里使用的模式是RTU模式,所以ascii和tcp文件夹就不需要了,如果你也只用RTU模式,那么只需要标红的文件夹。
在这里插入图片描述
在freemodbus-v1.6\demo\BARE\port这个文件夹中的文件全部需要,这些是接口文件。
在这里插入图片描述

把上面的文件放在一个文件夹里。

在自己的工程目录下新建FreeModbus(自己起名就好),并添加上面的文件,如下:在这里插入图片描述

在这里插入图片描述

2 修改CMakeLists_template

可以参考这篇文章Clion开发STM32——添加自己文件

增加头文件路径
在这里插入图片描述
增加编译文件
在这里插入图片描述
只关注蓝色框框起来的即可,其他文件可以忽略,那是我这个工程的其他文件。

3 cubeMX配置串口和定时器

3.1串口

在这里插入图片描述

3.2 定时器

在这里插入图片描述

3.3 NVIC

中断回调函数比较繁琐,所以我这里取消掉了,自己写中断内的内容。
在这里插入图片描述

4 修改接口 port

4.1 port.h

首先是port.h文件

#ifndef _PORT_H
#define _PORT_H

#include <assert.h>
#include <inttypes.h>

/* ----------------------- Platform includes --------------------------------*/
#include "stm32h7xx_hal.h"
#include "main.h"

/* ----------------------- Defines ---------*/

#define	INLINE                      inline
#define PR_BEGIN_EXTERN_C           extern "C" {
#define	PR_END_EXTERN_C             }

#define ENTER_CRITICAL_SECTION( )   __set_PRIMASK(1)//禁止中断
#define EXIT_CRITICAL_SECTION( )    __set_PRIMASK(0)//允许中断

typedef uint8_t BOOL;

typedef unsigned char UCHAR;
typedef char CHAR;

typedef uint16_t USHORT;
typedef int16_t SHORT;

typedef uint32_t ULONG;
typedef int32_t LONG;

#ifndef TRUE
#define TRUE            1
#endif

#ifndef FALSE
#define FALSE           0
#endif

#endif

包含平台,定义进出临界区
在这里插入图片描述

4.2 portserial.c

这个文件是留给用户写串口函数接口的。
使能串口中断(串口接收中断,串口发送中断)
这里串口发送中断,可以选择两种
1 发送寄存器空中断 UART_IT_TXE
2 发送完成中断 UART_IT_TC

两种都可以 ,只是UART_IT_TC不会主动触发中断,必须得发送完成,发送完成是只数据被写入到发送寄存器,然后串口会把数据送到移位寄存器,等到移位寄存器发送完成,才会置发送完成中断。所以后面发送函数里,得收到加一个发送函数来触发,才能进中断。

而UART_IT_TXE使能后就可以进入中断,因为发送寄存器本就空的。往发送寄存器里写数据之后,数据也会被送到移位寄存器。

我这里使用的事发生寄存器空中断UART_IT_TXE。

#include "port.h"

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"

/* ----------------------- static functions ---------------------------------*/
 void prvvUARTTxReadyISR( void );//被UART发送空中断调用的函数,注意是空中断,通知modbus数据可以发送
 void prvvUARTRxISR( void );//被UART接收中断调用的函数,通知modbus有数据到来

/* ----------------------- Start implementation -----------------------------*/
/*使能串口中断*/
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
    /* If xRXEnable enable serial receive interrupts. If xTxENable enable
     * transmitter empty interrupts.
     */
    if (xRxEnable) {
        /*设置485为接收模式*/
        __HAL_UART_ENABLE_IT(&huart2, UART_IT_RXNE);//使能接收中断
    } else {
        __HAL_UART_DISABLE_IT(&huart2, UART_IT_RXNE);
    }

    if (xTxEnable) {
        /*设置485为发送模式*/
        __HAL_UART_ENABLE_IT(&huart2, UART_IT_TXE);//使能发送寄存器为空中断
    } else {
        __HAL_UART_DISABLE_IT(&huart2, UART_IT_TXE);
    }
}
/*初始化串口*/
BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
    /*MX_USART2_UART_Init*/
    return TRUE;
}
/*发送一个字节*/
BOOL
xMBPortSerialPutByte( CHAR ucByte )
{
    /* Put a byte in the UARTs transmit buffer. This function is called
     * by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been
     * called. */
    huart2.Instance->TDR = ucByte;
    //HAL_UART_Transmit(&huart2, (uint8_t*)&ucByte, 1, 0);
    return TRUE;
}
/*接收一个字节*/
BOOL
xMBPortSerialGetByte( CHAR * pucByte )
{
    /* Return the byte in the UARTs receive buffer. This function is called
     * by the protocol stack after pxMBFrameCBByteReceived( ) has been called.
     */
    *pucByte = (uint8_t)(huart2.Instance->RDR & (uint8_t)0x00FF);
    return TRUE;
}

/* Create an interrupt handler for the transmit buffer empty interrupt
 * (or an equivalent) for your target processor. This function should then
 * call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that
 * a new character can be sent. The protocol stack will then call 
 * xMBPortSerialPutByte( ) to send the character.
 */
 void prvvUARTTxReadyISR( void )
{
    pxMBFrameCBTransmitterEmpty(  );
}

/* Create an interrupt handler for the receive interrupt for your target
 * processor. This function should then call pxMBFrameCBByteReceived( ). The
 * protocol stack will then call xMBPortSerialGetByte( ) to retrieve the
 * character.
 */
 void prvvUARTRxISR( void )
{
    pxMBFrameCBByteReceived(  );
}

这两个函数需要取消static定义,这两个函数要在中断里被调用,而static就限制了它只能在本文件中使用,所以取消掉。(当然你把中断函数写在这个文件也可以,我个人习惯把中断都放在stm32h7xx_it.c文件中)
在这里插入图片描述

4.3 portimer.c

/* ----------------------- Platform includes --------------------------------*/
#include "port.h"

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"

/* ----------------------- static functions ---------------------------------*/
 void prvvTIMERExpiredISR( void );//被定时器溢出中断调用的函数,通知modbus3.5个字符等待时间到

/* ----------------------- Start implementation -----------------------------*/
BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )
{

    TIM_MasterConfigTypeDef sMasterConfig = {0};

    htim7.Instance = TIM7;
    htim7.Init.Prescaler = 11999;
    htim7.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim7.Init.Period = usTim1Timerout50us - 1;
    htim7.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    if (HAL_TIM_Base_Init(&htim7) != HAL_OK)
    {
        return FALSE;
    }
    sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
    sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    if (HAL_TIMEx_MasterConfigSynchronization(&htim7, &sMasterConfig) != HAL_OK)
    {
        return FALSE;
    }
    return TRUE;
}

/*使能定时器*/
inline void
vMBPortTimersEnable(  )
{
    __HAL_TIM_CLEAR_IT(&htim7,TIM_IT_UPDATE);
    __HAL_TIM_SET_COUNTER(&htim7,0);
    HAL_TIM_Base_Start_IT(&htim7);
    /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
}

/*禁止定时器*/
inline void
vMBPortTimersDisable(  )
{
    HAL_TIM_Base_Stop_IT(&htim7);
    __HAL_TIM_SET_COUNTER(&htim7,0);
    __HAL_TIM_CLEAR_IT(&htim7,TIM_IT_UPDATE);
    /* Disable any pending timers. */
}

/* Create an ISR which is called whenever the timer has expired. This function
 * must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that
 * the timer has expired.
 */
 void prvvTIMERExpiredISR( void )
{
    ( void )pxMBPortCBTimerExpired(  );
}

这个函数同样取消static,它被定时器溢出中断调用
在这里插入图片描述
关于定时器参数为什么要这么设置,可以看一下FreeModbus学习——eMBInit初始化

4.4 添加port.c文件

这个文件中存放的都是功能码处理函数调用的回调函数,当然你也可以放在别的文件,比如main.c里都可以。

这个文件中的函数怎么调用的,可以看一下FreeModbus学习——读输入寄存器eMBFuncReadInputRegister

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"

/* ----------------------- Defines ------------------------------------------*/
//输入寄存器
#define REG_INPUT_START  3000
#define REG_INPUT_NREGS  4

//保持寄存器
#define REG_HOLD_START   4000
#define REG_HOLD_NREGS   10

//线圈
#define REG_COILS_START  0
#define REG_COILS_NREGS  4

//开关寄存器
#define REG_DISCRETE_START 1000
#define REG_DISCRETE_NREGS 4
/* ----------------------- Static variables ---------------------------------*/
static USHORT   usRegInputStart = REG_INPUT_START;
static USHORT   usRegInputBuf[REG_INPUT_NREGS] = {0x01, 0x02, 0x03, 0x04};


static USHORT   usRegHoldStart = REG_HOLD_START;
static USHORT   usRegHoldBuf[REG_HOLD_NREGS];

static USHORT   usRegCoilsStart = REG_COILS_START;
static uint8_t  usRegCoilsBuf[REG_COILS_NREGS];

static USHORT   usRegDiscreteStart = REG_DISCRETE_START;
static uint8_t  usRegDiscreteBuf[REG_DISCRETE_NREGS];
/****************************************************************************
* 名	  称:eMBRegInputCB
* 功    能:读取输入寄存器,对应功能码是 04 eMBFuncReadInputRegister
* 入口参数:pucRegBuffer: 数据缓存区,用于响应主机
*						usAddress: 寄存器地址
*						usNRegs: 要读取的寄存器个数
* 出口参数:
* 注	  意:上位机发来的 帧格式是: SlaveAddr(1 Byte)+FuncCode(1 Byte)
*								+StartAddrHiByte(1 Byte)+StartAddrLoByte(1 Byte)
*								+LenAddrHiByte(1 Byte)+LenAddrLoByte(1 Byte)+
*								+CRCAddrHiByte(1 Byte)+CRCAddrLoByte(1 Byte)
*							3 区
****************************************************************************/
eMBErrorCode
eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
    eMBErrorCode    eStatus = MB_ENOERR;
    int             iRegIndex;//寄存器数组索引
		usAddress = usAddress - 1;//传进来的地址+1了,这里要减1

	//判断地址是否在输入寄存器范围内
    if( ( usAddress >= REG_INPUT_START ) && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
    {
        iRegIndex = ( int )( usAddress - usRegInputStart );//地址 - 开始地址  =  索引
        while( usNRegs > 0 )
        {
            *pucRegBuffer++ = ( unsigned char )( usRegInputBuf[iRegIndex] >> 8 );//寄存器值高位
            *pucRegBuffer++ = ( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF );//寄存器值低位
            iRegIndex++;
            usNRegs--;
        }
    }
    else
    {
        eStatus = MB_ENOREG;
    }

    return eStatus;
}
/****************************************************************************
* 名	  称:eMBRegHoldingCB
* 功    能:对应功能码有:06 写保持寄存器 eMBFuncWriteHoldingRegister
*													16 写多个保持寄存器 eMBFuncWriteMultipleHoldingRegister
*													03 读保持寄存器 eMBFuncReadHoldingRegister
*													23 读写多个保持寄存器 eMBFuncReadWriteMultipleHoldingRegister
* 入口参数:pucRegBuffer: 数据缓存区,用于响应主机
*						usAddress: 寄存器地址
*						usNRegs: 要读写的寄存器个数
*						eMode: 功能码
* 出口参数:
* 注	  意:4 区
****************************************************************************/
eMBErrorCode
eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
{
    eMBErrorCode    eStatus = MB_ENOERR;
    int             iRegIndex;
		usAddress = usAddress - 1;

    if((usAddress >= REG_HOLD_START) && ((usAddress+usNRegs) <= (REG_HOLD_START + REG_HOLD_NREGS)))
    {
        iRegIndex = (int)(usAddress - usRegHoldStart);
        switch(eMode)
        {
        case MB_REG_READ://读寄存器
            while(usNRegs > 0)
            {
                *pucRegBuffer++ = (uint8_t)(usRegHoldBuf[iRegIndex] >> 8);
                *pucRegBuffer++ = (uint8_t)(usRegHoldBuf[iRegIndex] & 0xFF);
                iRegIndex++;
                usNRegs--;
            }
            break;
        case MB_REG_WRITE://写寄存器
            while(usNRegs > 0)
            {
                usRegHoldBuf[iRegIndex] = *pucRegBuffer++ << 8;
                usRegHoldBuf[iRegIndex] |= *pucRegBuffer++;
                iRegIndex++;
                usNRegs--;
            }
        }
    }
    else//错误
    {
        eStatus = MB_ENOREG;
    }

    return eStatus;
}

/****************************************************************************
* 名	  称:eMBRegCoilsCB
* 功    能:对应功能码有:01 读线圈 eMBFuncReadCoils
*													05 写线圈 eMBFuncWriteCoil
*													15 写多个线圈 eMBFuncWriteMultipleCoils
* 入口参数:pucRegBuffer: 数据缓存区,用于响应主机
*						usAddress: 线圈地址
*						usNCoils: 要读写的线圈个数
*						eMode: 功能码
* 出口参数:
* 注	  意:如继电器
*						0 区
****************************************************************************/
eMBErrorCode
eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
{
    eMBErrorCode    eStatus = MB_ENOERR;
    USHORT          iRegIndex;
    USHORT usCoilGroups = ((usNCoils - 1) / 8 + 1);
    UCHAR  ucStatus     = 0;
    UCHAR  ucBits       = 0;
    UCHAR  ucDisp       = 0;
		usAddress = usAddress - 1;
	
    if((usAddress >= REG_COILS_START) &&	((usAddress + usNCoils) <= (REG_COILS_START + REG_COILS_NREGS)))
    {
        iRegIndex = (int)(usAddress - usRegCoilsStart);
        switch(eMode)
        {
        case MB_REG_READ://读线圈
            while(usCoilGroups--)
            {
                ucDisp = 0;
                ucBits = 8;
                while((usNCoils--) != 0 && (ucBits--) != 0)
                {
                    ucStatus |= (usRegCoilsBuf[iRegIndex++] << (ucDisp++));
                }
                *pucRegBuffer++ = ucStatus;
            }
            break;
        case MB_REG_WRITE://写线圈
            while(usCoilGroups--)
            {
                ucStatus = *pucRegBuffer++;
                ucBits   = 8;
                while((usNCoils--) != 0 && (ucBits--) != 0)
                {
                    usRegCoilsBuf[iRegIndex++] = ucStatus & 0X01;
                    ucStatus >>= 1;
                }
            }
        }
    }
    else//错误
    {
        eStatus = MB_ENOREG;
    }

    return eStatus;
}
/****************************************************************************
* 名	  称:eMBRegDiscreteCB 
* 功    能:读取离散寄存器,对应功能码有:02 读离散寄存器 eMBFuncReadDiscreteInputs
* 入口参数:pucRegBuffer: 数据缓存区,用于响应主机   
*						usAddress: 寄存器地址
*						usNDiscrete: 要读取的寄存器个数
* 出口参数:
* 注	  意:1 区
****************************************************************************/
eMBErrorCode
eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
{
    eMBErrorCode    eStatus = MB_ENOERR;
    USHORT          iRegIndex;
    USHORT usDiscreteGroups = ((usNDiscrete - 1) / 8 + 1);
    UCHAR  ucStatus     = 0;
    UCHAR  ucBits       = 0;
    UCHAR  ucDisp       = 0;
		usAddress = usAddress - 1;
	
    if((usAddress >= REG_DISCRETE_START) &&	((usAddress + usNDiscrete) <= (REG_DISCRETE_START + REG_DISCRETE_NREGS)))
    {
        iRegIndex = (int)(usAddress - usRegDiscreteStart);

				while(usDiscreteGroups--)
				{
						ucDisp = 0;
						ucBits = 8;
						while((usNDiscrete--) != 0 && (ucBits--) != 0)
						{
								if(usRegDiscreteBuf[iRegIndex])
								{
										ucStatus |= (1 << ucDisp);
								}
								ucDisp++;
						}
						*pucRegBuffer++ = ucStatus;
				}
    }
    else//错误
    {
        eStatus = MB_ENOREG;
    }

    return eStatus;
}

4.5 写中断服务函数

void USART2_IRQHandler(void)
{
  /* USER CODE BEGIN USART2_IRQn 0 */
    if((__HAL_UART_GET_FLAG(&huart2, UART_FLAG_RXNE) != RESET)
        && (__HAL_UART_GET_IT_SOURCE(&huart2, UART_IT_RXNE)!= RESET)) {
        __HAL_UART_CLEAR_FLAG(&huart2, UART_FLAG_RXNE);
        
        prvvUARTRxISR();

        return;
    }


    if ((__HAL_UART_GET_IT_SOURCE(&huart2, UART_IT_TXE)!= RESET)
        && (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_TXE)!= RESET)){
        __HAL_UART_CLEAR_FLAG(&huart2, UART_FLAG_TXE);
        
        prvvUARTTxReadyISR();

        return;
    }

  
}

void TIM7_IRQHandler(void)
{
  /* USER CODE BEGIN TIM7_IRQn 0 */
    if(__HAL_TIM_GET_FLAG(&htim7, TIM_FLAG_UPDATE) != RESET
        && __HAL_TIM_GET_IT_SOURCE(&htim7, TIM_IT_UPDATE) !=RESET) {


        __HAL_TIM_CLEAR_FLAG(&htim7, TIM_FLAG_UPDATE);

        prvvTIMERExpiredISR();

    }

}

5 调用协议栈

依次调用
eMBInit( MB_RTU, 1, 3, 9600, MB_PAR_NONE );
eMBEnable();

然后把eMBPoll();放在循环里
在这里插入图片描述

6 测试

以读输入寄存器为例
功能码04

使用软件Modbus Poll
在这里插入图片描述
软件配置:
在这里插入图片描述
效果:
在这里插入图片描述
OK !
移植结束。
想深入了解FreeModbus源码,可以看我写的其笔记FreeModbus,写的不好,对源码理解上以及写的时候,难免有瑕疵纰漏,如有错误还请大佬指出。您原谅着瞧,原谅着看。

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

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

相关文章

数据结构之队列详解

1.队列的概念以及结构 队列&#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先进先出FIFo(Frist in Frist out)的特性 入队列&#xff1a;进行插入才操作的一端称为队尾 出队列&#xff1a;进行删除操作的一…

phpstudy下载使用以及搭建本地SQL labs靶场

一&#xff0c;PHP study 小皮面板(phpstudy) - 让天下没有难配的服务器环境&#xff01; (xp.cn) 1&#xff0c;下载。 根据自己电脑系统下载对应的版本。 双击exe文件运行 选择下载目录&#xff08;路径不能有中文名&#xff09;。 2&#xff0c;使用。 启动阿帕奇和MySQ…

Pytorch使用教学8-张量的科学运算

在介绍完PyTorch中的广播运算后&#xff0c;继续为大家介绍PyTorch的内置数学运算&#xff1a; 首先对内置函数有一个功能印象&#xff0c;知道它的存在&#xff0c;使用时再查具体怎么用其次&#xff0c;我还会介绍PyTorch科学运算的注意事项与一些实用小技巧 1 基本数学运算…

【python】PyQt5中QPushButton的用法详细解析与应用实战

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

【C/C++】printf和cout的区别

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…

JAVA基础 - 继承和多态

目录 一. 继承 二. 多态 三. 引用类型的转换 检查 向上转型&#xff08;Upcasting&#xff09; 向下转型&#xff08;Downcasting&#xff09; 四. final关键字 一. 继承 在 Java 中&#xff0c;继承&#xff08;Inheritance&#xff09;是面向对象编程的一个重要特性&a…

openEuler操作系统下Oracle 19c 从19.3补丁更新到19.17

Oracle 19c 从补丁19.3更新到19.17的过程涉及到多个步骤&#xff0c;包括备份、下载补丁、替换OPatch、验证清单信息、冲突检测、空间检测、应用补丁等。以下是一个概括性的流程&#xff0c;但请注意&#xff0c;具体步骤可能会根据实际的Oracle环境、补丁内容和Oracle的官方指…

基于python的BP神经网络红酒品质分类预测模型

1 导入必要的库 import pandas as pd import numpy as np import matplotlib.pyplot as plt from sklearn.model_selection import train_test_split from sklearn.preprocessing import LabelEncoder from tensorflow.keras.models import Sequential from tenso…

DataEase一键部署:轻松搭建数据可视化平台

DataEase是一个开源的数据可视化和分析工具&#xff0c;旨在帮助用户轻松创建和共享数据仪表盘。它支持多种数据源&#xff0c;包括关系型数据库&#xff0c;文件数据源&#xff0c;NoSQL数据库等&#xff0c;提供强大的数据查询、处理和可视化功能。DataEase 不仅是一款数据可…

oracle读写时相关字符集详解

服务器端操作系统&#xff08;Oracle linux&#xff09;字符集 服务器端数据库字符集 客户端操作系统&#xff08;Oracle linux&#xff09;字符集 客户端工具sqlplus字符集 结论1&#xff1a;客户端工具sqlplus的会话&#xff0c;使用的字符集&#xff0c;是数据库字符集。…

如何排查GD32 MCU复位是由哪个复位源导致的?

上期为大家讲解了GD32 MCU复位包括电源复位和系统复位&#xff0c;其中系统复位还包括独立看门狗复位、内核软复位、窗口看门狗复位等&#xff0c;在一个GD32系统中&#xff0c;如果莫名其妙产生了MCU复位&#xff0c;如何排查具体是由哪个复位源导致的呢&#xff1f; GD32 MC…

反激Flyback从逆向到初步设计(UC2844)

一.Flyback基本拓扑 国标gb/t 12325-2008《电能质量供电电压偏差》规定&#xff1a;220v单向供电电压偏差为标称电压的-10%&#xff0c;7%。 对应220V的标称电压&#xff0c;其浮动范围是在198~235.4V。以下运算均基于此规定进行。 首先220V进入EMI模块&#xff0c;消除差模干扰…

虚拟机配置RabbitMQ集群教程

RabbitMQ是常用的一款消息中间件&#xff0c;那么如何在我们虚拟机中创建其集群呢&#xff1f;跟着博主这篇文章让你一步到位 本篇搭建的是三台机器为一个集群&#xff01;假设大家虚拟机都为初始化状态&#xff0c;从0开始&#xff08;注意集群搭建需要CentOS8以上环境&#x…

老板电器发布首个烹饪AI模型,揭秘其如何引领厨电行业变革

数字发展日新月异&#xff0c;智慧产品迭代更新。当前&#xff0c;我们或许正身处一场连科学巨人也无法预见的深度变革之中。现代科技使得普通人无需深入学习数学或编程知识&#xff0c;也能借助手机或电脑&#xff0c;体验“苏格拉底式”的在线指导&#xff0c;或者与“乔布斯…

js 习题 1

文章目录 前言T1T2T3T4T5T6T7T8T9结语 前言 『最孤独的人最亲切&#xff0c;受过伤的人总是笑的最灿烂。』—— 「素媛」 T1 let buf""; process.stdin.on("readable",function(){let chunkprocess.stdin.read();if(chunk){bufchunk.toString();} });pr…

在英特尔 Gaudi 2 上加速蛋白质语言模型 ProtST

引言 蛋白质语言模型 (Protein Language Models, PLM) 已成为蛋白质结构与功能预测及设计的有力工具。在 2023 年国际机器学习会议 (ICML) 上&#xff0c;MILA 和英特尔实验室联合发布了ProtST模型&#xff0c;该模型是个可基于文本提示设计蛋白质的多模态模型。此后&#xff0…

uniapp中@click或者@tap多层嵌套的问题解决方法

我们在开发页面的过程中。例如要设计一个九宫格的相册&#xff0c;并且加上删除上传图片和点击图片后预览图片大图的功能例如下图的演示功能。 点击图片后显示大图预览图片&#xff0c;点击x号后要删除掉当前的图片&#xff0c;那么我们设计的时候如果我们代码写成如下的格式 …

【JavaScript】`Map` 数据结构

文章目录 一、Map 的基本概念二、常见操作三、与对象的对比四、实际应用场景 在现代 JavaScript 中&#xff0c;Map 是一种非常重要且强大的数据结构。与传统的对象&#xff08;Object&#xff09;不同&#xff0c;Map 允许您使用各种类型的值作为键&#xff0c;不限于字符串或…

jenkins自动化持续集成

一、持续集成优势 1.1 解放重复劳动 一次设置&#xff0c;多次复用。持续集成任务可以解放集成、测试、部署等重复性劳动&#xff0c;通过自动化任务能够显著提升集成频率。 1.2 更快解决问题 接入持续集成任务后&#xff0c;能够更早地感知变更后效果&#xff0c;及时进入…

『 Linux 』信号的写入与保存

文章目录 信号的发送信号的保存sigset_t 类型与信号集操作函数阻塞信号集(信号屏蔽字)操作函数未决信号集操作函数验证阻塞信号集与未决信号集 信号的发送 $ kill -l1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10)…