49_IAP实验

news2024/9/27 23:33:46

目录

IAP介绍

一般的程序执行流程

IAP程序执行流程

实验源码:


IAP介绍

STM32编程方式:

1.在线编程(ICP,In-Circuit Programming):通过JTAG/SWD协议或者系统加载程序(Bootloader)下载用户应用程序到微控制器中。

2.在程序中编程(IAP,In Application Programming):通过任何一种通信接口(如IO端口,USB,CAN,UART,I2C,SPI等)下载程序或者应用数据到存储器中。也就是说,TM32允许用户在应用程序中重新烧写闪存存储器中的内容。然而,IAP需要至少有一部分程序已经使用

ICP方式烧到闪存存储器中(Bootloader)。在不需要操作硬件平台的情况下实现升级(远程)。

每种STM32芯片(MO,M3,M4),它们的主存储器结构可能不一样,但是他们都有一个叫“系统存储器”的区域,此区域是留给ST自己用来存放芯片的bootloader程序,此程序在芯片出厂的时候已经固化在芯片内部。

系统存储器的Bootloader程序会通过串口1接受应用程序。

 系统存储器:只留给ST用来写启动程序代码代码。启动程序代码通过串口1接口实现对闪存存储器的编程。

STM32启动模式选择

 ICP下载流程

 B0接1,系统存储器被选为启动区域。启动代码从串口1接受程序,从地址0x08000000开始写入。JTAG/SWD下载,直接下载到FLASH指定区域。

IAP下载流程

一般的程序执行流程

STM32的内部闪存(FLASH)地址起始于0x08000000,般情况下,程序文件就从此地址开始写入。

0x08000004开始存放中断向量表。

当中断来临,STM32的内部硬件机制亦会自动将PC指针定位到“中断向量表”处,并根据中断源取出对应的中断向量执行中断服务程序。

1.STM32复位后,从0X08000004地址取出复位中断向量的地址,并跳转到复位中断服务程序。

2.在复位中断服务程序执行完之后,会跳转到我们的main函数。

3.main函数执行过程中,如果收到中断请求(发生重中断)此时STM32强制将PC指针指回中断向量表处。

4.根据中断源进入相应的中断服务程序。

5.在执行完中断服务程序以后,程序再次返回main函数执行

IAP程序执行流程

1.STM32复位后,还是从0x08000004地址取出复位中断向量的地址,并跳转到复位中断服务程序,在运行完复位中断服务程序之后跳转到IAP的main函数。

2.在执行完IAP以后(即将新的APP代码写入STM32的FLASH,灰底部分。新程序的复位中断向量起始地址为0x08000004+N+M) ,跳转至新写入程序的复位向量表,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至新程序的main函数,如图标号②和③所示。

3.在main函数执行过程中,如果CPU得到一个中断请求,PC指针仍强制跳转到地址0x08000004中断向量表处,而不是新程序的中断向量表

4.程序再根据我们设置的中断向量表偏移量,跳转到对应中断源新的中断服务程序中。

5.在执行完中断服务程序后,程序返回main函数继续运行。

实验源码:

只是简单的通过串口发送bin文件,然后按下按钮写入内部Flash里,在跳转,项目上是需要复杂一些,通过某个命令跳转到Boot里然后再请求总包数,然后请求第1包数据,在校验再写入Flash里面,写完请求第2包,直到数据发完,写完后在进行跳转。

/**
  ******************************************************************************
  * @file           : user_gpio.c
  * @brief          : V1.00
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */

/* Include 包含---------------------------------------------------------------*/
#include "user_gpio.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define  定义----------------------------------------------------------------*/
/* Macro   宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/
/* Constants 常量--------------------------------------------------------------*/
/* Function  函数--------------------------------------------------------------*/

/*!
	\brief		GPIO初始化函数
	\param[in]	none
	\param[out]	none
	\retval 	none
*/
void Gpio_Init(void)
{	
	/*GPIO结构体*/
	GPIO_InitTypeDef GPIO_InitTypeDefstruct;
	
	/*UART1发送引脚配置*/
	GPIO_InitTypeDefstruct.GPIO_Mode  = GPIO_Mode_AF_PP;//推挽复用输出
	GPIO_InitTypeDefstruct.GPIO_Pin   = GPIO_Pin_9;
	GPIO_InitTypeDefstruct.GPIO_Speed =	GPIO_Speed_10MHz;
	/*写入结构体到GPIOA*/
	GPIO_Init(GPIOA,&GPIO_InitTypeDefstruct);
	
	/*UART1接收引脚配置*/
	GPIO_InitTypeDefstruct.GPIO_Mode  = GPIO_Mode_IN_FLOATING;//浮空输入
	GPIO_InitTypeDefstruct.GPIO_Pin   = GPIO_Pin_10;
	GPIO_InitTypeDefstruct.GPIO_Speed =	GPIO_Speed_10MHz;
	/*写入结构体到GPIOA*/	
	GPIO_Init(GPIOA,&GPIO_InitTypeDefstruct);
	
	/*配置按键*/
	GPIO_InitTypeDefstruct.GPIO_Pin  = GPIO_Pin_0;
	GPIO_InitTypeDefstruct.GPIO_Mode = GPIO_Mode_IPD; //PA0设置成输入,默认下拉	  
	GPIO_Init(GPIOA, &GPIO_InitTypeDefstruct);//初始化GPIOA.0	
}

/************************************************************** END OF FILE ****/
 
/**
  ******************************************************************************
  * @file           : user_rcc_config.c
  * @brief          : V1.00
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */

/* Include 包含---------------------------------------------------------------*/
#include "user_rcc_config.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define  定义----------------------------------------------------------------*/
/* Macro   宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/
/* Constants 常量--------------------------------------------------------------*/
/* Function  函数--------------------------------------------------------------*/

/*!
	\brief		RCC配置
	\param[in]	none
	\param[out]	none
	\retval 	none
*/
void Rcc_config(void)
{	
	/*使能GPIOA时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	/*使能UART1时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);

}

/************************************************************** END OF FILE ****/
 
/**
  ******************************************************************************
  * @file           : user_uart.c
  * @brief          : V1.00
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */

/* Include 包含---------------------------------------------------------------*/
#include "user_uart.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define  定义----------------------------------------------------------------*/
/* Macro   宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/
uint16_t USART_RX_CNT=0;
uint8_t USART_RX_BUF[USART_REC_LEN];

/* Constants 常量--------------------------------------------------------------*/
/* Function  函数--------------------------------------------------------------*/
#if 1
#pragma import(__use_no_semihosting)  
/*实现Printf代码*/
struct __FILE 
{ 
	int handle; 

}; 
FILE __stdout;       

void _sys_exit(int x) 
{ 
	x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{      
	while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
    USART1->DR = (u8) ch;      
	return ch;
}
#endif 




/*!
	\brief		UART1初始化
	\param[in]	none
	\param[out]	none
	\retval 	none
*/

void Uart1_Init(u32 bound)
{
	/*UART结构体*/
	USART_InitTypeDef USART_InitTypeDefstruct;
	/*NVIC结构体*/
	NVIC_InitTypeDef NVIC_InitStructure;
	
	/*中断控制器配置*/
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;	//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;	//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器
	
	
	
	/*UART结构体配置*/
	USART_InitTypeDefstruct.USART_BaudRate = bound; //波特率
	USART_InitTypeDefstruct.USART_HardwareFlowControl =USART_HardwareFlowControl_None; //不使用硬件流
	USART_InitTypeDefstruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//发送接收使能
	USART_InitTypeDefstruct.USART_Parity = USART_Parity_No; //不使用奇偶校验
	USART_InitTypeDefstruct.USART_StopBits = USART_StopBits_1; //1个停止位
	USART_InitTypeDefstruct.USART_WordLength = USART_WordLength_8b; //8个数据位
	/*写入USART1*/
	USART_Init(USART1,&USART_InitTypeDefstruct);
	/*使能中断*/
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	/*使能串口1*/
	USART_Cmd(USART1,ENABLE);

}


/*!
	\brief		UART1中断服务函数
	\param[in]	none
	\param[out]	none
	\retval 	none
*/

void USART1_IRQHandler(void)
{
	uint8_t Receive;
	/*判断是否是接收缓冲区非空中断标志位置位*/
	if(USART_GetFlagStatus(USART1,USART_IT_RXNE))
	{
		/*接收数据*/
		Receive = USART_ReceiveData(USART1);
		
		/*最多一次接收55K字节*/
		if(USART_RX_CNT<USART_REC_LEN)
		{
			USART_RX_BUF[USART_RX_CNT]=Receive;
			USART_RX_CNT++;			 				
		}
	}
	
}
	

/************************************************************** END OF FILE ****/
 
/**
  ******************************************************************************
  * @file           : user_flash.h
  * @brief          : V1.00
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */

/* Define to prevent recursive incluson---------------------------------------*/
#ifndef _USER_FLASH_H__
#define _USER_FLASH_H__

/* Include 包含---------------------------------------------------------------*/
#include "stm32f10x.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define  定义----------------------------------------------------------------*/
/* Macro   宏------------------------------------------------------------------*/
/*FLASH的起始地址*/
#define STM32_FLASH_BASE 0x08000000 
/*所选STM32的FLASH容量大小(单位为K)*/
#define STM32_FLASH_SIZE 512
/*扇区字节数*/
#define STM_SECTOR_SIZE	2048
/* Variables 变量--------------------------------------------------------------*/
/* Constants 常量--------------------------------------------------------------*/
/* Function  函数--------------------------------------------------------------*/
void IAP_Load_App(u32 appxaddr);
void IAP_Write_Appbin(u32 appxaddr,u8 *appbuf,u32 appsize);


#endif

/************************************************************** END OF FILE ****/
/**
  ******************************************************************************
  * @file           : user_flash.c
  * @brief          : V1.00
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */

/* Include 包含---------------------------------------------------------------*/
#include "user_Qflash.h"
/* Typedef 类型----------------------------------------------------------------*/
/*函数指针*/
typedef  void (*iapfun)(void);		

/* Define  定义----------------------------------------------------------------*/
/* Macro   宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/
/*IAP一页Buf*/
uint16_t iapbuf[1024];
iapfun jump2app; 
/*最多是2K字节*/
uint16_t STMFLASH_BUF[STM_SECTOR_SIZE/2];
/* Constants 常量--------------------------------------------------------------*/
/* Function  函数--------------------------------------------------------------*/



/*!
	\brief		写一页地址
	\param[in]	起始地址
	\param[in]	数据指针
	\param[in]	半字(16位)数
	\retval 	none
*/
void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)   
{ 			 		 
	u16 i;
	for(i=0;i<NumToWrite;i++)
	{
		FLASH_ProgramHalfWord(WriteAddr,pBuffer[i]);
	    WriteAddr+=2;//地址增加2.
	}  
}


/*!
	\brief		写Flash函数
	\param[in]	写地址
	\param[in]	写BUFF
	\param[in]	写长度字节单位
	\retval 	none
*/
void Flash_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
{
	uint32_t secpos;	   //扇区地址
	uint16_t secoff;	   //扇区内偏移地址(16位字计算)
	uint16_t secremain;    //扇区内剩余地址(16位字计算)	   
 	uint16_t i = 0;    
	uint32_t offaddr;      //去掉0X08000000后的地址
	if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))
	{
		return;//非法地址
	}
	/*解锁*/
	FLASH_Unlock();
	/*实际偏移地址*/
	offaddr=WriteAddr-STM32_FLASH_BASE;
	/*扇区地址  0~127 for STM32F103RBT6*/
	secpos=offaddr/STM_SECTOR_SIZE;	
	/*在扇区内的偏移(2个字节为基本单位.)*/
	secoff=(offaddr%STM_SECTOR_SIZE)/2;
	/*扇区剩余空间大小*/
	secremain=STM_SECTOR_SIZE/2-secoff;
	/*不大于该扇区范围*/
	if(NumToWrite<=secremain)
	{
		secremain=NumToWrite;
	}
	while(1)
	{
		/*擦除扇区*/
		if(i<secremain)
		{
			/*擦除这个扇区*/
			FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);
			/*复制待写入数据2k*/
			for(i=0;i<secremain;i++)
			{
				STMFLASH_BUF[i+secoff]=pBuffer[i];	  
			}
			/*写入Flash里面*/
			STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);
		}
		/*写入结束*/
		if(NumToWrite==secremain)
		{
			break;
		}
		else
		{
			secpos++;				//扇区地址增1
			secoff=0;				//偏移位置为0 	 
		   	pBuffer+=secremain;  	//指针偏移
			WriteAddr+=secremain;	//写地址偏移	   
		   	NumToWrite-=secremain;	//字节(16位)数递减
			
			if(NumToWrite>(STM_SECTOR_SIZE/2))
			{	
				/*下一个扇区还是写不完*/
				secremain=STM_SECTOR_SIZE/2;
			}
			else
			{	/*下一个扇区可以写完了*/
				secremain=NumToWrite;
			}			
		}		
	}
	/*上锁*/
	FLASH_Lock();	
}

/*!
	\brief		IAP写入函数
	\param[in]	应用程序的起始地址
	\param[in]	应用程序CODE.
	\param[in]	写长度字节单位
	\param[in]	应用程序大小(字节)
	\retval 	none
*/
void IAP_Write_Appbin(u32 appxaddr,u8 *appbuf,u32 appsize)
{
	u16 t;
	u16 i=0;
	u16 temp;
	u32 fwaddr=appxaddr;//当前写入的地址
	u8 *dfu=appbuf;
	for(t=0;t<appsize;t+=2)
	{						    
		temp=(u16)dfu[1]<<8;
		temp+=(u16)dfu[0];	  
		dfu+=2;//偏移2个字节
		iapbuf[i++]=temp;	    
		if(i==1024)
		{
			i=0;
			Flash_Write(fwaddr,iapbuf,1024);	
			fwaddr+=2048;//偏移2048  16=2*8.所以要乘以2.
		}
	}
	/*将最后的一些内容字节写进去*/
	if(i)
	{
		Flash_Write(fwaddr,iapbuf,i);
	}

}

/*!
	\brief		跳转函数
	\param[in]	跳转地址
	\param[in]	none
	\retval 	none
*/
void IAP_Load_App(u32 appxaddr)
{
		jump2app=(iapfun)*(vu32*)(appxaddr+4);		//用户代码区第二个字为程序开始地址(复位地址)		
		MSR_MSP(*(vu32*)appxaddr);					//初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
		jump2app();									//跳转到APP.
	
}
/************************************************************** END OF FILE ****/
 
/**
  ******************************************************************************
  * @file           : user_mian.h
  * @brief          : V1.00
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */

/* Include 包含---------------------------------------------------------------*/
#include "stm32f10x.h"
#include <stdbool.h>
#include "user_gpio.h"
#include "user_delay.h"
#include "user_rcc_config.h"
#include "user_uart.h"
#include "user_flash.h"


/* Typedef 类型----------------------------------------------------------------*/
/* Define  定义----------------------------------------------------------------*/
/* Macro   宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/ 
extern uint8_t USART_RX_BUF[USART_REC_LEN];
extern uint16_t USART_RX_CNT;
/* Constants 常量--------------------------------------------------------------*/
/* Function  函数--------------------------------------------------------------*/


 int main(void)
 {	

	/*配置系统中断分组为2位抢占2位响应*/
	 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	 /*延时函数初始化*/
	 delay_init();
	/*RCC配置*/
	 Rcc_config();
	/*GPIO初始化*/ 
	 Gpio_Init();
	/*USART1初始化*/
	 Uart1_Init(9600);
	/*死循环*/ 
	 while(1){
		 printf("Boot\r\n");
		 delay_ms(1000);
		 /*写入并跳转*/
		if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0))
		{		
			/*关闭中断*/
			USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);	
			/*写入Flash*/
			IAP_Write_Appbin(0x08006000,USART_RX_BUF,USART_RX_CNT);
			/*跳转*/
			IAP_Load_App(0x08006000);
			
		}

	}
		

}
 
 /************************************************************** END OF FILE ****/

 

 

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

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

相关文章

MySQL复制底层技术——LOGICAL_CLOCK并行复制、WRITESET并行复制

1. LOGICAL_CLOCK 并行复制 1&#xff0e;原理 从MySQL 5.7版本开始&#xff0c;支持LOGICAL_CLOCK级别的并行复制&#xff08;基于MySQL 5.6的库级别的Group Commit并行复制的大幅改进&#xff09;&#xff0c;通过设置参数slave_parallel_type为LOGICAL_CLOCK来启用&#x…

蓝桥杯STM32G431RBT6学习——工程建立

蓝桥杯STM32G431RBT6学习——工程建立 前言 我始终认为一边学习一边记录是最好的方式&#xff0c;一方面便知识的梳理&#xff0c;另一方面便于二次复习使用。最后一次参加蓝桥杯&#xff0c;希望能整个不错的成绩。–2023/1/4 准备工具 1、keil5 2、STM32G系列芯片包 3、S…

PostgreSQL在openEuler上本地yum不能安装而网络yum却可以的原因分析

作者&#xff1a;IT圈黎俊杰 PostgreSQL是国外著名的开源数据库&#xff0c;openEuler是国内的开源操作系统品牌&#xff0c;当前PostgreSQL对openEuler是没有适配认证的&#xff0c;PostgreSQL官方更不存在提供可直接在openEuler操作系统上安装的RPM软件包了。 在PostgreSQL官…

56、mysql的作业

作业中要用到的两张表dept&#xff0c;emp&#xff0c;找不到原来的sql语句了&#xff0c;所以只有表的内容 1、选择题 &#xff08;1&#xff09;以下哪条语句是错误的&#xff1f; A. SELECT empno, ename name, sal salary FROM emp; B. SELECT empno, ename name, sal AS s…

基于Slimming的MobileNetV3半自动剪枝

本文尝试两种剪枝方法&#xff0c;分别是直接使用NNI工具 以及 通过Slimming方法进行硬编码。 1、剪枝的意义 深度模型落地需要权衡两个核心问题&#xff1a;精度和复杂度。 模型压缩加速&#xff0c;在保持精度基本不变、降低模型计算复杂度。一方面提升模型落地的可能性&a…

SpringBoot+Vue项目毕业论文管理系统

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7/8.0 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.9 浏…

使用PowerShell获取Trustedinstaller权限

准备工作: Windows Powershell 版本为 5.0 以上 以管理员身份运行Windows Powershell(在这里可能会遇到运行X64版本而导致后续有些命令无法使用, 所以可以运行X86版本) 安装所需模块 首先在C盘(系统盘)根目录&#xff0c;新建名为 token 的文件夹 接着在Windows Powershell输…

html5语义元素

HTML5 提供了新的语义元素来明确一个Web页面的不同部分: <header><nav><section><article><aside><figcaption><figure><footer><header> <header>元素描述了文档的头部区域&#xff0c;<header>元素主要…

如何停用Sidekiq Uniqueness

背景是这样的&#xff1a; 你有一些Sidekiq的job,并且设置了unique_for&#xff0c;然后系统崩溃了&#xff0c;在你的redis里仍然有这个唯一性的锁&#xff0c;但是job却不见了&#xff0c;导致后面的job也进不来&#xff0c;这个时候需要手动disable这个唯一锁。 官网传送&…

骨传导耳机会伤害耳朵吗、佩戴骨传导耳机有什么好处

首先要明确的告诉大家耳机都对听力/耳朵是一定会有伤害&#xff0c;只不过是程度如何而已。目前来说&#xff0c;同等音量的情况下&#xff0c;对听力损伤最低的就是骨传导耳机。因为它是避开了脆弱的鼓膜&#xff0c;经过骨头向听神经传播声音&#xff0c;这种方法解放了双耳&…

Jvm知识点一(jvm 组成部分)

jvm 主要组成部分以及其作用 jvm 组成部分&#xff08;包含两个子系统和两个组件&#xff09; 两个子系统 Class Loader (类装载器)&#xff1a;根据给定的全限定名类名&#xff08;如java.lang.Object&#xff09;来装载class 文件到Runtime data area(运行时数据区) 中的me…

excel制表基础:规范的制表理念和思路让效率提升 上篇

今天的文章是一篇理论文章&#xff0c;没有函数技巧讲解&#xff0c;没有酷炫图表实例&#xff0c;也没有神奇的VBA教程&#xff0c;如果你想学技巧&#xff0c;那这篇文章不适合你的要求。但是如果你想让自己在使用Excel方面更加地得心应手&#xff0c;能够让构建的表格提高我…

骨传导耳机哪个品牌好?国产骨传导蓝牙耳机排名

骨传导耳机传播声音不用经过耳道的设计&#xff0c;具有保护听力以及佩戴更安全的优势&#xff0c;受到越来越多人的青睐。骨传导耳机的品牌也是越来越多&#xff0c;那么哪些骨传导耳机品牌是最好的呢&#xff1f;下面就给大家分享一下几款好用的骨传导耳机。 1、南卡Runner …

超实用的跨境工具、软件分享!跨境人看过来!

对于跨境电商商家来说&#xff0c;一些常用的工具和网站是必不可少的。实用的工具可以帮助我们选择产品&#xff0c;提高工作效率&#xff0c;规划关键词等等。今天东哥给大家分享几类工具&#xff01; 语言翻译工具 1、deepL 如果还有跨境人在为小语种翻译苦恼的话&#xff0c…

JAVAEE-多线程(2)

线程安全 线程安全的意思技术在多线程的各种随机调度顺序下&#xff0c;代码没有bug,都能够符合预期的方式来执行 线程为什么会不安全&#xff1f;就是在多线程随机调度下出代码出现bug。 有些代码在多线程环境下执行会出现bug&#xff0c;这样的问题就叫做线程不安全。 /*…

海思嵌入式开发-004-Hi3516烧录问题总结

烧录Hi3516DV300小型系统 问题总结一、前言二、使用USB进行烧录① 前提条件② 操作步骤三、运行镜像文件一、前言 Hi3516DV300的镜像烧录通过Winodow环境进行烧录&#xff0c;开发者启动烧录操作后&#xff0c;DevEco Device Tool通过Remote远程模式&#xff0c;将Ubuntu环境下…

罗胖时间的朋友跨年演讲释放的世界氢燃料电池汽车信号

罗胖时间的朋友2022跨年演讲中关于《故乡与魔法》中讲述了关于氢能源相关的问题&#xff0c;日本在2013年就把发展氢能源定为了国策&#xff0c;在日本&#xff0c;关于氢能源相关的专利占世界30%左右&#xff0c;为什么他们就搞不起来&#xff0c;这是个很好的问题&#xff0c…

04-微服务客户端负载均衡器Ribbon、LoadBalance实战

什么是Ribbon 目前主流的负载方案分为以下两种&#xff1a; 集中式负载均衡&#xff0c;在消费者和服务提供方中间使用独立的代理方式进行负载&#xff0c;有硬件的&#xff08;比如F5&#xff09;&#xff0c;也有软件的&#xff08;比如 Nginx&#xff09;。客户端根据自己的…

同样是血氧仪方案,价格怎么相差这么多?

有没有发现最近血氧仪、额温枪、壁挂式测温仪又开始火了&#xff1f;并且市场活跃度越来越高。而作为我们血氧仪方案提供商或者生产企业来说&#xff0c;您是不是和优优一样会时常听到客户发出如下反馈&#xff1a; “我刚问了另外一家&#xff0c;和你这个样子差不多的&#…

智云通CRM:如何应对“我们还要再考虑一下”?

在征求决策层对报价问题的反馈信息时&#xff0c;销售经常会遇到的另一种意见&#xff08;群组决策中最常见的意见&#xff09;是推迟做出决策。这种情况显然是可以理解的&#xff0c;内部决策层认为有必要先私下讨论一番&#xff0c;然后再对你的报价做出答复。毕竟&#xff0…