这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【Let'sdo2025年第1期】功率监测与控制系统DIY活动进阶任务,串口控制

共1条 1/1 1 跳转至

【Let'sdo2025年第1期】功率监测与控制系统DIY活动进阶任务,串口控制电机工作

高工
2025-06-14 09:32:59     打赏

第一个拓展任务是实现串口设置电机的过流阈值、控制电机的启停,同时将电机的过流阈值写到MCU的片上Flash中。
# 串口收发数据

开发板上串口2和板载的调试器通讯,上位机可以通过USART2先MCU发送数据。
USART2_ST_Link.png

在ST的示例程序中,参考示例程序中的USART_Communication_RX_IT示例。

USART_RX_IT.png

在CubeMX中对USART2的参数和中断进行设置。


USART2_Para.png

USART2_NVIC.png


ST为外设提供简易封装的LL驱动,在CubeMX中可以选择LL库作为外设的驱动库,使用LL接口可以提升代码的运行效率。

USART2_LL.png完成上述配置并生成代码。在函数中添加以下代码,实现printf重定向到USART2。

#include "stdio.h"

#ifdef __GNUC__
  /* With GCC, small printf (option LD Linker->Libraries->Small printf
     set to 'Yes') calls __io_putchar() */
  #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
  #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */


/**
  * @brief  Retargets the C library printf function to the USART.
  * @param  None
  * @retval None
  */
PUTCHAR_PROTOTYPE
{
  /* Place your implementation of fputc here */
  /* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */
  LL_USART_TransmitData8(USART2, ch);
  while (!LL_USART_IsActiveFlag_TXE(USART2));

  return ch;
}

在生成的代码中,需要添加额外的代码来使能USART2的接收中断功能,添加的位置和代码如下。

USART2_RXNE.png

  LL_USART_EnableIT_RXNE(USART2);  
  LL_USART_EnableIT_ERROR(USART2);

USART2的中断函数中的处理如下。

void USART2_IRQHandler(void)
{
  /* USER CODE BEGIN USART2_IRQn 0 */

  /* USER CODE END USART2_IRQn 0 */
  /* USER CODE BEGIN USART2_IRQn 1 */
  /* Check RXNE flag value in SR register */
  if(LL_USART_IsActiveFlag_RXNE(USART2) && LL_USART_IsEnabledIT_RXNE(USART2))
  {
	cli_recv=LL_USART_ReceiveData8(USART2);
	/* RXNE flag will be cleared by reading of DR register (done in call) */
	/* Call function in charge of handling Character reception */
	if(USART_RX_CNT < USART_REC_LEN)
	{
		USART_RX_BUF[USART_RX_CNT]=cli_recv;		//记录接收到的值
		USART_RX_CNT++;						//接收数据增加1
	}
  }

  /* USER CODE END USART2_IRQn 1 */
}

# 在Flash中存储电流阈值信息。

在STM32F411的参考手册中可以看到片上FLASH的分布情况。用户使用其中的一个Sector作为存储电流阈值信息的位置。

Flash_Table.png

在配套的示例代码中可以参考F4系列的其他芯片的Flash示例程序来使用相应的Flash函数。

Flash_Program_Example.png

在指定的Flash地址中写入数据的代码如下。

/*Flash store*/
#define ADDR_FLASH_SECTOR_7     ((uint32_t)0x08060000) /* Base @ of Sector 7, 128 Kbytes */
#define FLASH_USER_START_ADDR   ADDR_FLASH_SECTOR_7   /* Start @ of user Flash area */


FLASH_EraseInitTypeDef pEraseInit;
uint32_t Address,SECTORError = 0;

//Write currentValue into flash
pEraseInit.TypeErase = TYPEERASE_SECTORS;
pEraseInit.Sector = FLASH_SECTOR_7;
pEraseInit.NbSectors = 1;
pEraseInit.VoltageRange = FLASH_VOLTAGE_RANGE_3;

HAL_FLASH_Unlock();

HAL_FLASHEx_Erase(&pEraseInit, &SECTORError);
Address=FLASH_USER_START_ADDR;
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, currentValue);
HAL_FLASH_Lock();

从Flash读取写入的数据的操作代码如下

uint32_t Address;
Address=FLASH_USER_START_ADDR;

Power_Info.Cur_Threshold=*(uint32_t*)Address;

# 串口接收数据解析以及过流保护

上述对串口USART2的数据收发以及Flash的读写的关键代码进行介绍,结合教程中对串口指令解析的代码,可以实现对电机的阈值管理、启动和停止功能。相关的代码如下:

uint8_t *rx_p;
uint8_t CTR_CMD,Model1_CMD,Model2_CMD,StartRun_CMD,StopRun_CMD;
uint32_t TEXT_Buffer[1];
uint32_t dataTemp[1];
uint8_t CTR_Mode=0;
uint8_t Unlock_Current=0;
char Can_Run_Status=0;

void Processing_data(void)
{
	char *thresholdStr = strstr((char*)Ext_USART_RX_BUF,"CurrentThreshold:" );

    FLASH_EraseInitTypeDef pEraseInit;
    uint32_t Address,SECTORError = 0;

	if(ext_recpack_finish_flag)
	{
		ext_recpack_finish_flag=0;
		if(thresholdStr!=NULL)
		{
			thresholdStr+=strlen("CurrentThreshold:");
			char *endPtr;
			long currentValue = strtol(thresholdStr,&endPtr,10);
			if(endPtr!=thresholdStr && *endPtr == 'm' && *(endPtr+1)== 'A')
			{
				TEXT_Buffer[0]= currentValue;
				//Write currentValue into flash
			    pEraseInit.TypeErase = TYPEERASE_SECTORS;
				pEraseInit.Sector = FLASH_SECTOR_7;
				pEraseInit.NbSectors = 1;
				pEraseInit.VoltageRange = FLASH_VOLTAGE_RANGE_3;

				HAL_FLASH_Unlock();

				HAL_FLASHEx_Erase(&pEraseInit, &SECTORError);
			    Address=FLASH_USER_START_ADDR;
			    HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, currentValue);
			    HAL_FLASH_Lock();

				//Read back currentValue to check whether writing success
			    dataTemp[0]=*(uint32_t*)Address;
				if(TEXT_Buffer[0]==dataTemp[0])
				{
					Power_Info.Cur_Threshold = dataTemp[0];
				}
				else
				{
					Power_Info.Cur_Threshold= Default_Cur_Threshold_value;
				}
			}
		}
	}

	rx_p=&Ext_USART_RX_BUF[0];
	CTR_CMD=StringContain((char*)rx_p,"RecoveryControl");
	StartRun_CMD=StringContain((char*)rx_p,"CanRun");
	StopRun_CMD=StringContain((char*)rx_p,"StopRun");
	Model1_CMD=StringContain((char*)rx_p,"CurrentMode1");
	Model2_CMD=StringContain((char*)rx_p,"CurrentMode2");

	if(CTR_CMD>0)
	{
		rx_p+=CTR_CMD;
		Unlock_Current=1;
	}

	if(StartRun_CMD>0)
	{
		rx_p+=StartRun_CMD;
		Can_Run_Status=1;
		HAL_GPIO_WritePin(Relay_GPIO_Port,Relay_Pin,GPIO_PIN_SET);
	}

	if(StopRun_CMD>0)
	{
		rx_p+=StopRun_CMD;
		Can_Run_Status=-1;
		HAL_GPIO_WritePin(Relay_GPIO_Port,Relay_Pin,GPIO_PIN_RESET);
	}

	if(Model1_CMD>0)
	{
		rx_p+=Model1_CMD;
		CTR_Mode=Self_Locking_Mode;
	}

	if(Model2_CMD>0)
	{
		rx_p+=Model2_CMD;
		CTR_Mode=Hiccup_Mode;
	}
}

INA219采集到电流数据后,与阈值电流进行比较,据此控制继电器的通断,从而实现对电机的过流保护。

if(Power_Info.current>Power_Info.Cur_Threshold)
{
	HAL_GPIO_WritePin(Relay_GPIO_Port,Relay_Pin,GPIO_PIN_RESET);
	OverCurrent_Flag = 1;
	printf("Self_Locking_Mode Current Overflow!\r\n");
}
if(Unlock_Current==1 && OverCurrent_Flag)
{
	OverCurrent_Flag=0;
	Unlock_Current=0;
	if(Can_Run_Status==1)
	{
		HAL_GPIO_WritePin(Relay_GPIO_Port,Relay_Pin,GPIO_PIN_SET);
		HAL_Delay(2000);
	}
	printf("Self_Locking_Mode Overflow Released!\r\n");
}

# 效果演示

通过串口控制电机的效果视频如下。
https://www.bilibili.com/video/BV1PzTezgE6p/?vd_source=c3c72f263ba4f17695f3ce613d1d0024

# 总结

教程中提供的串口解析指令和控制流程的效果不错,通过参考其中的代码,实现串口控制电机和在Flash中写入过流阈值信息。






关键词: Let do 2025    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]