RS485收发架构移植要点

1、串口使用发送和接收DMA(会出现运行一段时间后无法接收,只有重新初始化串口)

原因:产生了ORE中断却没有清除

STM32 的 UART 外设,触发 ORE 的唯一硬件条件是:

接收数据寄存器(RDR)或 RX FIFO 里已经有数据了,新的一帧数据又接收完成,旧数据还没被读走。此时旧数据会被新数据覆盖,硬件置位ORE标志,如果中断使能了就会触发错误中断。

解决:(我选择了方式二)

**方式一:**开启串口全局中断,并且该中断优先级低于串口DMA

初始化串口时:

// 使能串口错误中断:溢出错误、帧错误、噪声错误、奇偶错误

// LL_USART_EnableIT_ERROR(UART8);

// NVIC_SetPriority(UART8_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),4, 0));

// NVIC_EnableIRQ(UART8_IRQn);

中断里面处理ORE:

c 复制代码
	if(LL_USART_IsActiveFlag_ORE(UART8) == SET)
	{
		LL_USART_ClearFlag_ORE(UART8);		//在重新配置串口接收时,总线上有数据,则会出现该错误
		// 1. 关闭 DMA Stream 和串口 DMA 请求
		LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_1);
		while(LL_DMA_IsEnabledStream(DMA1, LL_DMA_STREAM_1));
		UART8->CR3 &= ~USART_CR3_DMAR;

		// 2. 重新配置 DMA 传输长度
		LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_1, MAX485LEN);
		// 3. 重新使能 DMA Stream 和串口 DMA 请求
		LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_1);
		UART8->CR3 |= USART_CR3_DMAR;

		// 4. 重置状态机和标志位
		RS485.RxDMAFlag = 0x1;
		RS485.DMA_RecTick = SYS_TICK;
		RS485.Status = RS485_WAIT_REC_FIRST_BYTE;
	}

方式二:

串口初始化时,直接关闭ORE功能

SET_BIT(UART8->CR3, USART_CR3_OVRDIS); //串口使能前调用才生效

c 复制代码
//UART8_TX DMA
void DMA1_Stream2_IRQHandler(void)
{
	if(LL_DMA_IsActiveFlag_TC2(DMA1))					//传输完成中断    
	{
		LL_DMA_ClearFlag_TC2(DMA1);               		//清除传输完成中断
		while(LL_USART_IsActiveFlag_TC(UART8) != SET);	//等待串口发送完成
		LL_USART_DisableDMAReq_TX(UART8);				//失能串口DMA控制器
		RS485_CTR_L;								//RS485设为接收模式
		RS485.TxDMAFlag = 1;
		RS485.Status = RS485_WAIT_REC_FIRST_BYTE;		//等待接收第一个字节
		
		//...配置串口接收DMA
		if(!RS485.RxDMAFlag & 0x1)
		{
			LL_DMA_DisableStream(DMA1,LL_DMA_STREAM_1);			//关闭接收DMA传输
			while(LL_DMA_IsEnabledStream(DMA1,LL_DMA_STREAM_1));	//确保DMA1_Stream1可以被设置
			LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_1, (uint32_t)MAX485LEN);	//设置传输长度
			LL_DMA_EnableStream(DMA1,LL_DMA_STREAM_1);			//使能接收DMA数据流通道
			UART8->CR1 |= (1 << 2);  			//串口接收使能 防止运行久了后串口接收死机
			UART8->CR3 |= (1 << 6);				//使能串口DMA接收
			RS485.RxDMAFlag = 0x1;				//标记已开启接收DMA
			RS485.DMA_RecTick = SYS_TICK;
		}		
		//...
		
	}
	if(LL_DMA_IsActiveFlag_TE2(DMA1))          			//传输错误中断    
	{
		LL_DMA_ClearFlag_TE2(DMA1);   
		while(LL_USART_IsActiveFlag_TC(UART8) != SET);	//等待串口DMA发送完成
		LL_USART_DisableDMAReq_TX(UART8);				//关闭串口DMA发送
		RS485.Status = RS485_INIT;
	}
}



void UART8_IRQHandler(void)
{
	//...产生以下错误中断	
	if(LL_USART_IsActiveFlag_PE(UART8) == SET)
	{
		LL_USART_ClearFlag_PE(UART8);
	}
	if(LL_USART_IsActiveFlag_FE(UART8) == SET)
	{
		LL_USART_ClearFlag_FE(UART8);
	}
	if(LL_USART_IsActiveFlag_NE(UART8) == SET)
	{
		LL_USART_ClearFlag_NE(UART8);
	}
	if(LL_USART_IsActiveFlag_ORE(UART8) == SET)
	{
		LL_USART_ClearFlag_ORE(UART8);		//在重新配置串口接收时,总线上有数据,则会出现该错误
		// 1. 关闭 DMA Stream 和串口 DMA 请求
		LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_1);
		while(LL_DMA_IsEnabledStream(DMA1, LL_DMA_STREAM_1));
		UART8->CR3 &= ~USART_CR3_DMAR;

		// 2. 重新配置 DMA 传输长度
		LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_1, MAX485LEN);
		// 3. 重新使能 DMA Stream 和串口 DMA 请求
		LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_1);
		UART8->CR3 |= USART_CR3_DMAR;

		// 4. 重置状态机和标志位
		RS485.RxDMAFlag = 0x1;
		RS485.DMA_RecTick = SYS_TICK;
		RS485.Status = RS485_WAIT_REC_FIRST_BYTE;
	}
}	

/* UART8 init function */
void MX_UART8_Init(void)
{

  /* USER CODE BEGIN UART8_Init 0 */

  /* USER CODE END UART8_Init 0 */

  LL_USART_InitTypeDef UART_InitStruct = {0};

  LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};

  /** Initializes the peripherals clock
  */
  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_UART8;
  PeriphClkInitStruct.Usart234578ClockSelection = RCC_USART234578CLKSOURCE_D2PCLK1;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /* Peripheral clock enable */
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_UART8);
  //...新增复位串口时钟
  /* Force reset of UART clock */
  LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_UART8);
  /* Release reset of UART clock */
  LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_UART8);
  //

  LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOE);
  /**UART8 GPIO Configuration
  PE0   ------> UART8_RX
  PE1   ------> UART8_TX
  */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_0|LL_GPIO_PIN_1;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_8;
  LL_GPIO_Init(GPIOE, &GPIO_InitStruct);

  /* UART8 DMA Init */

  /* UART8_RX Init */
  
  	//接收DMA配置
	LL_DMA_DisableStream(DMA1,LL_DMA_STREAM_1);			//关闭DMA传输
	while(LL_DMA_IsEnabledStream(DMA1,LL_DMA_STREAM_1));	//确保DMA1_Stream1可以被设置
	LL_DMA_SetPeriphAddress(DMA1, LL_DMA_STREAM_1, (uint32_t)&UART8->RDR);		//串口接收寄存器
	LL_DMA_SetMemoryAddress(DMA1, LL_DMA_STREAM_1, (uint32_t)&RS485.RxBuf);		//内存地址
	LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_1, (uint32_t)MAX485LEN);			//传输长度
  LL_DMA_SetPeriphRequest(DMA1, LL_DMA_STREAM_1, LL_DMAMUX1_REQ_UART8_RX);

  LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_STREAM_1, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);

  LL_DMA_SetStreamPriorityLevel(DMA1, LL_DMA_STREAM_1, LL_DMA_PRIORITY_LOW);

  LL_DMA_SetMode(DMA1, LL_DMA_STREAM_1, LL_DMA_MODE_NORMAL);

  LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_STREAM_1, LL_DMA_PERIPH_NOINCREMENT);

  LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_STREAM_1, LL_DMA_MEMORY_INCREMENT);

  LL_DMA_SetPeriphSize(DMA1, LL_DMA_STREAM_1, LL_DMA_PDATAALIGN_BYTE);

  LL_DMA_SetMemorySize(DMA1, LL_DMA_STREAM_1, LL_DMA_MDATAALIGN_BYTE);

  LL_DMA_DisableFifoMode(DMA1, LL_DMA_STREAM_1);

  /* UART8_TX Init */
  	//发送DMA配置
	LL_DMA_DisableStream(DMA1,LL_DMA_STREAM_2);			//关闭DMA传输
	while(LL_DMA_IsEnabledStream(DMA1,LL_DMA_STREAM_2));					//确保DMA1_Stream2可以被设置
	LL_DMA_SetPeriphAddress(DMA1, LL_DMA_STREAM_2, (uint32_t)&UART8->TDR);	//串口发送寄存器
	LL_DMA_SetMemoryAddress(DMA1, LL_DMA_STREAM_2, (uint32_t)&RS485.TxBuf);	//内存地址
	LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_2, (uint32_t)MAX485LEN);		//传输长度
  LL_DMA_SetPeriphRequest(DMA1, LL_DMA_STREAM_2, LL_DMAMUX1_REQ_UART8_TX);

  LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_STREAM_2, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);

  LL_DMA_SetStreamPriorityLevel(DMA1, LL_DMA_STREAM_2, LL_DMA_PRIORITY_LOW);

  LL_DMA_SetMode(DMA1, LL_DMA_STREAM_2, LL_DMA_MODE_NORMAL);

  LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_STREAM_2, LL_DMA_PERIPH_NOINCREMENT);

  LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_STREAM_2, LL_DMA_MEMORY_INCREMENT);

  LL_DMA_SetPeriphSize(DMA1, LL_DMA_STREAM_2, LL_DMA_PDATAALIGN_BYTE);

  LL_DMA_SetMemorySize(DMA1, LL_DMA_STREAM_2, LL_DMA_MDATAALIGN_BYTE);

  LL_DMA_DisableFifoMode(DMA1, LL_DMA_STREAM_2);

  /* USER CODE BEGIN UART8_Init 1 */
	
	//接收DMA中断配置
	LL_DMA_EnableIT_TC(DMA1, LL_DMA_STREAM_1);			//开启DMA传输完成中断
	LL_DMA_EnableIT_TE(DMA1, LL_DMA_STREAM_1);			//开启DMA传输错误中断
//	LL_DMA_EnableStream(DMA1,LL_DMA_STREAM_1);			//使能数据流
//	LL_USART_EnableDMAReq_RX(UART8);					//开启串口dma接收
	//发送DMA中断配置
	LL_DMA_EnableIT_TC(DMA1, LL_DMA_STREAM_2);			//开启DMA传输完成中断
	LL_DMA_EnableIT_TE(DMA1, LL_DMA_STREAM_2);			//开启DMA传输错误中断
	
  /* USER CODE END UART8_Init 1 */
  UART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1;
  UART_InitStruct.BaudRate = 4800;
  UART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
  UART_InitStruct.StopBits = LL_USART_STOPBITS_1;
  UART_InitStruct.Parity = LL_USART_PARITY_NONE;
  UART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
  UART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
  UART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
  LL_USART_Init(UART8, &UART_InitStruct);
  LL_USART_DisableFIFO(UART8);
  LL_USART_SetTXFIFOThreshold(UART8, LL_USART_FIFOTHRESHOLD_1_8);
  LL_USART_SetRXFIFOThreshold(UART8, LL_USART_FIFOTHRESHOLD_1_8);
  LL_USART_ConfigAsyncMode(UART8);

  /* USER CODE BEGIN WKUPType UART8 */

  /* USER CODE END WKUPType UART8 */
// 使能串口错误中断:溢出错误、帧错误、噪声错误、奇偶错误
//	LL_USART_EnableIT_ERROR(UART8);
//	NVIC_SetPriority(UART8_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),4, 0));
//	NVIC_EnableIRQ(UART8_IRQn);
  
	// 【终极预防】开启溢出禁用,溢出时仅丢弃新数据,不置位ORE标志、不卡死接收状态机
	SET_BIT(UART8->CR3, USART_CR3_OVRDIS);	//串口使能前调用才生效
  LL_USART_Enable(UART8);

  /* Polling UART8 initialisation */
  while((!(LL_USART_IsActiveFlag_TEACK(UART8))) || (!(LL_USART_IsActiveFlag_REACK(UART8))))
  {
  }
  /* USER CODE BEGIN UART8_Init 2 */
  /* USER CODE END UART8_Init 2 */

}
c 复制代码
//...DMA长度判断中断逻辑
void TIM15_IRQHandler(void)
{
	
	if(LL_TIM_IsActiveFlag_UPDATE(TIM15) != RESET)
	{
		//...UART8 DMA接收长度判断
		if(RS485.Status == RS485_WAIT_REC_FIRST_BYTE)
		{
			if(DMA1_Stream1->NDTR != MAX485LEN)//接收到了第一个字节
			{
				RS485.DMA_RecTick = SYS_TICK;
				RS485.Status = RS485_REC;	//跳转至接收状态
			}
		}
		if(RS485.Status == RS485_REC)
		{
			if(RS485.DMA_RecCount >= 2)
			{
				RS485.DMA_RecCount = 0;
			}
			RS485.DMA_RecLen[RS485.DMA_RecCount++] = DMA1_Stream1->NDTR;	//获取DMA接收的长度
			if(RS485.DMA_RecLen[0] != RS485.DMA_RecLen[1])
			{
				RS485.DMA_RecTick = SYS_TICK;
			}
		}
		//...UART4 DMA接收长度判断
	}
		
	
	LL_TIM_ClearFlag_UPDATE(TIM15);
}
//...

void RS485Pro(void)
{
	uint8_t RxFlag;
	uint16_t rec_len,k,DMARecTimeout = DMA_REC_TIMEOUT;
	
	switch(RS485.Status)
     {
     case RS485_INIT:
		MX_UART8_Init();
		#ifdef DEBUG_WIND
		RS485.TxDMAFlag = 1;
		RS485.Status = RS485_DEBUG;
		#else
		RS485_CTR_L;
          RS485.Status = RS485_WAIT_REC_FIRST_BYTE;		//状态变更为等待接收第一字节
          RS485.Tick = SYS_TICK;
		RS485.RxDMAFlag = 0;
		RS485.TxDMAFlag = 1;
		//...打开DMA接收
		if((RS485.RxDMAFlag & 0x1) == 0)
		{
			LL_DMA_DisableStream(DMA1,LL_DMA_STREAM_1);			//关闭接收DMA传输
			while(LL_DMA_IsEnabledStream(DMA1,LL_DMA_STREAM_1));	//确保DMA1_Stream1可以被设置
			LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_1, (uint32_t)MAX485LEN);	//设置传输长度
			LL_DMA_EnableStream(DMA1,LL_DMA_STREAM_1);			//使能接收DMA数据流通道
			UART8->CR3 |= (1 << 6);				//使能串口DMA接收
			RS485.RxDMAFlag = 0x1;				//标记已开启接收DMA
			RS485.DMA_RecTick = SYS_TICK;
		}		
		#endif
		//...
          break;
     case RS485_WAIT_SEND_OVER:
		if(MsTickDiff(RS485.DMA_TransTick) > 5000)					//连续5s都没有发送完成 重新初始化串口
			RS485.Status = RS485_INIT;
          break;
	case RS485_WAIT_REC_FIRST_BYTE:
		if(MsTickDiff(RS485.DMA_RecTick) > 2000)					//连续10s都没有收到第一个字节 重新初始化串口
			RS485.Status = RS485_INIT;
//		if(MsTickDiff(RS485.DMA_RecTick) > 500)					//连续10s都没有收到第一个字节 重新初始化串口
//			RS485.Status = RS485_INIT;
		break;
     case RS485_REC:
		if(MsTickDiff(RS485.DMA_RecTick) > DMARecTimeout)				//5ms未收到数据 
		{
			RS485.RxDMAFlag = 0;
			LL_DMA_DisableStream(DMA1,LL_DMA_STREAM_1);			//关闭接收DMA传输
			while(LL_DMA_IsEnabledStream(DMA1,LL_DMA_STREAM_1));	//确保DMA1_Stream1可以被设置
			UART8->CR3 &=~ (1 << 6);				//关闭串口DMA接收
			rec_len = DMA1_Stream1->NDTR;
			RS485.RxLen = MAX485LEN - rec_len;		//获取实际接收长度
			
			RxFlag = HandleRS485Data();
			
			for(k = 0;k < MAX485LEN;k++)			//清除接收buf
			{
				RS485.RxBuf[k] = 0;
			}
			RS485.RxLen = 0;
			//...接收到不符合要求的数据 符合要求的数据则会在发送完成DMA中重新开启接收DMA
			if(RxFlag == 0)
			{
				LL_DMA_DisableStream(DMA1,LL_DMA_STREAM_1);			//关闭接收DMA传输
				while(LL_DMA_IsEnabledStream(DMA1,LL_DMA_STREAM_1));	//确保DMA1_Stream1可以被设置
				LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_1, (uint32_t)MAX485LEN);	//设置传输长度
				LL_DMA_EnableStream(DMA1,LL_DMA_STREAM_1);			//使能接收DMA传输
				UART8->CR3 |= (1 << 6);				//使能串口DMA接收
				RS485.RxDMAFlag = 0x1;				//标记已开启接收DMA
				RS485.DMA_RecTick = SYS_TICK;
				RS485.Status = RS485_WAIT_REC_FIRST_BYTE;
			}
			//...
		}
          break;
	case RS485_DEBUG:
		
		break;
	case RS485_INIT_REC:
		break;
     default:
          RS485.Status = RS485_INIT;
          break;
     }
}
c 复制代码
//开启一次DMA传输 DMA1_Stream2
//ndtr:数据传输量
void Uart8_DMA_Send(uint16_t ndtr)
{
	if(RS485.TxDMAFlag == 0)
		return;
	if((UART8->ISR & 0x40) == 0)
		return;
	RS485.TxDMAFlag = 0;
	RS485_CTR_H;// 485发送模式
//	DelayMS(2);
	LL_DMA_DisableStream(DMA1,LL_DMA_STREAM_2);					//关闭发送DMA传输
	while(LL_DMA_IsEnabledStream(DMA1,LL_DMA_STREAM_2));			//确保DMA1_Stream2可以被设置
	LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_2, (uint32_t)ndtr);	//设置传输长度
	LL_DMA_EnableStream(DMA1,LL_DMA_STREAM_2);					//使能发送DMA传输
	UART8->CR3 |= 1 << 7;        								//使能串口DMA控制器 
	
	RS485.DMA_TransTick = SYS_TICK;							//更新DMA接收长度Tick
	RS485.Status = RS485_WAIT_SEND_OVER;
}



//UART8_RX DMA
void DMA1_Stream1_IRQHandler(void)
{
	if(LL_DMA_IsActiveFlag_TC1(DMA1))					//传输完成中断    
	{
		LL_DMA_ClearFlag_TC1(DMA1);               		//清除传输完成中断
	}
	if(LL_DMA_IsActiveFlag_TE1(DMA1))          			//传输错误中断    
	{
		LL_DMA_ClearFlag_TE1(DMA1);   				
		RS485.Status = RS485_INIT;
	}
}

//UART8_TX DMA
void DMA1_Stream2_IRQHandler(void)
{
	if(LL_DMA_IsActiveFlag_TC2(DMA1))					//传输完成中断    
	{
		LL_DMA_ClearFlag_TC2(DMA1);               		//清除传输完成中断
		while(LL_USART_IsActiveFlag_TC(UART8) != SET);	//等待串口发送完成
		LL_USART_DisableDMAReq_TX(UART8);				//失能串口DMA控制器
		RS485_CTR_L;								//RS485设为接收模式
		RS485.TxDMAFlag = 1;
		RS485.Status = RS485_WAIT_REC_FIRST_BYTE;		//等待接收第一个字节
		
		//...配置串口接收DMA
		if(!RS485.RxDMAFlag & 0x1)
		{
			LL_DMA_DisableStream(DMA1,LL_DMA_STREAM_1);			//关闭接收DMA传输
			while(LL_DMA_IsEnabledStream(DMA1,LL_DMA_STREAM_1));	//确保DMA1_Stream1可以被设置
			LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_1, (uint32_t)MAX485LEN);	//设置传输长度
			LL_DMA_EnableStream(DMA1,LL_DMA_STREAM_1);			//使能接收DMA数据流通道
			UART8->CR1 |= (1 << 2);  			//串口接收使能 防止运行久了后串口接收死机
			UART8->CR3 |= (1 << 6);				//使能串口DMA接收
			RS485.RxDMAFlag = 0x1;				//标记已开启接收DMA
			RS485.DMA_RecTick = SYS_TICK;
		}		
		//...
		
	}
	if(LL_DMA_IsActiveFlag_TE2(DMA1))          			//传输错误中断    
	{
		LL_DMA_ClearFlag_TE2(DMA1);   
		while(LL_USART_IsActiveFlag_TC(UART8) != SET);	//等待串口DMA发送完成
		LL_USART_DisableDMAReq_TX(UART8);				//关闭串口DMA发送
		RS485.Status = RS485_INIT;
	}
}
相关推荐
π同学2 小时前
基于CH343的一种自动烧录模式电路设计分析
单片机·嵌入式硬件·eps32
正点原子2 小时前
《DNESP32P4开发指南_V1.0》第十一章 LED实验
单片机·物联网·嵌入式
国科安芯2 小时前
商业航天视角下角度编码传感器的应用与MCU的集成适配
大数据·网络·单片机·嵌入式硬件·架构·制造·安全性测试
森利威尔电子-2 小时前
森利威尔SL3041B 替换LM5018 100V降压3.3V 5V 12V恒压芯片
单片机·嵌入式硬件·集成电路·芯片·电源芯片
zhenxin01222 小时前
SocketTool、串口调试助手、MQTT中间件基础
单片机·嵌入式硬件·中间件
CODE_RabbitV3 小时前
【保姆级实操版 - STM32 系列笔记】STM32F103标准库开发:Keil5新建工程完整教程
笔记·stm32·嵌入式硬件
项目題供诗3 小时前
51单片机入门-AD/DA(十五)
单片机·嵌入式硬件·51单片机
NULL指向我3 小时前
STM32 F103C8T6学习笔记20:cubemx 配置 ADC采样 电位器
笔记·stm32·学习
Heartache boy3 小时前
野火STM32_HAL库版课程笔记-TIM高级定时器基础中断应用
笔记·stm32·单片机