STM32CubeMx生成的工程中使用Printf函数调试和IAP

原文链接:https://blog.csdn.net/mynameislinduan/article/details/83579725

stm32cubemx工程,freerots
stm32cubemx调用printf
stm32 iap,stm32在线下载
stm32cubemx在线烧录
stm32 调用printf函数
stm32重定向printf函数输出
stm32串口printf输出
stm32进入dfu
stm32cubemx IAP,stm32cubemx duf
stm32cubemx在线烧录。stm32cubemx在线更新程序
stm32远程更新

一、STM调试信息打印方法。

1. 新建stm32cubemx工程,配置工程。

我用的是STM32cubeMx版本: Version 4.27.0, cube Version V1.0。

2. 在stm32cubemx工程,配置工程,勾选使用FreeRtOs系统。

注意,勾选左边的,freertos,使用系统。stm32做的,非常方便。

3. 生成带系统FreeRtos的源码工程(我这里基于keil MDK v5)

4. 生成工程,然后用keil打开,在源码里面添加代码,使得USART1重定向到标准Printf函数中输出。

在generated file里面,勾选:generate periheral initialization as apair of “.c/.h” files per periheral; 这样可以让每个外设,都生成一个文件。不用全面功能呢堆在一起。

  • 注意选择工程的种类,我用的是keil mdk v5。

5. 总结添加的代码如下:

  • 记得包含标准输入输入头文件: stdio.h

    在usart.c里面添加:

  • /* USER CODE BEGIN 0 */
    #include <stdio.h>
    /* USER CODE END 0 */
    
    /* USER CODE BEGIN 1 */
    #ifdef __GNUC__
    	#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
    #else
    	#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
    #endif
    PUTCHAR_PROTOTYPE
    {
    		HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);
    		return ch;
    }
    /* USER CODE END 1 */
    

    在“user code” 里面添加,这样你再次编译生成stm32cubmx工程的时候,你的代码才会被保留,不如会被知覆盖掉。

二、STM IAP方法(基于STM32F0xx系列)。
1. stm32f0x系列IAP,基于Stm32CubeMx的工程。

    下面说明如何基于stm32f0x系统里面,使用在线升级(stm32f0 IAP)

    Bootloader里面,main里调用函数跳转到APP地址0x8003000,下面的这个是main函数里面的主程序,分开烧录下载,一般来说,先下载这个程序用于做IAP的。
 

typedef  void (*pFunction)(void);
#define ApplicationAddress    0x8003000
void UserAppStart(void)//这里是bootloader里面的函数
{
	  if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)
    { 
      /* Jump to user application */
      JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
      Jump_To_Application = (pFunction) JumpAddress;//跳转到0x8003000
      /* Initialize user application's Stack Pointer */
      __set_MSP(*(__IO uint32_t*) ApplicationAddress);初始化用户程序的堆栈指针
      Jump_To_Application();//执行跳转,程序从0x8003000开始跑。
    }
}

void main(void)
{
	/*加入一些判断,或者*/
	UserAppStart;
}

stm23cubemx 工程源码里面(APP) main 函数添加:

#define APPLICATION_ADDRESS     (uint32_t)0x08003000
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
#if   (defined ( __CC_ARM ))
__IO uint32_t VectorTable[48] __attribute__((at(0x20000000)));
#elif (defined (__ICCARM__))
#pragma location = 0x20000000
__no_init __IO uint32_t VectorTable[48];
#elif defined   (  __GNUC__  )
__IO uint32_t VectorTable[48] __attribute__((section(".RAMVectorTable")));
#endif
// 上面是要自己添加的

int main(void)
{
//自己添加的,最好在user code里面添加,不会被覆盖
 /* USER CODE BEGIN 1 */
  uint32_t i = 0;
 /* USER CODE END 1 */
  HAL_Init();

  /* Configure the system clock to 48 MHz */
  SystemClock_Config();

  /* Relocate by software the vector table to the internal SRAM at 0x20000000 ***/  

  /* Copy the vector table from the Flash (mapped at the base of the application
     load address 0x08004000) to the base address of the SRAM at 0x20000000. */
  for(i = 0; i < 48; i++)
  {
    VectorTable[i] = *(__IO uint32_t*)(APPLICATION_ADDRESS + (i<<2));
  }

  /* Enable the SYSCFG peripheral clock*/
  __HAL_RCC_SYSCFG_CLK_ENABLE(); 
  /* Remap SRAM at 0x00000000 */
  
//拷贝中断向量到 VectorTable,stm32f0 没有中断偏移寄存器,所以将中断向量表读取到RAM中,然后重映射到RAM地址.
  __HAL_SYSCFG_REMAPMEMORY_SRAM();

  /* Add your own code here...    
*/
// 接下来做自己的事情,这里忽略不写...........
}

在Keil下设置IROM1:start 0x8003000 IRAM1:Start 0x200000C0
记得对应自己设置的位置。

stm32f0 没有中断偏移寄存器,所以将中断向量表读取到RAM中,然后重映射到RAM地址, 应该说,stm32f0x系列的都要这样做,否则不能正常使用中断。

2. stm32f1+系列IAP,基于Stm32CubeMx的工程。

  • stm32F103 dfu IAP
  • #define ApplicationAddress 0x08005000
    
    typedef  void (*pFunction)(void);
    
     int main(void)
    {
      DFU_Button_Config();
     
    	/*  DFU Button Check */
    	if (GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8) == 0)
    	{ 
    		// Test if user code is programmed starting from address 0x8005000 //
    		if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)
    		{ 
    			// Jump to user application //
    			JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
    			Jump_To_Application = (pFunction) JumpAddress;
    			// Initialize user application's Stack Pointer //
    			__set_MSP(*(__IO uint32_t*) ApplicationAddress);
    			Jump_To_Application();
    		}
    	 }
    

    这里不用重映射向量表,只需要跳转就好了。这个使用于stm32F1,stm32F2等单片机的dfu升级和在线升级。同样,需要在Keil中的"Target"设定IROM1的气势地址和结束地址, 一般来说,设置起始地址: 0x8003000,或者0x8005000都可以,主要是看你的IAP程序有多大。

  • 注意! 上面并非在线固件升级,而只是,修改bootloader,如果需要在线usb DFU升级,那么要用到Stm32fCubeMx的 dfu功能, 幸运的是,我们不需要在去折腾stm32寄存器,而用stm32cubemx直接生成dfu功能的代码了,详细见下面:

三、 STM32CubeMx Device firmware Update(STM32 DFU功能实现)
1. demo:STM32F103VBT单片机的dfu IAP功能的实现详细步骤(基于Stm32cubeMx):

    先打开Stm32CubeMx软件,然后选择型号,stm32f103vbt,在左侧配置功能的界面,打开usb device(STM32F103VBt只有 usb Device only功能)

版本版本有依旧之前一样。

2. 配置STM32F103VBT单片机的DFU(这里默认配置即可),启动地址一般位于0x08000000:

3. 生成工程,就万事大吉,启动开发板,你会发现需要你安装usb驱动,

4. 下载编译好的stm32cubemx工程DFU hex文件到开发板验证:

  • 然后上电启动,连开发板的usb到电脑,发现,电脑发现新硬件!需要安装smt32 的dfu驱动, 这个驱动在你的DFU下载软件DfuSeDemo的安装文件夹里面可以找到
  • 安装好驱动之后,就可以用那个来下载主程序了
  • 记得修改dfu里面的跳转(这样你才能有跑到你的应用程序的main函数去),参考上一个步骤,开始你的产品程序编写吧!
  • 参考:

    http://bbs.21ic.com/icview-571430-1-1.html

    http://bbs.21ic.com/icview-822770-1

    https://blog.csdn.net/xy6zzz/article/details/49992367

    https://blog.csdn.net/flydream0/article/details/52808191

     

可以使用STM32CubeMX配置DMA来实现串口通信printf功能。下面是一个示例代码,可以将printf输出通过DMA发送到串口: 1. 首先,确保你已经在STM32CubeMX启用了相关的USART和DMA功能,并正确配置了串口通信的参数。 2. 在生成的代码找到USART相关的初始化函数,一般位于`usart.c`文件。在该函数,找到`HAL_UART_MspInit()`函数实现。 3. 在`HAL_UART_MspInit()`函数的开头添加以下代码,用于初始化DMA传输相关的资源: ```c /* DMA interrupt init */ /* DMA1_Channel4_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA1_Channel4_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn); ``` 4. 接下来,在`HAL_UART_MspInit()`函数添加以下代码,用于配置DMA传输: ```c /* Enable the DMA transfer for transmit */ __HAL_RCC_DMA1_CLK_ENABLE(); hdma_usart2_tx.Instance = DMA1_Channel4; hdma_usart2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; hdma_usart2_tx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_usart2_tx.Init.MemInc = DMA_MINC_ENABLE; hdma_usart2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_usart2_tx.Init.Mode = DMA_NORMAL; hdma_usart2_tx.Init.Priority = DMA_PRIORITY_LOW; if (HAL_DMA_Init(&hdma_usart2_tx) != HAL_OK) { Error_Handler(); } /* Associate the initialized DMA handle to the UART handle */ __HAL_LINKDMA(huart, hdmatx, hdma_usart2_tx); /* Enable the DMA transfer complete interrupt */ __HAL_UART_ENABLE_IT(huart, UART_IT_TC); ``` 注意:上述代码使用的是USART2和DMA1_Channel4,你需要根据实际情况进行调整。 5. 在生成的代码找到`USART_Transmit()`函数,一般位于`usart.c`文件。在该函数添加以下代码,用于启动DMA传输: ```c /* Set the DMA transfer complete callback */ huart->hdmatx->XferCpltCallback = UART_DMA_TransmitCplt; /* Start the DMA transfer */ HAL_DMA_Start_IT(huart->hdmatx, (uint32_t)str, strlen(str)); ``` 6. 最后,在生成的代码找到`USART_IRQHandler()`函数,一般位于`stm32f4xx_it.c`文件。在该函数添加以下代码,用于处理DMA传输完成的断: ```c /* USART2 DMA Transmit Complete Interrupt */ void DMA1_Channel4_IRQHandler(void) { /* Check the DMA interrupt flag */ if (__HAL_DMA_GET_IT_SOURCE(huart2.hdmatx, DMA_IT_TC) != RESET) { /* Clear the DMA interrupt flag */ __HAL_DMA_CLEAR_FLAG(huart2.hdmatx, DMA_IT_TC); /* Disable the DMA transfer complete interrupt */ __HAL_DMA_DISABLE_IT(huart2.hdmatx, DMA_IT_TC); /* Call the DMA transmit complete callback function */ HAL_UART_TxCpltCallback(&huart2); } } ``` 完成以上步骤后,你就可以在代码使用标准的`printf`函数,输出的内容会通过DMA传输到串口。 希望这对你有帮助!如有更多问题,请继续提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值