STM32

STM32是STMicroelectronics(意法半导体)推出的一系列基于ARM Cortex-M内核的32位微控制器(MCU)产品。这些微控制器提供了广泛的产品系列,覆盖了多种不同的性能和功能需求,适用于各种应用领域,包括工业控制、汽车电子、消费类电子、医疗设备等。

STM32系列微控制器以其高性能、低功耗、丰富的外设接口和灵活的开发工具而闻名。它们通常具有丰富的存储器、多种通信接口(如UART、SPI、I2C、CAN等)、模拟数字转换器(ADC)、定时器、PWM输出等功能,以满足不同应用场景下的需求。

STM32微控制器通常使用标准的ARM Cortex-M内核,包括Cortex-M0、M0+、M3、M4和M7等,这些内核具有不同的性能和功耗特性,可根据具体应用的需求进行选择。此外,STM32系列还提供了多种封装和引脚配置,以满足不同尺寸和集成度的要求。

STMicroelectronics为STM32系列提供了丰富的开发工具和支持资源,包括基于ARM开发环境的集成开发环境(IDE)、调试器、评估板和参考设计等。这些工具和资源有助于开发人员快速开发和部署他们的应用,并提供了全面的技术支持和文档资料,帮助用户充分发挥STM32微控制器的性能和功能优势。

STM32等单片机是可编程处理器,内部运行着我们编写的程序,而把我们编写的程序“下载”到单片机中,方法有两种:

一、使用烧写器,如jlink,stlink,串口下载(需要配置boot0,boot1)。

二、通过IAP实现一个在线更新功能。

对于很多使用单片机作为主要处理器的电子产品,如遇到需要替换芯片内部程序以满足需求的情况,通常的解决办法是寄回该产品然后通过烧写器直接替换程序。但这样做无疑会增加相关的成本。所以很多的电子产品都会实现一个能远程更新(通过网络更新芯片程序,如手机更新操作系统等)或者自主更新程序(通过U盘,SD卡等方式更新,芯片读取并识别这些外部存储器存放的程序,并读取到自身内部空间中)。

首先简单说一下STM32等单片机,程序的存储位置是内部Flash空间,查看STM32F1参考手册等相关资料可以得知程序存放的起始地址为0x08000000(不同的单片机这个可能会有所不同)。

实现IAP功能的基本思路:划分芯片内部的Flash空间,分别存放不同的程序实现不同的功能。实现一个简单的跳转函数,使我们能控制芯片什么时候运行什么程序。通常划分的做法是:Bootloader+APP+APPBackup。

先介绍一下跳转函数,先看代码:

//定义一个函数类型:返回类型是void,函数参数是void
typedef  void (*iapfun)(void);
				
//声明jump2app函数的类型
iapfun jump2app; 
 
 
//
//appxaddr(需要跳转的地址)
//
void iap_load_app(u32 appxaddr)
{
    //判断栈顶地址是否合法
	if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000)	
	{ 
        //指定程序跳转的位置
		jump2app=(iapfun)*(vu32*)(appxaddr+4);		
		MSR_MSP(*(vu32*)appxaddr);
        
        //直接跳转,运行用户指定的程序。					
		jump2app();								
	}else
	{
		printf("error....\r\n");
	}
}		 

介绍一下Flash空间划分及对应存放的程序:

①:0x08000000 - 0x0800FFFF :共64Kb,存放Bootloader程序,用于判断是否需要更新及负责获取更新的数据并进行Flash操作写入数据。

②:0x08010000 - 0x0802FFFF :共128Kb,存放APP程序,即用户程序,会获得实际运行权的程序。

③:0x08030000 - 0x0804FFFF :共128Kb,作为IAP缓存区,Bootloader在更新过程中获取到的数据会先写入到这部分Flash空间,等待所有数据获取完成后,通过Flash搬移操作,把这部分Flash上的数据复制到第②部分的存储空间中。Bootloader不直接写第②部分的空间是为了避免在更新过程中出现的意外情况,如电量耗尽等,损坏了原来在芯片中的可运行程序。即确保芯片至少有一个可运行的程序,防止设备“变砖”。

④:0x08050000 - 0x0806FFFF:共128Kb,作为出厂程序备份区,针对更新出错等意外情况,提供一种解决办法能让设备恢复原来的状态。

⑤:0x08070000 - 0x0807FFFF:共64Kb,作为用户重要数据存储区。

运行流程:系统启动后,运行Bootloader程序,通过读取相关存储标志,如果需要更新,启动数据获取-转换-写Flash-跳转的更新流程。如果不需要更新,则直接跳转至用户程序:iap_load_app(0x08010000);

更新流程:程序数据获取-数据转换-写入Flash,循环直到数据全部写入完成。由于可能会出现需要更新的程序(目标程序)比较大,多达几十k上百k,因此要求Bootloader一次读取完全部程序数据是不现实的。更为可靠的做法是每次读入一定数量的程序数据,如2k等。

由于程序数据获取的方式太多,有串口输入,网络请求,读取外部存储器等方式,这里就不再介绍数据获取部分,无论是哪种方式实现的,其本质都是一样的,只是把需要更新的程序通过某一种方式让处理器能获取到。

顺带提一下,目标程序的文件格式是.bin文件(编译选项加入参数,见下图),且在编译前指定好了相关的Flash位置(可直接在target中设置)和中断向量偏移位置(通过修改SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;)。如按照上述的Flash分区设置,这里VECT_TAB_OFFSET的值应为0x10000。


假设我们的更新程序的前面2k数据已经获取完成了,保存在了u8 appbuf[1024*2+1]的数组中,我们需要把这些数据写入到内存中,但由于ST提供的Flash操作函数要求的是半字写入,即u16类型,所以这里我们获取到的数据还需要处理一下,转换成u16类型。

//不检查的写入
//WriteAddr:起始地址
//pBuffer:数据指针
//NumToWrite:半字(16位)数   
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.
	}  
} 
//从指定地址开始写入指定长度的数据
//WriteAddr:起始地址(此地址必须为2的倍数!!)
//pBuffer:数据指针
//NumToWrite:半字(16位)数(就是要写入的16位数据的个数.)
#if STM32_FLASH_SIZE<256
#define STM_SECTOR_SIZE 1024 //字节
#else 
#define STM_SECTOR_SIZE	2048
#endif		 
u16 STMFLASH_BUF[STM_SECTOR_SIZE/2];//最多是2K字节
void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)	
{
	u32 secpos;	   //扇区地址
	u16 secoff;	   //扇区内偏移地址(16位字计算)
	u16 secremain; //扇区内剩余地址(16位字计算)	   
 	u16 i;    
	u32 offaddr;   //去掉0X08000000后的地址
	if(WriteAddr < STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址
	FLASH_Unlock();						//解锁
	offaddr=WriteAddr-STM32_FLASH_BASE;		//实际偏移地址.
	secpos=offaddr/STM_SECTOR_SIZE;			//扇区地址  0~127 for STM32F103RBT6
	secoff=(offaddr%STM_SECTOR_SIZE)/2;		//在扇区内的偏移(2个字节为基本单位.)
	secremain=STM_SECTOR_SIZE/2-secoff;		//扇区剩余空间大小   
	if(NumToWrite < =secremain)secremain=NumToWrite;//不大于该扇区范围
	while(1) 
	{	
 
		STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容
//		printf("\nwrite flash\n");
		for(i=0;i < secremain;i++)//校验数据
		{
			if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;//需要擦除
		}
		
		if(i < secremain)//需要擦除
		{
			FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);//擦除这个扇区
			for(i=0;i< secremain;i++)//复制
			{
				STMFLASH_BUF[i+secoff]=pBuffer[i];	  
			}
			STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区  
		}else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);//写已经擦除了的,直接写入扇区剩余区间. 				   
		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();//上锁
}
#endif
 
//
//用户接口程序
//实现写入用户获取到的程序数据
//
u32 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;
		iapbuf[i++]=temp;	    
	}
	STMFLASH_Write(fwaddr,iapbuf,i);	
	return fwaddr+appsize; 
}

声明:以上贴出的代码片出自正点原子提供的例程中。

调用 iap_write_appbin(0x08030000+x,appbuf, 1024*2) 。即可完成 转换-写入的操作流程。其中x为已经写入的大小。

由于每个程序大小都是不固定的,因此会出现获取最后一帧数据的时候,数据量不足2k的情况,这时候需要对最后的这一部分做处理,只写入实际获取到的长度,而不能直接写入2k的数据。

全部数据获取完成后,由于之前写入的地址是0x08030000,而这个地址不是我们设置的跳转地址,它在这里只是起到一个存储的功能,不会获得实际的程序运行权,所以,我们还需要实现一个功能,把0x08030000开始的128k数据复制到0x08010000开始的存储空间里。这里的实现方法可以参考我们用烧写器烧录的流程:擦除-写入-校验。代码片在这里就不再贴出了,可以照这个思路自己实现一下。

到这里整个在线更新的流程差不多就走完了,可以在清除更新标志后,直接跳转到用户程序区继续执行,也可以通过主动软件复位功能重启系统,然后让Bootloader判断跳转。

(完)2020.01.21

版权声明:本文为CSDN博主(weymin)原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/u013053268/article/details/104058622

围观 640
  • 降低完整射频电路设计工作量,加快新产品上市时间
  • 优化无线连接性能,低功耗,尺寸紧凑
  • Bluetooth ® LE、Zigbee®和OpenThread认证
  • FCC、CE、JRF、KC、SRRC[1]、GOST地区认证

2021年1月12日—— 横跨多重电子应用领域的全球领先的半导体供应商意法半导体(STMicroelectronics,简称ST;纽约证券交易所代码:STM)推出一个新的加快物联网产品上市的解决方案,该方案可利用现成的微型STM32无线微控制器(MCU)模块加快基于Bluetooth® LE和802.15.4新物联网设备的开发周期。

这个7mm x 11.3mm的 STM32WB5MMG模块让缺少无线设计能力的产品研发团队也能开发物联网产品。为开发层数最少的低成本PCB电路板而设计,新模块集成了直到天线的整个射频子系统。用户还可以免费使用意法半导体的STM32Cube MCU开发生态系统工具、设计向导、射频协议栈和完整软件库,快速高效地完成开发项目。

意法半导体部门副总裁兼微控制器产品总经理Ricardo de Sa Earp表示:“我们的首个基于STM32的无线模块有助于简化技术难题,为智能物联网设备市场带来激动人心的发展机会。作为一个现成的单封装的完整射频子系统,STM32WB5MMG是一个开箱即用的射频性能出色的无线解决方案,并已通过BluetoothZigbeeOpenThread规范认证。”

此外,该模块还支持意法半导体的独树一帜的共存双协议模式,用户可以将任何基于IEEE 802.15.4射频技术的协议(包括Zigbee 3.0和OpenThread)直连任何低功耗蓝牙BLE设备。

得益于意法半导体的STM32WB55超低功耗无线微控制器的所有功能,该模块可用于智能家居、智能建筑和智能工厂设备的各种应用场景。用户可以利用MCU的双核架构将射频和应用处理分开,处理性能不会被任何因素影响;兼具大容量存储器存放射频应用代码和数据,及最新的网络安全功能保护设备安全。

STM32WB5MMG现已开始上市

详细技术信息

STM32WB5MMG可以应对各种层面的应用机会,包括成本敏感的高度小型化设备。优化的引脚让设计人员可以开发简单的低成本PCB电路板,并利用现有的STM32WB55 MCU固件库和工具链开发产品。此外,意法半导体还专门创建了一个应用笔记,为模块用户提供额外的设计指南。

该模块集成了与接收电路正确匹配的微型天线、内部开关电源(SMPS)电路和频率控制组件。通过支持无晶体USB全速接口,该模块使用户可以最大程度地降低物料清单成本,并简化硬件设计。

在网络保护功能中,无线下载(OTA)等安全软件更新可保护品牌和产品设备的完好性,客户密钥存储和专有代码读取保护(PCROP)可保护开发者的知识产权,公共密钥验证(PKA)支持功能支持用密码加密技术保护软件代码和数据通信。

高射频性能与低功耗兼备,新模块确保无线连接可靠稳定,并有助于延长电池续航时间。

[1] Final certification available in January 2021  最终认证于20211月获得

关于意法半导体

意法半导体拥有46,000名半导体技术、产品和方案的创新者和创造者,掌握半导体供应链和最先进的制造设备。作为一家独立的半导体设备制造商,意法半导体与十万余客户、上千合作伙伴一起研发产品和解决方案,共同构建生态系统,帮助他们更好地应对各种挑战和新机遇,满足世界对可持续发展的更高需求。意法半导体的技术让人们的出行更智能,电力和能源管理更高效,物联网和5G技术应用更广泛。详情请浏览意法半导体公司网站:www.st.com

围观 10

串口发送数据

1、串口发送数据最直接的方式就是标准调用库函数 。

void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);

第一个参数是发送的串口号,第二个参数是要发送的数据,但是用过的朋友应该觉得不好用,一次只能发送单个字符,所以我们有必要根据这个函数加以扩展:

void Send_data(u8 *s)
{
 while(*s!='\0')
 { 
  while(USART_GetFlagStatus(USART1,USART_FLAG_TC )==RESET); 
  USART_SendData(USART1,*s);
  s++;
 }
}

以上程序的形参就是我们调用该函数时要发送的字符串,这里通过循环调用USART_SendData来一 一发送我们的字符串。

while(USART_GetFlagStatus(USART1,USART_FLAG_TC )==RESET);

这句话有必要加,它是用于检查串口是否发送完成的标志,如果不加这句话会发生数据丢失的情况。这个函数只能用于串口1发送。有些时候根据需要,要用到多个串口发送,那么就还需要改进这个程序。如下:

void Send_data(USART_TypeDef * USARTx,u8 *s)
{
 while(*s!='\0')
 { 
  while(USART_GetFlagStatus(USARTx,USART_FLAG_TC )==RESET); 
  USART_SendData(USARTx,*s);
  s++;
 }
}

这样就可实现任意的串口发送。但有一点,我在使用实时操作系统的时候(如UCOS,Freertos等),需考虑函数重入的问题。

当然也可以简单的实现把该函数复制一下,然后修改串口号也可以避免该问题。然而这个函数不能像printf那样传递多个参数,所以还可以再改进,最终程序如下:

void USART_printf ( USART_TypeDef * USARTx, char * Data, ... )
{
 const char *s;
 int d;   
 char buf[16];

 va_list ap;
 va_start(ap, Data);

 while ( * Data != 0 )     // 判断是否到达字符串结束符
 {                              
  if ( * Data == 0x5c )  //'\'
  {           
   switch ( *++Data )
   {
    case 'r':                 //回车符
    USART_SendData(USARTx, 0x0d);
    Data ++;
    break;

    case 'n':                 //换行符
    USART_SendData(USARTx, 0x0a); 
    Data ++;
    break;

    default:
    Data ++;
    break;
   }    
  }

  else if ( * Data == '%')
  {           //
   switch ( *++Data )
   {    
    case 's':            //字符串
    s = va_arg(ap, const char *);

    for ( ; *s; s++) 
    {
     USART_SendData(USARTx,*s);
     while( USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET );
    }

    Data++;

    break;

    case 'd':   
     //十进制
    d = va_arg(ap, int);

    itoa(d, buf, 10);

    for (s = buf; *s; s++) 
    {
     USART_SendData(USARTx,*s);
     while( USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET );
    }

    Data++;

    break;

    default:
    Data++;

    break;

   }   
  }

  else USART_SendData(USARTx, *Data++);

  while ( USART_GetFlagStatus ( USARTx, USART_FLAG_TXE ) == RESET );

 }
}

该函数就可以像printf使用可变参数,方便很多。通过观察函数但这个函数只支持了%d,%s的参数,想要支持更多,可以仿照printf的函数写法加以补充。

2、 直接使用printf函数。

很多朋友都知道STM32直接使用printf不行的。需要加上以下的重映射函数:


如果不想添加以上代码,也可以勾选以下的Use MicroLI选项来支持printf函数使用:


串口接收数据

串口接收最后应有一定的协议,如发送一帧数据应该有头标志或尾标志,也可两个标志都有。

这样在处理数据时既能能保证数据的正确接收,也有利于接收完后我们处理数据。串口的配置在这里就不再赘述,这里以串口2接收中断服务程序函数且接收的数据包含头尾标识为例。

#define Max_BUFF_Len 18
unsigned char Uart2_Buffer[Max_BUFF_Len];
unsigned int Uart2_Rx=0;
void USART2_IRQHandler() 
{
 if(USART_GetITStatus(USART2,USART_IT_RXNE) != RESET) //中断产生 
 {
  USART_ClearITPendingBit(USART2,USART_IT_RXNE); //清除中断标志

  Uart2_Buffer[Uart2_Rx] = USART_ReceiveData(USART2);     //接收串口1数据到buff缓冲区
  Uart2_Rx++; 

  if(Uart2_Buffer[Uart2_Rx-1] == 0x0a || Uart2_Rx == Max_BUFF_Len)    //如果接收到尾标识是换行符(或者等于最大接受数就清空重新接收)
  {
   if(Uart2_Buffer[0] == '+')                      //检测到头标识是我们需要的 
   {
    printf("%s\r\n",Uart2_Buffer);        //这里我做打印数据处理
    Uart2_Rx=0;                                   
   } 
   else
   {
    Uart2_Rx=0;                                   //不是我们需要的数据或者达到最大接收数则开始重新接收
   }
  }
 }
}

数据的头标识为“\n”既换行符,尾标识为“+”。该函数将串口接收的数据存放在USART_Buffer数组中,然后先判断当前字符是不是尾标识,如果是,说明接收完毕,然后再来判断头标识是不是“+”号,如果还是,那么就是我们想要的数据,接下来就可以进行相应数据的处理了。但如果不是,那么就让Usart2_Rx=0重新接收数据。

这样做有以下好处:

  • 可以接收不定长度的数据,最大接收长度可以通过Max_BUFF_Len来更改
  • 可以接收指定的数据
  • 防止接收的数据使数组越界

这里得把接收正确数据直接打印出来,也可以通过设置标识位,然后在主函数里面轮询再操作。

以上的接收形式是中断一次就接收一个字符,这在UCOS等实时内核系统中频繁的中断,非常消耗CPU资源,在有些时候我们需要接收大量数据时且波特率很高的情况下,长时间中断会带来一些额外的问题。

所以以DMA形式配合串口的IDLE(空闲中断)来接收数据将会大大的提高CPU的利用率,减少系统资源的消耗。首先还是先看代码。

#define DMA_USART1_RECEIVE_LEN 18
void USART1_IRQHandler(void)                                 
{     
    u32 temp = 0;  
    uint16_t i = 0;  

    if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)  
    {  
        USART1->SR;  
        USART1->DR; //这里我们通过先读SR(状态寄存器)和DR(数据寄存器)来清USART_IT_IDLE标志    
        DMA_Cmd(DMA1_Channel5,DISABLE);  
        temp = DMA_USART1_RECEIVE_LEN - DMA_GetCurrDataCounter(DMA1_Channel5); //接收的字符串长度=设置的接收长度-剩余DMA缓存大小 
        for (i = 0;i < temp;i++)  
        {  
            Uart2_Buffer[i] = USART1_RECEIVE_DMABuffer[i];  

        }  
        //设置传输数据长度  
        DMA_SetCurrDataCounter(DMA1_Channel5,DMA_USART1_RECEIVE_LEN);  
        //打开DMA  
        DMA_Cmd(DMA1_Channel5,ENABLE);  
    }        
} 

之前的串口中断是一个一个字符的接收,现在改为串口空闲中断,就是一帧数据过来才中断进入一次。而且接收的数据时候是DMA来搬运到我们指定的缓冲区(也就是程序中的USART1_RECEIVE_DMABuffer数组),是不占用CPU时间资源的。

最后在讲下DMA的发送:

#define DMA_USART1_SEND_LEN 64
void DMA_SEND_EN(void)
{
 DMA_Cmd(DMA1_Channel4, DISABLE);      
 DMA_SetCurrDataCounter(DMA1_Channel4,DMA_USART1_SEND_LEN);   
 DMA_Cmd(DMA1_Channel4, ENABLE);
}

这里需要注意下DMA_Cmd(DMA1_Channel4,DISABLE)函数需要在设置传输大小之前调用一下,否则不会重新启动DMA发送。

有了以上的接收方式,对一般的串口数据处理是没有问题的了。下面再讲一下,在ucosiii中我使用信号量+消息队列+储存管理的形式来处理我们的串口数据。先来说一下这种方式对比其他方式的一些优缺点。

一般对串口的处理形式是"生产者"和"消费者"的模式,即本次接收的数据要马上处理,否则当数据大量涌进的时候,就来不及"消费"掉生产者(串口接收中断)的数据,那么就会丢失本次的数据处理。所以使用队列就能够很方便的解决这个问题。

在下面的程序中,对数据的处理是先接收,在处理,如果在处理的过程中,有串口中断接收数据,那么就把它依次放在队列中,队列的特征是先进先出,在串口中就是先处理先接收的数据,所以根据生产和消费的速度,定义不同大小的消息队列缓冲区就可以了。缺点就是太占用系统资源,一般51单片机是没可能了。

下面是从我做的项目中截取过来的程序:

OS_MSG_SIZE  Usart1_Rx_cnt;          //字节大小计数值
unsigned char Usart1_data;           //每次中断接收的数据
unsigned char* Usart1_Rx_Ptr;        //储存管理分配内存的首地址的指针
unsigned char* Usart1_Rx_Ptr1;       //储存首地址的指针

void USART1_IRQHandler() 
{
 OS_ERR err;
 OSIntEnter();

  if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) != RESET) //中断产生 
  {   
    USART_ClearFlag(USART1, USART_FLAG_RXNE);     //清除中断标志

    Usart1_data = USART_ReceiveData(USART1);     //接收串口1数据到buff缓冲区

  if(Usart1_data =='+')                     //接收到数据头标识
  {
//   OSSemPend((OS_SEM*  )&SEM_IAR_UART,  //这里请求信号量是为了保证分配的存储区,但一般来说不允许
//   (OS_TICK  )0,                   //在终端服务函数中调用信号量请求但因为
//   (OS_OPT   )OS_OPT_PEND_NON_BLOCKING,//我OPT参数设置为非阻塞,所以可以这么写
//   (CPU_TS*  )0,
//   (OS_ERR*  )&err); 
//   if(err==OS_ERR_PEND_WOULD_BLOCK)     //检测到当前信号量不可用
//   {
//     printf("error");
//   }    
   Usart1_Rx_Ptr=(unsigned char*) OSMemGet((OS_MEM*)&UART1_MemPool,&err);//分配存储区
   Usart1_Rx_Ptr1=Usart1_Rx_Ptr;          //储存存储区的首地址
  }
  if(Usart1_data == 0x0a )       //接收到尾标志
  {                    
   *Usart1_Rx_Ptr++=Usart1_data;
   Usart1_Rx_cnt++;                         //字节大小增加
   OSTaskQPost((OS_TCB    *  )&Task1_TaskTCB,
                                   (void      *  )Usart1_Rx_Ptr1,    //发送存储区首地址到消息队列
                                   (OS_MSG_SIZE  )Usart1_Rx_cnt,
                                   (OS_OPT       )OS_OPT_POST_FIFO,  //先进先出,也可设置为后进先出,再有地方很有用
                                   (OS_ERR    *  )&err);

   Usart1_Rx_Ptr=NULL;          //将指针指向为空,防止修改
   Usart1_Rx_cnt=0;      //字节大小计数清零
  }
  else
  {
   *Usart1_Rx_Ptr=Usart1_data; //储存接收到的数据
   Usart1_Rx_Ptr++;
   Usart1_Rx_cnt++;
  } 
 }    
 OSIntExit();
}

上面被注释掉的代码为了防止当分区中没有空闲的存储块时加入信号量,打印出报警信息。当然我们也可以将存储块直接设置大一点,但是还是无法避免当没有可用存储块时会程序会崩溃现象的发生。希望懂的朋友能告知下~。

下面是串口数据处理任务,这里删去了其他代码,只把他打印出来了而已。

void task1_task(void *p_arg)
{
 OS_ERR err;
 OS_MSG_SIZE Usart1_Data_size;
 u8 *p;

 while(1)
 {
  p=(u8*)OSTaskQPend((OS_TICK  )0, //请求消息队列,获得储存区首地址
   (OS_OPT    )OS_OPT_PEND_BLOCKING,
   (OS_MSG_SIZE* )&Usart1_Data_size,
   (CPU_TS*   )0,
   (OS_ERR*   )&err);

  printf("%s\r\n",p);        //打印数据

  delay_ms(100);
  OSMemPut((OS_MEM* )&UART1_MemPool,    //释放储存区
  (void*   )p,
  (OS_ERR*  )&err);

  OSSemPost((OS_SEM* )&SEM_IAR_UART,    //释放信号量
  (OS_OPT  )OS_OPT_POST_NO_SCHED,
  (OS_ERR* )&err);

  OSTimeDlyHMSM(0,0,1,500,OS_OPT_TIME_PERIODIC,&err);     
 }
}

作者:可以吃的鱼
原文:
https://blog.csdn.net/qq_35281599/article/details/80299770
声明:本文系网络转载,版权归原作者所有。

围观 93

1. 什么是Bootloader

Bootloader是硬件启动的引导程序,是运行操作系统的前提。在操作系统内核或用户应用程序运行之前运行的一段小代码。对硬件进行相应的初始化和设定,最终为操作系统准备好环境。

2. Bootloader的特点

Bootloader不属于操作系统,一般采用汇编语言和C语言开发。需要针对特定的硬件平台编写。在移植过程时,首先为开发板移植Bootloader。Bootloader不但依赖于CPU的体系架构,而且依赖于嵌入式系统板级设备的配置。

3. STM32中bootloader的内存分配

stm32默认的是从0x08000000开始启动程序,所以bootloader也存在于这个地址,大小可以设置。如下图举例分配 48K的大小空间给Bootloader


还有一种分配方式:镜像的备份 Firmware ---> Application Bak ---> SysRest ----> Bootloader -----> Check if new Firmware -----> Move App Bak to App area

这种方式需要更大的存储空间,如果MCU内置FLASH 不够备份Firmware则需要外置Flash,将Firmware备份在外置FLASH。


根据实际MCU的Flash的大小和固件的大小来分配空间。一般可以把固件信息(app固件的StartAddr, EndAddr, FirmwareSize, CRC等)存放在Free Spae.

bootloader的作用一般是用作更新APP,和初始化后设定跳转到对应的APP。如果APP不加更新功能的话也可以直接将APP写入到0x08000000这个地址里。更新程序就是数据包的接收、校验、写入,全部写入完成后检查APP的启动向量为合格就可以跳转到APP里。


4. Bootloader的跳转简单实现

4.1 Bootloader

我基于STM32Cube配置的外设,IDE用的STM32SW4,STM32F103RCT6。

在实现IAP功能前,先实现跳转。这里先不涉及固件更新。

/*FLASH : 0x8000000 --- 0x8040000 Total Size: 256K
*RAM : 0x20000000 --- 0x2000C000 Total Size: 48K
*Bootloader: 0x8000000 --- 0x8008000 Total Size: 32K

*/

1 /* Includes ------------------------------------------------------------------*/
  2 #include "main.h"
  3 #include "stm32f1xx_hal.h"
  4 #include "usart.h"
  5 #include "gpio.h"
  6 
  7 /* USER CODE BEGIN Includes */
  8 #include "stdio.h"
  9 /* USER CODE END Includes */
 10 
 11 /* USER CODE BEGIN PFP */
 12 /* Private function prototypes -----------------------------------------------*/
 13 pFunction jump2app;
 14 void (*jump2app)();
 15 /* USER CODE END PFP */
 16 
 17 
 18 
 19 /* USER CODE BEGIN 0 */
 20 #ifdef __GNUC__
 21     #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
 22 #else
 23     #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
 24 #endif
 25 
 26 /*retargets the C library printf function to the USART*/
 27 PUTCHAR_PROTOTYPE
 28 {
 29     HAL_UART_Transmit(&huart1,(uint8_t*)&ch, 1, 0xFFFF);
 30     return ch;
 31 }
 32 
 33 //FLASH            : 0x8000000  --- 0x8040000       Total Size: 256K
 34 //RAM           : 0x20000000 --- 0x2000C000       Total Size: 48K
 35 //Bootloader     : 0x8000000 --- 0x8008000     Total Size: 32K 
 36   37 #define ApplicationAddress    0x8008000
 38 
 39 
 40 void iap_load_app(uint32_t appAddr)
 41 {
 42     printf("first word : 0x%x\n",(*(uint32_t*)appAddr));
 43     if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)
 44     {
 45         printf("IAP load APP!!!\n");
 46 
 47         __disable_irq();
 48 
 49         jump2app = (void (*)())*(__IO uint32_t*) (appAddr + 4);
 50 
 51         __set_MSP(*(__IO uint32_t*) appAddr);
 52 
 53         jump2app();
 54     }
 55 }
 56 /* USER CODE END 0 */
 57 
 58 /**
 59   * @brief  The application entry point.
 60   *
 61   * @retval None
 62   */
 63 int main(void)
 64 {
 65   /* USER CODE BEGIN 1 */
 66 
 67   /* USER CODE END 1 */
 68 
 69   /* MCU Configuration----------------------------------------------------------*/
 70 
 71   /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
 72   HAL_Init();
 73 
 74   /* USER CODE BEGIN Init */
 75 
 76   /* USER CODE END Init */
 77 
 78   /* Configure the system clock */
 79   SystemClock_Config();
 80 
 81   /* USER CODE BEGIN SysInit */
 82 
 83   /* USER CODE END SysInit */
 84 
 85   /* Initialize all configured peripherals */
 86   MX_GPIO_Init();
 87   MX_USART1_UART_Init();
 88   /* USER CODE BEGIN 2 */
 89 
 90   /* USER CODE END 2 */
 91 
 92   /* Infinite loop */
 93   /* USER CODE BEGIN WHILE */
 94   while (1)
 95   {
 96       printf("I am bootloader,jump to app after 5 seconds!\n");
 97 
 98       HAL_Delay(1000);
 99 
100       printf("1\r\n");
101 
102       HAL_Delay(1000);
103 
104       printf("2\r\n");
105 
106       HAL_Delay(1000);
107 
108       printf("3\r\n");
109 
110       HAL_Delay(1000);
111 
112       printf("4\r\n");
113 
114       HAL_Delay(1000);
115 
116       printf("ready to jump!\n");
117 
118       iap_load_app(ApplicationAddress);
119   /* USER CODE END WHILE */
120 
121   /* USER CODE BEGIN 3 */
122 
123   }
124   /* USER CODE END 3 */
125 
126 }

修改ld文件 STM32F103RCTx_Flash.ld


编译烧录。首先将STM32F103RCT6的FLASH全部擦除如下图,然后用STM32SW4烧录Bootloader


调试Bootloader如下图


4.2 Application

APP主要是修改ld文件,Bootloader分配了 32Kb, 剩余224K的先全分配给App, 实现简单跳转。


int main(void)
{
    //NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x2000);
  /* USER CODE BEGIN 1 */
    SCB->VTOR = ((uint32_t)0x8000000) | (0x8000 & (uint32_t)0x1FFFFF80);
  /* USER CODE END 1 */

  /* MCU Configuration----------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  __enable_irq();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
     printf("I am new APP !\n\r");

     HAL_Delay(1000);
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */

  }
  /* USER CODE END 3 */

}

再将APP烧录,Reset


来源:博客园 - M&D
转载此文目的在于传递更多信息,版权归原作者所有。
原文链接:
https://www.cnblogs.com/mickey-double/p/11718755.html

围观 1673

边缘AI助力智能楼宇实现数字化人流量监测

意法半导体(STMicroelectronics,简称ST;纽约证券交易所代码:STM) 与能源管理和工业自动化数字化转型的市场领导者施耐德电气 (Schneider Electric)联合推出一款物联网传感器原型。通过监测建筑物的居住率和使用率,该解决方案可以实现新型物业管理服务,提高楼宇的能源管理效率。

两家公司合作,在高性能人数统计传感器内集成人工智能(AI)技术,以克服在有多个入口的大型场所内监测人流量的挑战。在2020年11月19日IoT&5G研讨会期间,施耐德电气将以嘉宾身份参加ST Live Days活动,演示这款物联网传感器解决方案的功能。

随着楼宇及人居管理数字化的提升,施耐德致力于成为客户可持续发展和提高能源效率的数字合作伙伴,为客户提供有新意、有价值的洞见,如,协助智能楼宇管理的排队监控,同时在设计上充分尊重个人隐私。该先进的物联网传感器解决方案将意法半导体AI产品部的先进专业知识与施耐德深厚的传感器应用专长相结合,将高性能的物体检测神经网络嵌入到经济型微控制器(MCU)中。

施耐德电气通过使用STM32Cube.AI工具链提高了设计效率,该工具链具有成熟的功能,可为各种STM32 MCU开发AI应用。STM32Cube软件开发生态系统的工程资源、先进技术和易用性为施耐德电气提供了宝贵的硬件设计灵活性和效率。

该人数统计原型传感器整合了施耐德电气独有的超低功耗LYNRED ThermEyeTM系列热成像传感器,意法半导体最近推出的高性能STM32H723 MCU,以及基于Yolo的神经网络模型。

施耐德电气物联网传感器项目经理Maxime Loidreau表示:“这项前景广阔的技术解锁一项新的楼宇居住率监测和人流量统计方案,适用于监测排队、建筑使用率、社交距离等多种应用场景。我们与ST合作开发的创新的演示原型不仅适用于酒店、办公室、百货零售等场所,也适用任何需要掌握人流量及空间使用率等情况的任何场景。这将重新定义未来建筑!”

意法半导体的AI解决方案业务线经理Miguel Castro补充说:“该项目证明深度学习能够提升嵌入式数据处理性能,展示了如何在基于经济型微控制器的系统平台上运行高价值的应用软件。我们的STM32Cube.AI生态系统使用户能够在很短的时间内开发出灵活的解决方案,利用我们技术团队的支持服务克服工程挑战,客户可以享受更高的设计效率。”

技术详情

STM32 AI生态系统提供了在STM32 MCU上运行神经网络所需的基本模块,可以实现经济、节能的解决方案,原生支持各种深度学习框架,例如:Keras、TensorFlow™Lite和ONNX交换格式。

在这个开发生态系统中,X-CUBE-AI软件扩展包扩大了STM32CubeMX初始化工具的功能,可以自动转换预先训练的神经网络,为目标MCU生成优化的软件库,并集成到用户的项目中。此外,还有很多其他的自动化功能,把开发者从繁重的开发任务中解放出来,例如,可以验证神经网络模型并测量STM32 MCU的性能,而无需人工开发C代码。

意法半导体的软件开发生态系统支持的普通DNN方案已经映射到STM32丰富的产品组合内,使用户可以有效地复制开发成果,为多个市场创建产品。在ST Live Days上运行演示应用的STM32H723 MCU完全胜任运行AI应用,因为这款产品的内核性能优异,闪存容量高达1Mbyte,提供高速片外存储器接口,以及各种传感器接口。

了解STM32Cube.AI更多信息,请访问 www.st.com/STM32CubeAI

了解STM32H7 MCU详情,请访问:https://www.st.com/en/microcontrollers-microprocessors/stm32h7-series.html

了解如何在STM32微控制器和应用处理器上运行边缘AI应用,请联系我们:edge.ai@st.com

关于意法半导体

意法半导体拥有46,000名半导体技术、产品和方案的创新者和创造者,掌握半导体供应链和最先进的制造设备。作为一家独立的半导体设备制造商,意法半导体与十万余客户、上千合作伙伴一起研发产品和解决方案,共同构建生态系统,帮助他们更好地应对各种挑战和新机遇,满足世界对可持续发展的更高需求。意法半导体的技术让人们的出行更智能,电力和能源管理更高效,物联网和5G技术应用更广泛。详情请浏览意法半导体公司网站:www.st.com

围观 40

2020年10月20日——意法半导体新推出的STM32 * Nucleo Shield显示板卡开创物联网产品人机界面之先河。新SPI Shield显示板卡X-NUCLEO- GFX01M1利用STM32G0微控制器(MCU)的经济性,支持引入低成本非内存映射SPI闪存IC支持等新功能的最新版TouchGFX软件(4.15.0版)。

如果采用STM32G0和TouchGFX开发项目,开发人员可用仅5美元的物料清单成本,给任何项目添加一个小图形界面屏幕,这样,定时器、控制器、家用电器等简单设备也能为用户带来类似智能手机的使用体验。

新的X-Nucleo-GFX01M1 Shield显示板卡支持新的X-cube-display软件包,该软件包提供了简单的“ hello world”界面例程。这款Shield板卡集成一块2.2英寸QVGA(320x240)SPI显示屏、64 Mbit SPI NOR闪存和一个控制手柄,可以与NUCLEO-G071RB等各种STM32 MCU开发板配套使用,STM32G071RB是一款主流的Arm®Cortex®-M0+ MCU,集成高达128KB闪存、36KB SRAM、各种通信接口、模拟外设、快速I/O端口、硬件安全ID和一个USB Type-C™Power Delivery控制器。

最新版TouchGFX软件基于TouchGFX引擎的部分帧缓存方法,可以将GUI 占用的RAM空间降低多达90%,并允许在只有16-20KB的MCU RAM内存中实现简单的用户界面。新版软件采用一种新的渲染算法增强GUI性能,通过一个优化的顺序先更新部分屏幕,然后再完成额外的帧更新,从而避免了分散注意力的撕裂视觉效果。另外一个新增功能是支持非内存映射SPI 闪存,使更复杂的GUI可以把图像、字体等占用大量内存空间的图形资源存放在低成本的外部存储器中。

为了简化用户界面原型设计,TouchGFX Designer还提供了为STM32G071 Nucleo开发板和显示开发套件优化的应用模板。必要时还可以把一个RTOS系统导入设置中,然后用TouchGFX Generator工具更换硬件。

所有软件组件现在都可以下载使用,包括X-cube-display软件包和TouchGFX 4.15.0,以及在G071RB开发板上运行的代码示例。X-NUCLEO- GFX01M1和STM32G0产品已批量生产,可通过正常的意法半导体代理渠道购买。

此外,还有一个新的图形小工具,可以简化使用线、柱状图、面积图、直方图或组合图显示顺序数据。这个小程序可以在任何一个STM32 MCU上运行顺畅运行。开发人员可以使用TouchGFX Designer自定义颜色、布局等参数。

对STM32H725的全面的即用支持也是TouchGFX 4.15.0的新特性,使开发人员可以在

意法半导体的Cortex-M7 MCU上运行微处理器级的图形。STM32H725是STM32系列最新的图形应用旗舰产品,搭载550MHz处理器内核,采用意法半导体的Chrom-ART Accelerator™图形加速技术,可以提供更快的图形处理性能;8针SPI接口用于高速连接外部闪存和RAM,以及XGA TFT-LCD显示控制器。TouchGFX Designer包含例程源代码,点击此处可以下载演示视频。

了解更多信息,免费下载软件,请访问http://www.st.com/x-cube-touchgfx

或访问 https://blog.st.com/x-nucleo-gfx01m1/ 阅读相关博文。

关于意法半导体

意法半导体拥有46,000名半导体技术、产品和方案的创新者和创造者,掌握半导体供应链和最先进的制造设备。作为一家独立的半导体设备制造商,意法半导体与十万余客户、上千合作伙伴一起研发产品和解决方案,共同构建生态系统,帮助他们更好地应对各种挑战和新机遇,满足世界对可持续发展的更高需求。意法半导体的技术让人们的出行更智能,电力和能源管理更高效,物联网和5G技术应用更广泛。详情请浏览意法半导体公司网站:www.st.com

围观 63

页面

订阅 RSS - STM32