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微控制器的性能和功能优势。

串口发送数据

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等的时候,需考虑函数重入的问题,相关推荐:使用STM32CubeMx工具,写FreeRTOS的demo程序

当然也可以简单的实现把该函数复制一下,然后修改串口号也可以避免该问题。然而这个函数不能像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不行的。需要加上以下的重映射函数:

“STM32串口发送数据和接收数据方式总结"

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

“STM32串口发送数据和接收数据方式总结"

串口接收数据

串口接收最后应有一定的协议,如发送一帧数据应该有头标志或尾标志,也可两个标志都有,串口其他相关文章:学习STM32单片机,绕不开的串口。

这样在处理数据时既能能保证数据的正确接收,也有利于接收完后我们处理数据。串口的配置在这里就不在赘述,这里我以串口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);     
 }
}

来源:STM32嵌入式开发
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 621

以45.8mm x 8.3mm 的纤薄主板为亮点,意法半导体STEVAL-IOD04KT1工业传感器套件可简化开发者为独立于现场总线的点对点双向通信应用开发紧凑的IO-Link (IEC 61131-9) 传感器。

“意法半导体发布工业智能传感器评估套件,加快基于IO-Link

该主板集成了意法半导体的STM32G0微控制器和L6364W IO-Link收发器、IIS2MDC高精度3轴数字输出磁力计,以及内嵌机器学习核心的ISM330DHCX iNEMO惯性测量模块。得益于L6364W的2.5mm x 2.5mm 的CSP19微型芯片级封装和 STM32G0的2.3mm x 2.5mm的WLCSP25封装 ,板子的紧凑尺寸可以用外壳极小的传感器。板子配备一个 4 针 M8 工业接口,可连接到任何支持 IO-Link 1.1 的 IO-Link 主控制器。10 针扩展接口可以让板子连接更多的不同模式的传感器。

配套的 STM32Cube 软件包 STSW-IOD04K 提供 IO-Link 设备描述 (IODD) 文件、意法半导体专有 IO-Link 演示软件栈,以及用于管理 L6364W和MEMS 传感器的例程。软件包支持热插拔激活,包含有助于开发各种类型传感器的函数库,软件架构可与其他 X-CUBE 软件轻松集成,以进一步扩展传感器的功能。

因为能够处理 IO-Link 通信功能,L6364W 收发器减轻了 STM32G0 的工作负荷。芯片还实现了符合 IO-Link 标准的电气接口和数字功能,包括唤醒识别、15 字节数据缓冲区和无晶振 IO-Link 时钟提取功能。片上集成最高±2.5kV 的浪涌脉冲保护、ESD 保护和反极性保护等安全功能,无需用户安装额外的器件,并节省 PCB 空间和物料清单成本。L6364W片上 集成3.3V 和 5.0V LDO稳压器,以及可数字配置的降压 DC/DC变换器,可提供高达 50mA 的负载电流,帮助开发人员满足应用的能效和EMC 要求。

L6364W 属于意法半导体IO-Link芯片产品家族。该产品家族提供一整套可简化IO-Link 物理层实现的芯片解决方案,其中还包括 L6360 IO-Link 控制端收发器以及 L6362A 和 L6364Q IO-Link 设备芯片。

STEVAL-IOD04KT1套件包含快速连接传感器以进行评估和使用所需的全部工具,包括 IO-Link M8-M12 适配器电缆和意法半导体的 STLINK-V3MINI代码烧录器。套件现已上市。

来源:意法半导体
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 18

近日,AGM Micro发布了兼容STM32 的MCU产品系列,推出具有低延迟高灵活性的功能模块MCU产品系列。AGM 的FPGA兼容产品系列已常年持续地服务于FPGA长尾商业模式的近千个AGM客户。AGM32产品系列对32位MCU的广大客户群提供国产替代和新智能应用市场的开拓。

“AGM

此次AGM Micro发布的产品系列包括AG103/107/205/303/407,与现有STM32产品功能和管脚完全兼容。

AGM的32位MCU采用了自主研发的高性能单(多)核,以及高性价比嵌入式CPU技术,使其MCU系列产品相对于国产同类产品来说具有高性能、低延迟、高灵活度(整合功能)的技术优势。

AGM32 103系列的最高主频为168MHz; 407系列的最高主频为248MHz,配备了1M的片上Flash和零等待指令执行功能。所有 AGM32 系列产品都提供从flash零等待指令执行功能。

芯片内置了丰富的外设资源,包含3个12位ADC、2个DAC,2个基本定时器,5个高级定时器,以及一系列接口:2个CAN2.0、5个UART、2个I2C,支持SDIO、Ethernet MAC、USB、FS+OTG等。

关于AGM Micro:

AGM Micro(www.agm-micro.com) 是领先的32位MCU、可编程SoC、和异构(MCU)边缘计算芯片和方案提供商,是一家民营企业,致力于为消费电子、工业和AIoT中高量市场提供智能化的设计软件和芯片系统。AGM针对不同的纵向应用市场,并拥有19个知识产权,以及获得专利的编译软件(包括数据库、综合、布局、布线、时序分析、比特流产生等)及电路。

围观 62

作者 | strongerHuang

微信公众号 | 嵌入式专栏

STM32CubeMX中Cortex系统定时器(System Timer)选择1分频和8分频,为啥生成代码一样?

“STM32的SysTick时钟源来自哪里?"

因为STM32CubeMX会启动SysTick作为延时(HAL_Delay)函数的时基,而SysTick作为Cortex内核的一部分,就会用到Cortex系统定时器。

那么,问题就来了SysTick时钟源来自哪里?

1、数据手册和STM32CubeMX时钟树

数据手册时钟树:

“STM32的SysTick时钟源来自哪里?"

STM32CubeMX时钟树:

“STM32的SysTick时钟源来自哪里?"

你会发现:数据手册中的只有『/8』分频,而STM32CubeMX除了『/8』分频,还有『/1』分频。

2、SysTick时钟初始化代码

不管是使用标准外设库,还是HAL库,你初始化SysTick,都会调用内核中的SysTick_Config函数。

标准库常用初始化:

SysTick_Config(SystemCoreClock / 1000);

HAL库初始化同样也是调用底层的初始化函数:

uint32_t HAL_SYSTICK_Config(uint32_t TicksNumb)
{
   return SysTick_Config(TicksNumb);
}

初始化调用这段代码之后,SysTick将会实现1ms中断一次。

这段代码实现1ms中断一次相信大家都能理解,但是这里SysTick初始化和上面说的时钟『/8』有关系吗?

3、SysTick时钟源是来自哪里?

这个问题只要认真看参考手册都能找到答案。

RCC通过AHB时钟(HCLK)8分频后作为Cortex系统定时器(SysTick)的外部时钟。

通过对SysTick控制与状态寄存器的设置,可选择上述时钟或Cortex(HCLK)时钟作为SysTick时钟。(--来自参考手册)

也就是说SysTick时钟源可以来自两个地方:

  • AHB时钟8分频

  • HCLK(内核)时钟

通过SysTick控制与状态寄存器的设置进行选择时钟源。

具体就是通过CLKSOURCE(时钟源)这一Bit位来选择:

“STM32的SysTick时钟源来自哪里?"

再次看SysTick_Config函数源码:

__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
  if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
  {
    return (1UL);                                                   /* Reload value impossible */
  }

  SysTick->LOAD  = (uint32_t)(ticks - 1UL);                         /* set reload register */
  NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
  SysTick->VAL   = 0UL;                                             /* Load the SysTick Counter Value */
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
                   SysTick_CTRL_TICKINT_Msk   |
                   SysTick_CTRL_ENABLE_Msk;                         /* Enable SysTick IRQ and SysTick Timer */
  return (0UL);                                                     /* Function successful */
}
(在core_cm3.h,或者core_cm4.h等内核源码中)

你会发现,其实源码已经默认使用HCLK(内核)时钟。

而SysTick_Config函数属于内核(如core_cm3.h)已经写好源码,一般我们不去修改。

所以,到这里,你会明白:SysTick时钟源其实就是用的HCLK(内核)时钟。

4、最后

开篇的问题:STM32CubeMX中Cortex系统定时器(System Timer)选择1分频和8分频,为啥生成代码一样?

“STM32的SysTick时钟源来自哪里?"

难道,STM32CubeMX配置Cortex系统定时器时钟是有Bug吗?

来源:嵌入式专栏
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 120

如果你还不了解什么是STM32对其Flash的保护,那么今天就来给你讲解一下什么是STM32的Flash保护!

01、什么是Flash?

STM32的FLASH组织结构,可能因不同系列、型号略有不同。比如大家熟悉的STM32F1中小容量一页大小只有1K,而F1大容量一页有2K。

还比如有些系列以扇区为最小单元,有的扇区最小16K,有的128K不等。

通常Flash包含几大块,这里以F40x为例:

  • 主存储器:用来存放用户代码或数据。

  • 系统存储器:用来存放出厂程序,一般是启动程序代码。

  • OTP 区域:一小段一次性可编程区域,供用户存放特定的数据。

  • 选项字节:存放与芯片资源或属性相关的配置信息。

“讲讲STM32单片机Flash的读保护和写保护"

02、什么是STM32对内部Flash的保护?

所有的STM32芯片都会提供对Flash的保护,防止对Flash的非法访问,分为:写保护和读保护。

1、读保护就是大家通常说的“加密”,作用于整个Flash存储区域。如果一旦设置了Flash的读保护,那么单片机内置的Flash存储区就只能通过程序的正常执行才能读出,而不能通过下述方式读出:

(1) 使用调试器(JTAG或SWD);
(2)从内存RAM中启动并执行的程序;

2、写保护是以四页(1KB/页) Flash存储区为单位提供写保护,如果对Flash设置了写保护,那么就无法对Flash进行编程和擦除,而且同时产生操作错误标志。 当出现下图标志的时候,就要检查Flash是否被保护起来了。

“讲讲STM32单片机Flash的读保护和写保护"

03、读保护与写保护的相关效果

当设置读保护与写保护时,其效果如下图所示:

“讲讲STM32单片机Flash的读保护和写保护"

1)flash保护的相关函数

“讲讲STM32单片机Flash的读保护和写保护"

2)STM32如何设置读保护?

我们只需要在程序开头加入“设置读保护”的代码就可以,这样就可以在每次运行代码的时候都检查一下,如果没有开的话就打开,如果开了就跳过。下面是读保护的代码:

“讲讲STM32单片机Flash的读保护和写保护"

当我们在程序的开头执行了上面的代码之后,使用j-link就不能在读出程序了,这样就实现了读保护。

3)如何通过代码解除Flash保护

解除读保护可以下面代码来进行解除,我们为了方便解锁,可以设置一个按键。

“讲讲STM32单片机Flash的读保护和写保护"

来源:玩转单片机与嵌入式
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 3477

水表是常见的家用设备。模拟式水表(如下图中所示)数量庞大,需要技术人员每月现场抄表并手工记录数据,以计算当月的用水量。整个过程枯燥且费力。当前,虽然联网仪表正在取代模拟式水表,但成本也会随之上涨;更何况,一些国家并不具备接入全球网络的能力,而有的家庭和公司也无计划支付更换仪表的费用。

“STM32低功耗计算机视觉应用:后装智能无线抄表模型"

本文,意法半导体为你演示如何使用低功耗、低成本的系统(由采用MCU嵌入式连接的低分辨率摄像机组成)高效地将模拟式仪表数字化。

该方法以STM32WL55为基础,通过摄像头捕捉水表读数区域,然后通过MCU上运行的人工智能算法识别水表读数。之后,AI分类器算法的运行结果(即仪表读数)通过STM32WL支持的远程sub-GHz无线网络(如LoRaWAN)进行传输。传统的联网设备会将图像传送到云端,而我们的解决方案传送的是读数。

该解决方案的优点是:通过本地AI模型,可以快速准确地识别读数,只需将结果发送回数据中心即可。此举不仅能有效保护用户数据隐私(仅传输推理结果),而且效率更高,还节省带宽。这样,我们就能以低成本、低功耗、高效率的方式解决问题。

“STM32低功耗计算机视觉应用:后装智能无线抄表模型"

STM32WL系列是世界上第一款支持远程无线通信的MCU。

作为市场上首款可以连接LoRa低功耗广域网的系统级芯片,STM32WL集成了STM32L4超低功耗微控制器和支持多种调制方案的Sub-GHz射频子系统。

“STM32低功耗计算机视觉应用:后装智能无线抄表模型"

STM32取得成功的一个重要因素是其强大的生态系统。基于STM32WL产品的开发人员可以借用已经被市场证明成熟可靠的STM32生态系统。该生态系统包含已经熟练掌握的开发工具(支持基于STM32进行通用开发)、专门用于sub-GHz无线电开发的软件包,以及AI设计工具,由此大大降低了开发难度,并缩短了产品上市时间。

生态系统中的资源包括STM32CubeMX项目配置和代码生成工具、STM32CubeMonitor运行时监测和可视化工具,以及STM32CubeProgrammer代码烧录工具。

STM32Cube.AI可以帮助用户快速将经过训练的AI模型部署到STM32并进行验证测试。

STM32CubeWL MCU软件包的组件包含STM32WL系列运行所需的所有嵌入式软件模块,包括外设驱动程序、意法半导体的LoRaWAN协议栈、Sigfox协议栈,以及利用意法半导体安全启动和安全固件更新技术实现LoRaWAN固件无线更新的示例代码。

此外,还有两种基于STM32WL的nucleo板件可用于快速原型制作:NUCLEO-WL55JC1(868pm 915amp 923 MHz)和NUCLEO-WL55JC2(433Accord470 MHz)。与此同时,有两种基于Nucleo的Nucleo板可用于快速原型开发。该项目基于NUCLEO-WL55JC2开发板。

“STM32低功耗计算机视觉应用:后装智能无线抄表模型"

除了NUCLEO-WL55JC2,该项目的另一个关键组成部分是摄像头。

摄像头模块(基于低成本的OV2640传感器)通过标准GPIO直接连接到NUCLEO-WL55JC2开发板,因为STM32WL系列产品不提供DCMI接口。为方便进行演示,我们使用了一款常见的电磁计数器,这样更容易进行模型演示。

实验系统如下图所示。我们将直流电源转化成方波,用于驱动电磁计数器,然后摄像头就可以采集计数器的读数画面,通过GPIO传输到NUCLEO-WL55JC2,由MCU上运行的AI模型进行识别。

“STM32低功耗计算机视觉应用:后装智能无线抄表模型"

所有硬件准备就绪后,我们就可以自己制作一个数据集,用于模型训练。

在计算机视觉应用中,有一个经典的入门级项目 - 识别MNIST数据集。MNIST数据集收集0-9这10个阿拉伯数字的手写字体,包括训练集中的60000个样本和测试集中的1000个样本。有的用户想要通过真实数据来尝试学习技巧和模式识别方法,同时尽可能地节省在预处理和格式化方面花费的精力,对于此类用户, MNIST是一个很好的练手项目。

“STM32低功耗计算机视觉应用:后装智能无线抄表模型"

但是我们不使用该数据集,因为水表上的数字的字体和颜色与该数据集差别很大。为了使模型获得更好的表现,我们将使用上面提到的设备制作一个类似于MNIST的数据集。

该数据集大约有4000个样本。每个样本包含5位数字。数据集的部分样本如下所示:

“STM32低功耗计算机视觉应用:后装智能无线抄表模型"

拥有数据集之后,我们可以构建一个神经网络,并用自己的数据集来训练模型。在该模型中,我们输入一幅40 X32(单个字符)的灰度图像来识别从0到19(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19)的20个类。下图显示了训练中的损失和准确率的变化。该数据集的特点是背景简单和字体规则,看起来训练效果非常好。实际上,我们可以收集不同的水表读数(不同的字体和颜色)进行训练,这样一个模型就可以识别多个不同水表读数。

“STM32低功耗计算机视觉应用:后装智能无线抄表模型"

训练结束后,我们将得到一个模型文件。此时,我们可以使用前面文章中提到的STM32cube.AI工具将模型转换为优化的代码,并快速将其部署到NUCLEO-WL55JC2板。

STM32Cube集成使STM32Cube.AI用户能够有效地在广泛的STM32微控制器系列产品之间移植模型,并且(在相似型号适用于不同产品的情况下)在STM32产品之间轻松迁移。该项目使用STM32cube.AI将模型部署到STM32WL。关于计算机视觉应用,我们在前面文章中介绍的FP-AI-VISION1软件包中有许多开放代码,可帮助开发人员理解和快速开发。用户可以浏览前面的文章:AI技术专题之六:STM32计算机视觉包FP-AI-VISION1简介,以了解详细信息。

该插件扩展了STM32CubeMX功能,可自动转换预训练人工智能模型和将生成的优化库集成到用户项目中,而不是人工构建代码,并支持将深度学习解决方案嵌入到广泛的STM32微控制器产品组合中,从而为每个产品添加新的智能化功能。

STM32Cube.AI原生支持各种深度学习框架,如Keras、TensorFlow™ Lite、ConvNetJs,并支持可导出为ONNX标准格式的所有框架,如PyTorch™、Microsoft®Cognitive Toolkit、MATLAB®等。

此外,STM32Cube.AI支持来自广泛ML开源库Scikit-Learn的标准机器学习算法,如孤立森林、支持向量机(SVM)、K-Means。

在该项目中,我们使用的是TensorFlow框架。

“STM32低功耗计算机视觉应用:后装智能无线抄表模型"

最后,让我们看看实际表现如何。为了便于演示,我们将摄像头捕捉到的图像和MCU上的识别结果传输到计算机屏幕上。视频中黑色背景上的数字(白色字体)是摄像头捕捉到的图像,第一行是AI模型的识别结果。我们将水表设置为每五秒钟驱动一次,这样数字每隔五秒就会更新一次。该视频显示的是未经编辑的录屏。在演示过程中,我们采用了遮挡的光线的方式,去模拟不同的光照条件,证明模型的鲁棒性很好。

“查看视频"

来源:意法半导体中国
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 69

页面

订阅 RSS - STM32