STM32_IAP详解

发布者:rocky96最新更新时间:2017-01-12 来源: eefocus关键字:STM32  IAP 手机看文章 扫描二维码
随时随地手机看文章

  Iap,全名为in applacation programming,即在应用编程,与之相对应的叫做isp,in system programming,在系统编程,两者的不同是isp需要依靠烧写器在单片机复位离线的情况下编程,需要人工的干预,而iap则是用户自己的程序在运行过程中对User Flash 的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。在工程应用中经常会出现我们的产品被安装在某个特定的机械结构中,更新程序的时候拆机很不方便,使用iap技术能很好地降低工作量.

  实现iap有两个很重要的前提,首先,单片机程序能对自身的内部flash进行擦写,第二,单片机要有能够和外部进行通讯的方式,无论是网络还是别的方式,只要能传输数据就行

  通常实现 IAP 功能时,即用户程序运行中作自身的更新操作,需要在设计固件程序时编写两个项目代码,第一个项目程序不执行正常的功能操作,而只是通过某种通信方式(如 USB、 USART)接收程序或数据,执行对第二部分代码的更新;第二个项目代码才是真正的功能代码。这两部分项目代码都同时烧录在 User Flash 中,当芯片上电后,首先是第一个项目代码开始运行,它作如下操作: 
1)检查是否需要对第二部分代码进行更新 
2)如果不需要更新则转到 4) 
3)执行更新操作 
4)跳转到第二部分代码执行 
    第一部分代码必须通过其它手段,如 JTAG 或 ISP 烧入;第二部分代码可以调用第一部分的功能

也就是说,将iap和app做成两个程序,这是其中的一种策略,还有一种策略,可以把iap程序和app程序做在一个代码中,但是那样耦合性有点高,我们先进行第一种尝试.

要做iap首先我们要知道stm32的启动流程,流程如下

  1. 单片机从0x80000000位置启动,并将该地址当成系统栈顶地址

  2. 运行到中断向量表中,默认的中断向量表为0x80000004,该位置存放复位中断

  3. 跳转到复位中断处理函数当中,进行系统初始化,然后运行main函数

 

  当我们准备用iap的时候,单片机内部是有着两套程序的,这个时候我们就需要在iap中

  和app中分别放置两套中断向量表,当iap代码中将app烧写到flash中之后,跳转到app的中断向量表中,程序就可以正常执行了,当然需要修改某些系统设置,使得在app和iap阶段单片机可见的中断向量表只能有一套(具体请查看stm32芯片的启动代码)

  而当需要从app跳转到iap的时候,只需要将app的中断向量表修改成iap的中断向量表,同时主动跳转到iap的reset中断处理程序,这样就能再次开始iap流程.

  这样,在系统中就需要我们确定几个东西,第一个是iap程序的中断向量表,为0x80000004位置(80000000存放的是msp的初始值),第二个是app程序的中断向量表,该位置需要根据iap程序的长度计算,比如iap占用了64K,那么512K的芯片而言,就还有448K的空间存放app程序,448K的最开始放置中断向量表,位置就应该是0x08000000+0x10004的位置.

Cortex-m3的中断向量并不是在程序中固定的,我们可以通过修改某些寄存器来修改对于当前应用的中断向量表位置.

决定中断向量表的寄存器是如下这个

 

  通过修改这个寄存器的值,我们可以控制对于当前单片机应用来说可见的向量表的位置(也就说说逻辑上我们有两个向量表,但是同一时间只有一个运行)

  以上是内核阶段的操作,在此之外我们还需要对stm32的flash进行编程,那么就涉及到删除的编程和擦除操作,这需要参考stm32的闪存编程手册

  首先,当单片机复位之后,闪存式被锁住的,需要主动去解锁,向FLASH_KEYR写入两个指定的连续键值用于解锁

  然后将需要写入的闪存擦除,擦除之后在进行写入,写入完成,再次上锁

对应的代码如下

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+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);//读出整个扇区的内容

        for(i=0;i

        {

            if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;//需要擦除    

        }

        if(i

        {

            FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);//擦除这个扇区

            for(i=0;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();//上锁

 

  该函数可以实现flash的写入操作,接下来我们需要定义一套通讯协议用于串口数据传输

//串口接收缓冲区

u8 serial_Buffer[SERIAL_MAX_LENGTH] = {0};

//串口接收数据长度

u16 serial_Buffer_Length = 0;

 

u8 receiveMode = 0;//接收参数的中断处理模型,为0的时候是命令模式,为1的时候为下载模式

u8 receiveExpectCount = 0;//串口期望接收长度

 

 

//串口中断处理

static void SerialRecv(u8 ch)

{

    if(receiveMode == 0)

    {

       if((serial_Buffer_Length&0x8000) == 0x8000)//已经接收完成,系统还没处理

       {

           serial_Buffer_Length |= 0x8000;//退出

       }

       else if((serial_Buffer_Length&0x4000) == 0x4000)//接收到回车还没接收到换行

       {

           if(ch == '\n')serial_Buffer_Length |= 0x8000;

           else

           {

              //一帧接受失败

              serial_Buffer_Length = 0;

           }

       }

       else

       {

           if((serial_Buffer_Length&0xff) < SERIAL_MAX_LENGTH)

           {

              if(ch == '\r')serial_Buffer_Length |= 0x4000;

              else

              {

                  serial_Buffer[(serial_Buffer_Length&0xff)] = ch;

                  serial_Buffer_Length++;

              }

           }

           else

           {

              //一帧接受失败

              serial_Buffer_Length = 0;

           }

       }

    }

    else

    {

       //下载模式,只控制字符串的量,数据的第一位是该数据包的长度,接收到这么多长度,接收完成位置一

       //注意,在这种模式下,清除serial_Buffer_Length之前应当清除receiveExpectCount的值

       if(receiveExpectCount == 0)//期望下载为0,第一个数就是期望下载数

       {

           receiveExpectCount = ch;

       }

       else

       {

           if((serial_Buffer_Length&0x8000) == 0x8000)//已经接收完成,系统还没处理,此时不接收数据

           {

              serial_Buffer_Length |= 0x8000;//退出

           }

           else

           {

              serial_Buffer[(serial_Buffer_Length&0xff)] = ch;//接收数据并保存

              serial_Buffer_Length++;

              if((serial_Buffer_Length&0xff) == receiveExpectCount)//接收到了期望长度的数据

              {

                  serial_Buffer_Length |= 0x8000;//一包接收完成标志

              }

           }

       }

      

    }

}

 

  这样系统就能接收数据了,接下来定义五个命令

  "iap_down"

  "iap_jump_app"

  "iap_over"

  "iap_set_flag"

  "iap_clear_flag"

第一个命令为系统开始下载,在这个命令之后上位机就能够将程序数据发下来了,

第二个命令为iap跳转到app的跳转指令

第三个命令是指示iap完成,将系统缓冲区清空的指令

第四个指令为设置app标志,当iap检测到该标志的时候直接跳转到app程序中

第五个指令为清除app标志,让iap程序不自动跳转到app程序中,我们分别来看

  首先是iap_set_flag,其响应函数如下

#define APP_CONFIG_ADDR     0X08001FFC //配置地址

#define APP_CONFIG_SET_VALUE    0X5555 //设置值

#define APP_CONFIG_CLEAR_VALUE  0XFFFF //清零值

 

//设置app固化配置

void iap_set_flag_s(void)

{

    Test_Write(APP_CONFIG_ADDR,APP_CONFIG_SET_VALUE);

    printf("ok\r\n");

}

我们使用0x08000000-0x08003000来存放iap代码,并将0X08001FFC作为存放app固化标志的地方

 

//清除app固化配置

void iap_clear_flag(void)

{

    Test_Write(APP_CONFIG_ADDR,APP_CONFIG_CLEAR_VALUE);

    printf("ok\r\n");

}

 

对iap_jump2app命令的响应如下

//跳转到应用程序段

//appxaddr:用户代码起始地址.

void iap_load_app(u32 appxaddr)

{

    if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000)  //检查栈顶地址是否合法.0x20000000是sram的起始地址,也是程序的栈顶地址

    {

       printf("ok\r\n");

       Delay_Ms(10);

       jump2app=(iapfun)*(vu32*)(appxaddr+4);    //用户代码区第二个字为程序开始地址(复位地址)     

       MSR_MSP(*(vu32*)appxaddr);                //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)

       jump2app();                               //跳转到APP.

    }

    else

    {

       printf("program in flash is error\r\n");

    }

}

 

//跳转到app区域运行

void iap_jump_app_s(void)

{

    iap_load_app(FLASH_APP1_ADDR);//跳转到app的复位向量地址

}

接下来就是iap_down,用于下载的核心算法

#define FLASH_APP1_ADDR     0x08002000     //第一个应用程序起始地址(存放在FLASH)

                                       //保留的空间为IAP使用

 

u16 iapbuf[1024] = {0}; //用于缓存数据的数组

u16 receiveDataCur = 0;  //当前iapbuffer中已经填充的数据长度,一次填充满了之后写入flash并清零

u32 addrCur = FLASH_APP1_ADDR;         //当前系统写入地址,每次写入之后地址增加2048

 

//开始下载

void iap_down_s(void)

{

    u16 i = 0;

    u16 temp = 0;

    u16 receiveCount;

    printf("begin,wait data download\r\n");

    receiveMode = 1;//串口进入下载接收数据模式

    while(1)

    {

       //循环接收数据,每次必须要发128个数据下来,如果没有128,说明这是最后一包数据

       //接收到一包数据之后,返回一个小数点,发送完成,系统编程完成之后返回一个iap_over

       if(serial_Buffer_Length & 0x8000)

       {

           receiveCount = (u8)(serial_Buffer_Length&0x00ff);

           if(receiveCount == 128)//满足一包,填充并查看是否有了1024字节,有了写入闪存

           {

              for(i = 0; i < receiveCount; i+=2)

              {

                  //数据八位融合为16位

                  temp = (((u16)serial_Buffer[i+1])<<8) + ((u16)serial_Buffer[i]);

                  iapbuf[receiveDataCur] = temp;

                  receiveDataCur++;//完成之后receiveDataCur++;

              }

              receiveExpectCount = 0;//清除期望接收模式

              serial_Buffer_Length = 0;//清除串口满标志

              printf(".");//每次接受一次数据打一个点

              //此时需要检测receiveDataCur的值,要是放满了,就需要写入

              if(receiveDataCur == 1024)

              {

                  //写入flash中

                  STMFLASH_Write(addrCur,iapbuf,1024);

                  //printf("\r\nwrite addr %x,length 1024\r\n",addrCur);

                  addrCur += 2048;//地址+2048

                  //写完之后receiveDataCur要清零等待下一次传输

                  receiveDataCur = 0;

              }

              else //有可能最后一包有128个数据但是最终没有2048个数据,此时扩展一个指令用于完成最后一个的写入

              {

                 

              }

              //还没放满,等待下一次数据过来

           }

           else   //不满足一包,说明数据传送这是最后一包,写入闪存

           {

              //没有一包也要传送到缓存中

              for(i = 0; i < receiveCount; i+=2)

              {

                  //数据八位融合为16位

                  temp = (((u16)serial_Buffer[i+1])<<8) + ((u16)serial_Buffer[i]);

                  iapbuf[receiveDataCur] = temp;

                  receiveDataCur++;//完成之后receiveDataCur++;

              }

              receiveExpectCount = 0;//清除期望接收模式

              serial_Buffer_Length = 0;//清除串口满标志

              printf(".");//每次接受一次数据打一个点

              //之后就要将这数据写入到闪存中

              STMFLASH_Write(addrCur,iapbuf,receiveDataCur);//将最后的一些内容字节写进去.

              //printf("\r\nwrite addr %x,length %d\r\n",addrCur,receiveDataCur);

              //写完之后要把地址恢复到原来的位置

              addrCur = FLASH_APP1_ADDR;

              receiveDataCur = 0;

              //写完之后要退出下载循环并告诉上位机,已经下载完了

              printf("download over\r\n");

              //同时,也要退出下载循环模式

              receiveMode = 0;

              return;

           }

    这段代码的核心思想是上位机每次发送128个数据下来,发满了2048个写一次flash,当最后一包数据不是128的时候说明数据发送完成了,这时候退出下载模式,但是当遇到最后一包数据也是128个时候怎么办呢,于是定义了这个指令

  iap_over,上位机侦测到最后一包数据也是128个的时候补充发送该命令,下位机将缓存写入并退出

//最后一包有128个数据但是最终没有2048个数据

//收到这个指令检测receiveDataCur和addrCur的值,

//完成最后的写入

void iap_over_s(void)

{

    //这个时候,依然在串口下载模式

    if(receiveDataCur != 0)

    {

       STMFLASH_Write(addrCur,iapbuf,receiveDataCur);//将最后的一些内容字节写进去.

       //printf("write addr %x,length %d",addrCur,receiveDataCur);

       addrCur = FLASH_APP1_ADDR;

       receiveDataCur = 0;

       //同时,也要退出下载模式

       receiveMode = 0;

    }

    printf("ok\r\n");

}

 

这是iap的核心代码,接下来我们在main函数中检测app固化标志,如果标志位设置,那么跳转到app

if(STMFLASH_ReadHalfWord(APP_CONFIG_ADDR) == 0x5555)

{

    //直接跳转到APP

    iap_jump_app_s();

}

 

  到这里基本上就完成了iap的工作,可是想想,还需要设置一个地方,我们要在target中设置使用的flash空间,不能超范围,如下

 

如果需要flash下载的话还需要设置jlink的flash下载设置如下.

 

 

 

  这样可以直接使用jlink将代码下载到单片机中,而且不会影响原先的app程序,注意,要选择erase sector used,不能全部擦除flash

 

  桥斗麻袋,我们忘了一件事情,假设我们设置了app标志,那及时app能跳转到iap中,iap岂不是马上会跳转回app,永远不能等待下载?

  解决办法就是我们在app中app跳转到iap的指令中将app固化标志清除掉,在app代码中添加一条指令

  Iap,其响应方法为

 

__asm void MSR_MSP(u32 addr)

{

    MSR MSP, r0           //set Main Stack value

    BX r14

}

 

 

void iap_jump(u32 iapxaddr)

{

    if(((*(vu32*)iapxaddr)&0x2FFE0000)==0x20000000)  //检查栈顶地址是否合法.0x20000000是sram的起始地址,也是程序的栈顶地址

    {

       printf("ok\r\n");

       Delay_Ms(10);

       jump2iap=(iapfun)*(vu32*)(iapxaddr+4);    //用户代码区第二个字为程序开始地址(复位地址)     

       MSR_MSP(*(vu32*)iapxaddr);                //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)

       jump2iap();                               //跳转到APP.

    }

    else

    {

       printf("iap program loss,please check\r\n");

    }

}

 

#define APP_CONFIG_ADDR     0X08001FFC //配置地址

#define APP_CONFIG_SET_VALUE    0X5555 //设置值

#define APP_CONFIG_CLEAR_VALUE  0XFFFF //清零值

 

void iap_Func(void)

{

    Test_Write(APP_CONFIG_ADDR,APP_CONFIG_CLEAR_VALUE);

    iap_jump(FLASH_IAP_ADDR);//跳转到iap的复位向量地址

}

 

  可以看到,我们先清除了app标志,然后在跳转到iap程序中,就不会影响到iap的流程了,同时app代码也还在单片机里面,另外,app工程里面也要设置两个东西

 

因为flash的起始地址为0x08000000,而我们用了之前2000的空间作为iap代码空间,那么,app代码的起始空间就变成了0x8002000,还有一个下载界面需要设置

 

  红框部分也要修改.

  是不是没有说中断向量表的问题,在iap中我们不需要考虑中断向量表,因为默认就是在0x8000000位置的,但是在app中代码的起始位置变了,必须重新设置中断向量表

在system_stm32f10x.c中有一个system_init函数,该函数被启动代码调用,配置系统时钟,在该函数中的最后一句为

#ifdef VECT_TAB_SRAM

  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */

#else

  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */

#endif

其中VECT_TAB_OFFSET就是我们要定义的偏移量,也就是app程序的起始地址偏移,我们知道是2000,那么该值的宏就需要修改,在大约128行的位置

//此处为flash偏移地址,app应当修改这个地址

#define VECT_TAB_OFFSET  0x2000 /*!< Vector Table base offset field.

                                  This value must be a multiple of 0x200. */

  嗯,完整流程就是这样了,另外,该工程分为三个部分,一个iap,一个app,还有一个当然是下载程序啦,下载程序是这样的

 

  三个代码的工程我会打包上传到csdn,想更深入了解的可以下载来看看,软件用mfc编写的

最后,下载需要使用bin文件,该文件的生成方法参考另一篇博文,lpc1768-iap

代码打包上传地址

http://download.csdn.net/detail/dengrengong/8499911


关键字:STM32  IAP 引用地址:STM32_IAP详解

上一篇:LPC1768的USB使用-枚举过程
下一篇:STM32音乐播放器,文件查找的实现

推荐阅读最新更新时间:2024-03-16 15:30

利用STM32的FLASH模拟 EEPROM(F103)系列
STM32的FLASH是用来存储主程序的,ST公司为了节约成本,没有加入 EEPROM,但是许多场合下我们需要用EEPROM;不过FLASH的容量还是可观的,我们可以利用FLASH模拟EEPROM。 根据《STM32F10X闪存编程》中的介绍,FLASH除了保存用户代码的部分,其余部分我们是可以利用其作为数据存储使用的。stm32的FLASH分为主存储块和信息块。主存储块用于保存具体的程序代码和用户数据,信息块用于负责由stm32出厂是放置2KB的启动程序(Bootloader)并锁死,用户无法更改。选项字节存储芯片的配置信息及对主存储块的保护信息。STM32的FLASH主存储块按页组织,有的产品每页1KB,有的产
[单片机]
利用<font color='red'>STM32</font>的FLASH模拟 EEPROM(F103)系列
STM32-systick系统定时器
systick系统定时器 系统定时器存在内核中,是24位的定时器,只能向下递减,嵌套在NVIC中 counter 在时钟的驱动下 在reload的初值开始向下递减计时到0,产生中断置位标志然后又从reload值开始重新递减计数,循环 定时时间计算 t=reload*(1/clk) clk=72M时,t=72*(1/72m)=1us clk=72M时,t=72000*(1/72m)=1ms clk=72M时,t=72000000*(1/72m)=1s 1s=1000MS =1000 000US=1000 000 000NS sysTick属于内核中的外设,他的中断优先级和外设的中断优先级相比,哪个
[单片机]
STM32-systick系统定时器
STM32】HAL库 STM32CubeMX教程三----外部中断(HAL库GPIO讲解)
前言 上一节我们讲解了STM32CubeMX的基本使用和工程的配置,那么这一节我们正式来学习CubeMX配置STM32的各个外设功能了 今天我们会详细的带你学习STM32CubeMX配置外部中断,并且讲解HAL库的GPIO的各种函数,带你学习不一样的STM32 那么话不多说,我们开始正式的讲解吧! 准备工作: 1、STM32开发板(我的是STM32F407ZE和STM32F103RC) 2、STM32CubeMx软件、 IDE: Keil软件 3、STM32F1xx/STM32F4xxHAL库 4、按键管脚 PA11 PB4 5 LED管脚 PC4 PC5 PB0 PB1 1新建工程 1.1New Pr
[单片机]
【<font color='red'>STM32</font>】HAL库 STM32CubeMX教程三----外部中断(HAL库GPIO讲解)
STM32之SPI库函数介绍
首先SPI的一些基本介绍参照相关芯片的库函数或者使用手册,以下介绍SPI库函数的基本用法和参数的定义。 1.SPI_DeInit函数的功能是将外设SPIx寄存器重设为默认值。输入参数SPIx可以是1和2,是用来选择SPI外设的。 2.SPI_Init函数的功能是根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器。SPI_InitDefType的结构体是定义在文件stm32f10x_spi.h里。 typedef struct { u16 SPI_Direction;//用于设置SPI单向或者双向数据模式。 u16 SPI_Mode;//用于设置SPI的工作模式; u16 SPI_Dat
[单片机]
STM32上创建链表并实现LCD滚动显示串口消息
在实现STM32开发ESP8266的时候发现ESP8266串口发送的消息行数很多, 如果使用普通的数组来存储消息需要大量的存储开销, 并且数据的显示也会损耗MCU的处理速度, 故而实现对消息的传输装入一个可以动态拓展, 并且具有灵活的调用形式的容器. 链表理所当然成为首选. 关于C语言链表的相关操作本文不再详细叙述, 若有需求请移步网址:https://blog.csdn.net/morixinguan/article/details/68951912先学习链表后再来学习在STM32创建链表. 首先粘贴STM32上链表.c文件的代码: //////roll_display.c的代码 #include roll_di
[单片机]
在<font color='red'>STM32</font>上创建链表并实现LCD滚动显示串口消息
STM32 HAL库 printf 串口重定向
在对printf重定向之前,一定不要有printf,否则程序马上跑飞。 在main函数之前加上如下代码对串口进行重定向,当然,串口一定要初始化之后再用printf,否则程序虽然不会飞,但是printf也不会有结果 #ifdef __GNUC__ #define PUTCHAR_PROTOTYPE int __io_putchar(int ch) #else #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) #endif PUTCHAR_PROTOTYPE { //具体哪个串口可以更改huart1为其它串口 HAL_UART_Transmit(&huart1 , (
[单片机]
关于STM32的USART_GetFlagStatus和USART_GetITStatus解析
前言 STM32固件库中提供了串口收发的标志位函数,包括USART_GetFlagStatus(…,…);和USART_GetITStatus(…,…);,两者容易混淆,重点区别就在于:前者返回值是中断标志位状态(读SR寄存器),后者返回值是中断发生与否的判断(读CR寄存器),以下主要对这两个函数进行分析。 一、USART_GETFlagStatus(…,…) /** * @brief Checks whether the specified USART flag is set or not. * @param USARTx: Select the USART or the UART peripheral. * Th
[单片机]
关于<font color='red'>STM32</font>的USART_GetFlagStatus和USART_GetITStatus解析
意法半导体二代STM32微处理器推动智能边缘发展,提高处理性能和工业韧性
新STM32MP2 MPUs搭载64位处理器和边缘 AI加速器 与生俱来的速度、安全性和可靠性 依托STM32生态系统,加快应用开发,安全配置网络 2024年3月12日,中国 -- 服务多重电子应用领域、全球排名前列的半导体公司意法半导体(STMicroelectronics,简称ST)发布了新一代的STM32MP2系列工业级微处理器 (MPUs),以推动智能工厂、智能医疗、智能楼宇和智能基础设施等领域未来的发展。 数字化转型席卷全球,它推动企业提高生产效率、改善医疗服务质量,加强楼宇、公用设施和交通网络的安全和能源管理。数字化的核心赋能技术包括云计算、数据分析、人工智能 (AI)和物联网 (IoT)
[单片机]
意法半导体二代<font color='red'>STM32</font>微处理器推动智能边缘发展,提高处理性能和工业韧性
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved