单片机

1、C语言和汇编语言在开发单片机时各有哪些优缺点?

汇编语言是一种用文字助记符来表示机器指令的符号语言,是最接近机器码的一种语言。其主要优点是占用资源少、程序执行效率高。但是不同的CPU,其汇编语言可能有所差异,所以不易移植。

C语言是一种结构化的高级语言。其优点是可读性好,移植容易,是普遍使用的一种计算机语言。缺点是占用资源较多,执行效率没有汇编高。

对于目前普遍使用的RISC架构的8bit MCU来说,其内部ROM、RAM、STACK等资源都有限,如果使用C语言编写,一条C语言指令编译后,会变成很多条机器码,很容易出现ROM空间不够、堆栈溢出等问题。而且一些单片机厂家也不一定能提供C编译器。而汇编语言,一条指令就对应一个机器码,每一步执行什幺动作都很清楚,并且程序大小和堆栈调用情况都容易控制,调试起来也比较方便。所以在资源较少的单片机开发中,还是建议采用汇编语言比较好。

02、C或汇编语言可以用于单片机,C++能吗?

在单片机开发中,主要是汇编和C,没有用C++的。

03、搞单片机开发,一定要会C吗?

汇编语言是一种用文字助记符来表示机器指令的符号语言,是最接近机器码的一种语言。其主要优点是占用资源少、程序执行效率高。但是不同的CPU,其汇编语言可能有所差异,所以不易移植。

而C语言是一种编译型程序设计语言,它兼顾了多种高级语言的特点,并具备汇编语言的功能。C语言有功能丰富的库函数、运算速度快、编译效率高、有良好的可移植性,而且可以直接实现对系统硬件的控制。C语言是一种结构化程序设计语言,它支持当前程序设计中广泛采用的由顶向下结构化程序设计技术。此外,C语言程序具有完善的模块程序结构,从而为软件开发中采用模块化程序设计方法提供了有力的保障。因此,使用C语言进行程序设计已成为软件开发的一个主流。用C语言来编写目标系统软件,会大大缩短开发周期,且明显地增加软件的可读性,便于改进和扩充,从而研制出规模更大、性能更完备的系统。

综上所述,用C语言进行单片机程序设计是单片机开发与应用的必然趋势。所以作为一个技术全面并涉足较大规模的软件系统开发的单片机开发人员最好能够掌握基本的C语言编程。

04、当开发一个较复杂而又开发时间短的项目时,用C还是用汇编开发好?

对于复杂而开发时间紧的项目时,可以采用C语言,但前提是要求对该MCU系统的C语言和C编译器非常熟悉,特别要注意该C编译系统所能支持的数据类型和算法。虽然C语言是最普遍的一种高级语言,但不同的MCU厂家其C语言编译系统是有所差别的,特别是在一些特殊功能模块的操作上。如果对这些特性不了解,那调试起来就有的烦了,到头来可能还不如用汇编来的快。

05、8088和196芯片单片机教材,请问哪里可以找到?

有关这方面的教材,大学里常用的一本是《IBM-PC汇编语言程序设计》清华大学出版社出版的,在网上以及书店都是可以找到的。另外网上还可以搜索到很多其他的教材如:《微机原理及汇编语言教程》(杨延双 张晓冬 等编著 )和《16/32 位微机原理、汇编语言及接口技术》(作者:钟晓捷 陈涛 ,机械工业出版社 出版)等,可以在较大型的科技书店里查找或者直接从网上订购。

06、初学者到底是应该先学C还是汇编?

对于单片机的初学者来说,应该从汇编学起。因为汇编语言是最接近机器码的一种语言,可以加深初学者对单片机各个功能模块的了解,从而打好扎实的基础。

07、大三生,学了电子线路、数字逻辑、汇编和接口、C语言,但总是感觉很迷茫,觉好像什么都不会,怎么办?

大学过程是一个理论过程,实践的机会比较少,往往会造成理论与实践相脱节,这是国内大学教育系统的通病,不过对于学生来说切不可好高骛远。一般从大三会开始接触到一些专业课程,电子相关专业会开设相关的单片机应用课程并且会有简单的实验项目,那么要充分把握实验课的机会,多多地实际上机操作练习。平时可以多看看相关的电子技术杂志网站,看看别人的开发经验,硬件设计方案以及他人的软件设计经验。有可能的话,还可以参加一些电子设计大赛,借此机会2--3个人合作做一个完整系统,会更有帮助。到了大四毕业设计阶段,也可以选择相关的课题作些实际案例增长经验。做什么事情都有个经验的积累过程,循序渐进。

08、作为学生,如何学好单片机?

学习好单片机,最主要的是实践,在实践中增长经验。在校学生的话,实践机会的确会比较少,但是有机会的话,可以毕业实习选择相关的课题,这样就可以接触到实际的项目。而且如果单片机微机原理是一门主课的话,相信学校会安排比较多的实践上机机会。有能力的话,可以找一些相关兼职工作做做,会更有帮助。而且单片机开发应用需要软硬件结合,所以不能只满足于编程技巧如何完美,平时也要注意硬件知识的积累,多上上电子论坛网站,买一些相关杂志。可能的话,可以到电子市场去买一些小零件,自己搭一个小系统让它工作起来。

09、如何才能才为单片机的高手?

要成为单片机高手,应该多实践,时常关注单片机的发展趋势;经常上一些相关网站,从那里可以找到许多有用的资料。

10、8位机还能延续多久?

以现在MCU产品主力还是在8位领域,主要应用于汽车应用、消费性电子、电脑及PC周边、电信与通讯、办公室自动化、工业控制等六大市场,其中车用市场多在欧、美地区,而亚太地区则以消费性电子为主, 并以量大低单价为产品主流,目前16位MCU与8位产品,还有相当幅度的价差,新的应用领域也仍在开发,至少在目前8位的MCU还有一席之地。

来源:单片机与嵌入式学堂

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

围观 14

本文介绍如何使用带FIFO的串口来减少接收中断次数,通过一种自定义通讯协议格式,给出帧打包方法;之后介绍一种特殊的串口数据发送方法,可在避免使用串口发送中断的情况下,提高系统的响应速度。

简介 

串口由于使用简单,价格低廉,配合RS485芯片可以实现长距离、抗干扰能力强的局域网络而被广泛使用。随着产品功能的增多,需要处理的任务也越来越复杂,系统任务也越来越需要及时响应。

绝大多数的现代单片机(ARM7、Cortex-M3)串口都带有一定数量的硬件FIFO,本文将介绍如何使用硬件FIFO来减少接收中断次数,提高发送效率。在此之前,先来列举一下传统串口数据收发的不足之处:

  • 每接收一个字节数据,产生一次接收中断。不能有效的利用串口硬件FIFO,减少中断次数。

  • 应答数据采用等待发送的方法。由于串行数据传输的时间远远跟不上CPU的处理时间,等待串口发送完当前字节再发送下一字节会造成CPU资源浪费,不利于系统整体响应(在1200bps下,发送一字节大约需要10ms,如果一次发送几十个字节数据,CPU会长时间处于等待状态)。

  • 应答数据采用中断发送。增加一个中断源,增加系统的中断次数,这会影响系统整体稳定性(从可靠性角度考虑,中断事件应越少越好)。

  • 针对上述的不足之处,将结合一个常用自定义通讯协议,提供一个完整的解决方案。

串口FIFO

串口FIFO可以理解为串口专用的缓存,该缓存采用先进先出方式。数据接收FIFO和数据发送FIFO通常是独立的两个硬件。

串口接收的数据,先放入接收FIFO中,当FIFO中的数据达到触发值(通常触发值为1、2、4、8、14字节)或者FIFO中的数据虽然没有达到设定值但是一段时间(通常为3.5个字符传输时间)没有再接收到数据,则通知CPU产生接收中断;发送的数据要先写入发送FIFO,只要发送FIFO未空,硬件会自动发送FIFO中的数据。

写入发送FIFO的字节个数受FIFO最大深度影响,通常一次写入最多允许16字节。上述列举的数据跟具体的硬件有关,CPU类型不同,特性也不尽相同,使用前应参考相应的数据手册。

数据接收与打包

FIFO可以缓存串口接收到的数据,因此我们可以利用FIFO来减少中断次数。以NXP的lpc1778芯片为例,接收FIFO的触发级别可以设置为1、2、4、8、14字节,推荐使用8字节或者14字节,这也是PC串口接收FIFO的默认值。

这样,当接收到大量数据时,每8个字节或者14个字节才会产生一次中断(最后一次接收除外),相比接收一个字节即产生一个中断,这种方法串口接收中断次数大大减少。

将接收FIFO设置为8或者14字节也十分简单,还是以lpc1778为例,只需要设置UART FIFO控制寄存器UnFCR即可。

接收的数据要符合通讯协议规定,数据与协议是密不可分的。通常我们需要将接收到的数据根据协议打包成一帧,然后交由上层处理。下面介绍一个自定义的协议帧格式,并给出一个通用打包成帧的方法。

自定义协议格式如下图所示。

1.png

  • 帧首:通常是3~5个0xFF或者0xEE

  • 地址号:要进行通讯的设备的地址编号,1字节

  • 命令号:对应不同的功能,1字节

  • 长度:数据区域的字节个数,1字节

  • 数据:与具体的命令号有关,数据区长度可以为0,整个帧的长度不应超过256字节

  • 校验:异或和校验(1字节)或者CRC16校验(2字节),本例使用CRC16校验

下面介绍如何将接收到的数据按照上图所示的格式打包成一帧。

1 定义数据结构

typedef struct
{
    uint8_t * dst_buf;                  //指向接收缓存  
    uint8_t sfd;                        //帧首标志,为0xFF或者0xEE  
    uint8_t sfd_flag;                   //找到帧首,一般是3~5个FF或EE  
    uint8_t sfd_count;                  //帧首的个数,一般3~5个  
    uint8_t received_len;               //已经接收的字节数  
    uint8_t find_fram_flag;             //找到完整帧后,置1  
    uint8_t frame_len;                  //本帧数据总长度,这个区域是可选的  
}find_frame_struct;

2 初始化数据结构,一般放在串口初始化中

/** 
* @brief    初始化寻找帧的数据结构 
* @param    p_fine_frame:指向打包帧数据结构体变量 
* @param    dst_buf:指向帧缓冲区 
* @param    sfd:帧首标志,一般为0xFF或者0xEE 
*/
void init_find_frame_struct(find_frame_struct * p_find_frame,uint8_t *dst_buf,uint8_t sfd)  
{      
    p_find_frame->dst_buf=dst_buf;      
    p_find_frame->sfd=sfd;      
    p_find_frame->find_fram_flag=0;      
    p_find_frame->frame_len=10;           
    p_find_frame->received_len=0;      
    p_find_frame->sfd_count=0;      
    p_find_frame->sfd_flag=0;  
}

3 数据打包程序

/** 
* @brief    寻找一帧数据  返回处理的数据个数 
* @param    p_find_frame:指向打包帧数据结构体变量 
* @param    src_buf:指向串口接收的原始数据 
* @param    data_len:src_buf本次串口接收到的原始数据个数 
* @param    sum_len:帧缓存的最大长度 
* @return   本次处理的数据个数 
*/

uint32_t find_one_frame(find_frame_struct * p_find_frame,const uint8_t * src_buf,uint32_t data_len,uint32_t sum_len)  
{       
  uint32_t src_len=0;  
  while(data_len--)      
  {  
      if(p_find_frame ->sfd_flag==0)                                
      {   
          //没有找到起始帧首  
          if(src_buf[src_len++]==p_find_frame ->sfd)              
          {                  
              p_find_frame ->dst_buf[p_find_frame ->received_len++]=p_find_frame ->sfd;  
              if(++p_find_frame ->sfd_count==5)                          
              {                      
                  p_find_frame ->sfd_flag=1;                      
                  p_find_frame ->sfd_count=0;                      
                  p_find_frame ->frame_len=10;                  
              }              
          }  
          else            
          {                  
              p_find_frame ->sfd_count=0;                   
              p_find_frame ->received_len=0;               
          }          
      }  
      else        
      {   
          //是否是"长度"字节? Y->获取这帧的数据长度  
          if(7==p_find_frame ->received_len)                            
          {                  
              p_find_frame->frame_len=src_buf[src_len]+5+1+1+1+2; //帧首+地址号+命令号+数据长度+校验       
              if(p_find_frame->frame_len>=sum_len)                  
              {   
                  //这里处理方法根据具体应用不一定相同                      
                  MY_DEBUGF(SLAVE_DEBUG,("数据长度超出缓存!\n"));                      
                  p_find_frame->frame_len= sum_len;                       
              }              
          }             
        p_find_frame ->dst_buf[p_find_frame->received_len++]=src_buf[src_len++];                
        if(p_find_frame ->received_len==p_find_frame ->frame_len)                              
        {                  
            p_find_frame ->received_len=0;              //一帧完成                    
            p_find_frame ->sfd_flag=0;                  
            p_find_frame ->find_fram_flag=1;                   
            return src_len;              
        }          
        }      
    }      
    p_find_frame ->find_fram_flag=0;  return src_len;  
}

使用例子:

定义数据结构体变量:

find_frame_struct slave_find_frame_srt;

定义接收数据缓冲区:

#define SLAVE_REC_DATA_LEN  128
uint8_t slave_rec_buf[SLAVE_REC_DATA_LEN];

在串口初始化中调用结构体变量初始化函数:

init_find_frame_struct(&slave_find_frame_srt,slave_rec_buf,0xEE);

在串口接收中断中调用数据打包函数:

find_one_frame(&slave_find_frame_srt,tmp_rec_buf,data_len,SLAVE_REC_DATA_LEN);

其中,rec_buf是串口接收临时缓冲区,data_len是本次接收的数据长度。
数据发送

前文提到,传统的等待发送方式会浪费CPU资源,而中断发送方式虽然不会造成CPU资源浪费,但又增加了一个中断源。在我们的使用中发现,定时器中断是几乎每个应用都会使用的,我们可以利用定时器中断以及硬件FIFO来进行数据发送,通过合理设计后,这样的发送方法即不会造成CPU资源浪费,也不会多增加中断源和中断事件。

需要提前说明的是,这个方法并不是对所有应用都合适,对于那些没有开定时器中断的应用本方法当然是不支持的,另外如果定时器中断间隔较长而通讯波特率又特别高的话,本方法也不太适用。

公司目前使用的通讯波特率一般比较小(1200bps、2400bps),在这些波特率下,定时器间隔为10ms以下(含10ms)就能满足。如果定时器间隔为1ms以下(含1ms),是可以使用115200bps的。本方法主要思想是:定时器中断触发后,判断是否有数据要发送,如果有数据要发送并且满足发送条件,则将数据放入发送FIFO中,对于lpc1778来说,一次最多可以放16字节数据。之后硬件会自动启动发送,无需CPU参与。

下面介绍如何使用定时器发送数据,硬件载体为RS485。因为发送需要操作串口寄存器以及RS485方向控制引脚,需跟硬件密切相关,以下代码使用的硬件为lpc1778,但思想是通用的。

1 定义数据结构

/*串口帧发送结构体*/
typedef struct
{
    uint16_t send_sum_len;          //要发送的帧数据长度  
    uint8_t  send_cur_len;          //当前已经发送的数据长度  
    uint8_t  send_flag;             //是否发送标志  
    uint8_t * send_data;            //指向要发送的数据缓冲区  
}uart_send_struct;

2 定时处理函数

/** 
* @brief    定时发送函数,在定时器中断中调用,不使用发送中断的情况下减少发送等待 
* @param    UARTx:指向硬件串口寄存器基地址 
* @param    p:指向串口帧发送结构体变量 
*/
#define FARME_SEND_FALG 0x5A          
#define SEND_DATA_NUM   12  
static void uart_send_com(LPC_UART_TypeDef *UARTx,uart_send_struct *p)  
{      
    uint32_t i;      
    uint32_t tmp32;  

    if(UARTx->LSR &(0x01<<6))                      //发送为空      
    {         
        if(p->send_flag==FARME_SEND_FALG)          
        {                                      
            RS485ClrDE;                             // 置485为发送状态  
            tmp32=p->send_sum_len-p->send_cur_len;  
            if(tmp32>SEND_DATA_NUM)                 //向发送FIFO填充字节数据              
            {  
                for(i=0;i<SEND_DATA_NUM;i++)                  
                {                      
                    UARTx->THR=p->send_data[p->send_cur_len++];                  
                }              
            }  
            else            
            {  
                for(i=0;i<tmp32;i++)                  
                {                      
                    UARTx->THR=p->send_data[p->send_cur_len++];                  
                }                  
                p->send_flag=0;                                  
            }          
        }  
        else        
        {              
            RS485SetDE;          
        }      
    }  
}

其中,RS485ClrDE为宏定义,设置RS485为发送模式;

RS485SetDE也为宏定义,设置RS485为接收模式。

使用例子:

定义数据结构体变量:

uart_send_struct uart0_send_str;

定义发送缓冲区:

uint8_t uart0_send_buf[UART0_SEND_LEN];

根据使用的硬件串口,对定时处理函数做二次封装:

2.png

将封装函数uart0_send_data();放入定时器中断处理函数中;

在需要发送数据的地方,设置串口帧发送结构体变量:

uart0_send_str.send_sum_len=data_len;      //data_len为要发送的数据长度
uart0_send_str.send_cur_len=0;             //固定为0
uart0_send_str.send_data=uart0_send_buf;   //绑定发送缓冲区
uart0_send_str.send_flag=FARME_SEND_FALG;  //设置发送标志

总结

本文主要讨论了一种高效的串口数据收发方法,并给出了具体的代码实现。在当前处理器任务不断增加的情况下,提供了一个占用资源少,可提高系统整体性能的新的思路。

来源:STM32嵌入式开发

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

围观 24

为了提供单片机内存数据传输效率,众多单片机都支持 DMA(Direct Memory Access)传输。

但是,还有另外一种可以提高内存传输效率的功能较TCM。今天就结合瑞萨RA8单片机给大家讲讲TCM的优点及应用。

关于TCM

TCM:Tightly-Coupled Memory,紧耦合内存,是通过专用的接口直接连接到处理器的存储器区域,它提供单周期访问,避免其他存储器可能存在的仲裁延时和延迟。
RA8单片机 Cortex-M85内核有2种TCM类型:Instruction TCM (ITCM) 指令TCM和Data TCM (DTCM) 数据TCM。

RA8x1有64KB ITCM和64KB DTCM。

ECC保护(代码生成和纠正逻辑)。

RA8x1支持ITCM和DTCM ECC功能,可以通过OFS2.INITECCEN设置来启用和禁用。

1.png

RA8x1 CPU方框图

TCM具有以下优点:

低延迟访问:TCM与处理器核心之间的直接连接消除了访问延迟,使得数据和指令能够更快速地被处理器访问,从而提高了系统的响应速度。

高带宽:TCM通常具有更高的带宽,可以支持处理器对数据和指令的高速读写,提升了系统的整体性能。

节省功耗:由于TCM与处理器核心直接连接,不需要通过总线进行数据传输,因此可以降低功耗。此外,由于访问延迟较低,处理器可以更快地完成任务并进入休眠模式,进一步降低功耗。

提高实时性能:对于需要实时响应的应用程序,TCM的低延迟和高带宽特性使得处理器能够更快地访问关键数据和指令,从而提高了系统的实时性能。

增强安全性:TCM可以用于存储敏感数据和关键代码,通过与处理器核心的紧密耦合,可以降低数据泄露和恶意攻击的风险,提高系统的安全性。

减少对外部存储器的依赖:TCM可以用于存储频繁访问的数据和指令,减少了对外部存储器的访问次数,降低了总体的访问延迟和功耗。这对于一些资源有限的嵌入式系统尤为重要。

增强可靠性:TCM的直接连接和高速访问特性可以提高系统的可靠性,减少因外部存储器或总线故障而导致的系统性能下降或故障。

因此,TCM适用于许多不同的应用和场景,特别是对于需要高性能、低延迟和实时响应的应用,其优势更加突出。以下是一些适合使用TCM的应用和场景:

实时控制系统:对于需要快速响应的实时控制系统,如工业机器人、航空航天控制系统、医疗设备等,TCM可以提供所需的低延迟和高带宽,确保系统能够及时、准确地响应各种控制指令。

信号处理应用:TCM对于需要大量数据处理和信号处理的应用非常适用,例如无线通信系统、雷达系统、图像处理系统等。通过将频繁访问的数据和指令存储在TCM中,可以提高系统的处理速度和效率。

物联网设备:随着物联网设备的普及,对于需要在资源有限的设备上实现高性能和实时响应的应用,TCM可以帮助提高系统的性能和能效,同时减少对外部存储器和网络带宽的依赖。

高性能计算:在需要进行复杂计算和大规模数据处理的高性能计算应用中,TCM可以提供更快速和可靠的数据访问,从而提高系统的计算性能和吞吐量。

MCU如何将通过FSP将代码/数据放置到TCM中?

瑞萨电子灵活配置软件包(FSP)是用于嵌入式系统设计的高质量增强型软件包,支持瑞萨电子RA产品家族ARM微控制器,提供用户友好的界面且可灵活扩展,确保从入门级到高性能的整个RA微控制器的软件兼容性。FSP包括高性能、低内存占用的业界一流的HAL驱动程序。还包含集成了Azure RTOS和FreeRTOS的中间件协议栈,能够简化通信和安全等复杂模块的实现。e2 studio IDE提供了对图形化配置工具和智能代码生成器的支持,从而使编程和调试变得更加轻松快捷。

  • 瑞萨FSP链接脚本提供TCM内存区域段定义

memory_region.ld中的内存大小定义:

2.png

内存区域定义:

3.png

如下原型定义可用于将用户代码/数据放置到TCM中。在启动过程中,.itcm_data和.dtcm_data区域将通过闪存中存储的初始化代码进行数据初始化。.dtcm_bss区域已初始化为零。

4.png

FSP中的ITCM段定义:

5.png

FSP中的DTCM段定义:

6.png

TCM例子分析

下图是一个RA8x1 MCU实际使用TCM的例子,它使用了ITCM和DTCM。图片中的右边为RA8x1 MCU的系统地址空间。

7.png

具体分析过程为:

1)紫色的代码“uint16_t i;”全局变量,它运行的时候,分配的地址是在从0x2200_0000开始的On-chip SRAM中。

2)红色的代码“BSP_PLACE_IN_SECTION(“.dtcm_data”)uint8x16_t rega_8, regb_8, regc_8, regd_8;”,全局变量,但是由于这些变量前面添加了BSP_PLACE_IN_SECTION(“.dtcm_data”),这表示将这些变量放置到DTCM中,它运行的时候,分配的地址是在从0x2000_0000开始的DTCM中。

3)青色的代码“void hal_entry(void)”,它是函数,运行的时候,分配的地址是在从0x0200_0000开始的On-chip flash中。

4)深绿色的代码“void helium_test(void)”,它是函数,但是由于这些变量前面添加了BSP_PLACE_IN_SECTION(“.itcm_data”),这意味着,该代码运行的时候,分配的地址是从0x0000_0000开始的ITCM中。

下图是上面描述的代码在e2 studio中使用了LLVM工具链编译并仿真运行的截图,可以发现右边表达式窗口中的i,rega_8, regb_8, regc_8, regd_8,helium_test,hal_entry这些代码或者变量的地址和刚刚分析的结果是相符的。

8.png

来源:瑞萨嵌入式小百科

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

围观 25

Holtek持续扩展中高阶市场产品应用,推出全新HT32F49153/HT32F49163系列32-bit单片机,采用高效能Arm® Cortex®-M4核心,提供单精度浮点运算单元(FPU),支持所有Arm®单精度数据处理指令和数据类型。该核心亦具备完整DSP指令与内存保护单元(MPU),增强数值运算效能与应用安全性,并提供多种节能模式,适用于各种应用场景,如嵌入式系统、工业控制、物联网、智慧家庭等。

1.jpg

HT32F49153/HT32F49163系列最高运行速度可达150MHz,工作电压范围为2.4V~3.6V,符合-40℃~105℃工业温度范围。Flash容量为128KB/256KB,并支持外部内存扩展(XMC),兼容8080/6800模式作为TFT-LCD接口。提供耐受5V电压输入的GPIO及丰富的外围资源,如转速高达5.33Msps 12-bit ADC×1,12-bit DAC×2,16-bit通用定时器×8,32-bit通用定时器×1,高级定时器×1,基本定时器×2,I²C×3,SPI/I²S×3,USART/UART×8,IRTMR×1,OTGFS×1 (Device模式下支持无晶振Xtal-less)和CAN×2。

HT32F49153/HT32F49163提供32-pin QFN、48/64/100-pin LQFP封装,依据封装类型GPIO脚位可达87个。全系列支持业界主流Keil MDK及IAR EWARM开发环境,并提供硬件开发工具包、周边驱动函式库(Firmware Library)及应用范例等,提供开发便利性,缩短产品开发周期。

来源:HOLTEK

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

围观 14

单片机可以替代PLC吗?

单片机不能完全替代PLC 。虽然单片机可以通过编程实现类似的功能,但是在可靠性和稳定性方面相对来说稍逊于PLC。PLC具有良好的可编程性、实时性和稳定性等特点,在工业控制和自动化领域受到广泛应用。

PLC有哪些优点是单片机无法替代的?

PLC相对于单片机具有以下一些优点,这些优点使得PLC在某些应用中无法被单片机替代:
  • 可靠性高 :PLC采用工业级元器件,具有可靠、稳定的性能。其内部机制设计复杂、精密,运行稳定,因此很少出现类似死机或蓝屏等情况,使用寿命长。

  • 抗干扰能力强 :PLC通常有更强的抗干扰能力和更高的稳定性,可以工作在更恶劣的环境下,如高温、高湿、强电磁干扰等。

  • 模块化、分布式控制 :PLC可以通过模块化、分布式控制的方式来构建复杂的系统,支持多任务处理和多设备协同控制。

  • 编程简单 :PLC编程相对简单,常常使用图形化编程语言,如梯形图(Ladder Diagram)、顺序功能图(SFC)、结构化文本(ST)等。这使得开发周期缩短,也降低了开发难度。

  • 输入输出接口灵活 :PLC的输入输出接口通常数量较多,可方便扩展使用,另外PLC也具有很好的多通道控制功能,对于输入输出操作可以直接进行操作。

  • 扩展性强 :PLC可以通过模块扩展来增加功能,而不需要对PLC本身进行修改。

  • 安全性高 :PLC通常具有较高的安全性,可以通过安全模块和安全协议来确保系统的安全性。

PLC在可靠性、抗干扰能力、模块化、编程简单、输入输出接口灵活、扩展性和安全性等方面都具有优势,这些优势使得PLC在一些特定应用中无法被单片机替代。

单片机和PLC的区别是什么?

单片机和PLC的区别主要体现在以下几个方面:
  • 应用领域:单片机通常用于小型应用,如小型电器设备中控制电机、传感器等。而PLC则主要用于大型应用,如自动化和机器控制。
  • 功能实现方式:单片机可以通过编程实现其功能,通常用于不需要高级程序和算法的简单应用。而PLC则可以通过编程和配置来实现更为复杂的控制任务。
  • 结构和接口:单片机一般只有少量的输入输出接口,而PLC可以具有多种输入输出接口,以适应不同的应用要求。
  • 可靠性和稳定性:PLC具有良好的可编程性、实时性和稳定性等特点,因此在工业控制和自动化领域受到广泛应用。而单片机的可靠性和稳定性相对来说稍逊于PLC。
  • 成本:对于量大的配套项目,采用单片机系统具有成本低、效益高的优点,但这需要有相当的研发力量和行业经验才能使系统稳定。而PLC的成本相对较高,但在一些需要高可靠性和稳定性的应用中,使用PLC可能更为合适。
  • 编程和调试:PLC广泛使用梯形图代替计算机语言,对编程有一定的优势,使得使用者更加容易使用。而单片机的编程和调试则需要较高的技术水平。

总的来说,单片机和PLC在应用、功能实现方式、结构、接口、可靠性、稳定性、成本和编程调试等方面都有所不同。在实际应用中,需要根据具体的需求和条件来选择合适的控制系统。

来源:电工与电气控制技术知识

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

围观 12

大家好,今天分享一篇关于按键处理的文章,项目地址如下https://gitee.com/wei513723/key_board 

key_board介绍

key_board用于单片机中的小巧多功能按键支持,软件采用了分层的思想,并且做到了与平台无关,用户只需要提供按键的基本信息和读写io电平的函数即可,非常方便移植,同时支持多个矩阵键盘及多个单io控制键盘。

目前已实现按下触发、弹起触发、长按自动触发、长按弹起触发、多击触发、连续触发等功能,并且能够随意组合(支持状态的同一时间轴和非同一时间轴),后续还会添加更多的功能。

使用说明

  1. 初始化相关的硬件资源。

  2. 提供一个1ms的定时器,用于周期性的调用'key_check'函数。

  3. 提供按键的描述及读写io的函数。

  4. 将键盘注册到系统。

  5. 具体的操作参考提供的stm32例程。

  6. 因为程序默认使用了堆内存,当发现程序运行结果不正常时,尝试增大你的程序堆空间,或者注册调试接口查看原因。

  7. 更详细的使用教程见详细使用说明或者提供的stm32例程。

已支持的键盘

1、矩阵键盘

1.jpg

矩阵键盘

2、单io按键

2.jpg

单io按键

详细使用说明

将key_board.c,key_board.h,key_board_config.h放进key_board文件夹中并包含进你的工程,添加头文件路径。

基础功能移植(以stm32矩阵键盘为例)

首先需要一个可使用的定时器(如果不想使用定时器也可直接放到主循环中,但不推荐,会导致时基不准确),固定为1ms触发一次;

准备待检测的按键的基本信息,可参考key_board_sample.c文件中的struct key_pin_t结构体,如:

struct key_pin_t {
    GPIO_TypeDef *port;     //按键端口号
    uint16_t pin;           //按键的引脚号
    GPIO_PinState valid;    //按键的有效电平(即按键按下时的电平)
    GPIO_PinState invalid;  //按键的无效电平(即按键空闲时的电平)
    /*
    可添加你的其它参数
    */
};

定义待检测的按键信息,可参考key_board_sample.c文件中的const struct key_pin_t key_pin_sig[]结构体数组,对应头文件为key_board_sample.h,如:

//全局变量
const struct key_pin_t key_pin_sig[] = {
    {
        .port = KEY_PORT_J12,
        .pin = KEY_PIN_J12,
        .valid = KEY_PRESS_LEVEL_J12,
        .invalid = KEY_RELEASE_LEVEL_J12
    },
    {
        .port = KEY_PORT_J34,
        .pin = KEY_PIN_J34,
        .valid = KEY_PRESS_LEVEL_J34,
        .invalid = KEY_RELEASE_LEVEL_J34
    },
    {
        .port = KEY_PORT_J56,
        .pin = KEY_PIN_J56,
        .valid = KEY_PRESS_LEVEL_J56,
        .invalid = KEY_RELEASE_LEVEL_J56
    },
};

如果为矩阵键盘还需要定义控制io的相关信息,可参考key_board_sample.c文件中的const struct key_pin_t key_pin_ctrl[]结构体数组,对应头文件为key_board_sample.h,如:

const struct key_pin_t key_pin_ctrl[] = {
    {
        .port = KEY_PORT_J135,
        .pin = KEY_PIN_J135,
        .valid = KEY_CTL_LINE_ENABLE,
        .invalid = KEY_CTL_LINE_DISABLE
    },
    {
        .port = KEY_PORT_J246,
        .pin = KEY_PIN_J246,
        .valid = KEY_CTL_LINE_ENABLE,
        .invalid = KEY_CTL_LINE_DISABLE
    },
};

实现按键io的电平读取函数,可参考key_board_sample.c文件中的pin_level_get函数,如:

static inline bool pin_level_get(const void *desc)
{
    struct key_pin_t *pdesc;

    pdesc = (struct key_pin_t *)desc;
    return HAL_GPIO_ReadPin(pdesc->port, pdesc->pin) == pdesc->valid;
}

如果为矩阵键盘还需要实现按键io的电平写入函数,可参考key_board_sample.c文件中的pin_level_set函数,如:

static inline void pin_level_set(const void *desc, bool flag)
{
    struct key_pin_t *pdesc;

    pdesc = (struct key_pin_t *)desc;
    HAL_GPIO_WritePin(pdesc->port, pdesc->pin, flag ? pdesc->valid : pdesc->invalid);
}

定义按键的id及功能结构体struct key_public_sig_t,可参考key_board_sample.c文件中的const struct key_public_sig_t key_public_sig[]结构体数组,对应头文件key_board.h,如:

const struct key_public_sig_t key_public_sig[] = {
    KEY_PUBLIC_SIG_DEF(KEY_UP, &key_pin_sig[0], pin_level_get, KEY_FLAG_NONE),
    KEY_PUBLIC_SIG_DEF(KEY_LEFT, &key_pin_sig[1], pin_level_get, KEY_FLAG_NONE),
    KEY_PUBLIC_SIG_DEF(KEY_DOWN, &key_pin_sig[2], pin_level_get, KEY_FLAG_NONE),
    //下面的是因为使用的矩阵键盘而扩展出来的三个按键
    KEY_PUBLIC_SIG_DEF(KEY_ENTER, &key_pin_sig[0], pin_level_get, KEY_FLAG_NONE),
    KEY_PUBLIC_SIG_DEF(KEY_RIGHT, &key_pin_sig[1], pin_level_get, KEY_FLAG_NONE),
    KEY_PUBLIC_SIG_DEF(KEY_EXIT, &key_pin_sig[2], pin_level_get, KEY_FLAG_NONE),
};

如果为矩阵键盘还需要定义控制io的id及功能结构体struct key_public_ctrl_t,可参考key_board_sample.c文件中的const struct key_public_ctrl_t key_public_ctrl[]结构体数组,对应头文件key_board.h,如:

const struct key_public_ctrl_t key_public_ctrl[] = {
    KEY_PUBLIC_CTRL_DEF(&key_pin_ctrl[0], pin_level_set),
    KEY_PUBLIC_CTRL_DEF(&key_pin_ctrl[1], pin_level_set),
};

初始化键盘,可参考key_board_sample.c文件中的GPIO_Key_Board_Init函数,如:

void GPIO_Key_Board_Init(void)
{
    //硬件io的初始化
    GPIO_InitTypeDef GPIO_InitStruct;
    unsigned int i;

    RCC_KEY_BOARD_CLK_ENABLE();

    GPIO_InitStruct.Pull  = GPIO_PULLUP;
    GPIO_InitStruct.Mode  = GPIO_MODE_INPUT;
    for(i = 0;i < ARRAY_SIZE(key_pin_sig);i++)
    {
        GPIO_InitStruct.Pin   = key_pin_sig[i].pin;
        HAL_GPIO_Init(key_pin_sig[i].port, &GPIO_InitStruct);
    }

    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Pull  = GPIO_NOPULL;
    GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP;
    for(i = 0;i < ARRAY_SIZE(key_pin_ctrl);i++)
    {
        GPIO_InitStruct.Pin   = key_pin_ctrl[i].pin;
        HAL_GPIO_Init(key_pin_ctrl[i].port, &GPIO_InitStruct);
    }

    //初始化键盘
    key_board_init();
    //注册键盘到系统中(矩阵键盘)
    key_board_register(KEY_BOARD_MATRIX, key_public_sig, ARRAY_SIZE(key_public_sig), key_public_ctrl, ARRAY_SIZE(key_public_ctrl));
}

主流程伪代码框架,更多例子参考main_test.c文件:

int main(void)
{
    //初始化硬件io,并注册键盘
    GPIO_Key_Board_Init();
    //初始化定时器,用于按键扫描(1ms)
    init_tmr();

    for(;;)
    {
        if(key_check_state(KEY_UP, KEY_RELEASE))
        {
            PRINTF("KEY_UP KEY_RELEASE\r\n");
        }
        if(key_check_state(KEY_UP, KEY_PRESS))
        {
            PRINTF("KEY_UP KEY_PRESS\r\n");
        }
    }
}

//定时器到期回调处理函数
void tmr_irq_callback(void)
{
    //调用按键扫描核心函数
    key_check();
}

扩展功能长按的使用

首先确保key_board_config.h文件中宏KEY_LONG_SUPPORT已处于使能状态,并且正确设置了宏KEY_DEFAULT_LONG_TRRIGER_TIME的值;

设置按键功能需要对长按进行检测,如:

KEY_PUBLIC_SIG_DEF(KEY_UP, &key_pin_sig[0], pin_level_get, KEY_FLAG_PRESS_LONG | KEY_FLAG_RELEASE_LONG)

使用例程:

if(key_check_state(KEY_UP, KEY_PRESS_LONG))
{
    PRINTF("KEY_UP KEY_PRESS_LONG\r\n");
}
if(key_check_state(KEY_UP, KEY_RELEASE_LONG))
{
    PRINTF("KEY_UP KEY_RELEASE_LONG\r\n");
}

扩展功能连按的使用

首先确保key_board_config.h文件中宏KEY_CONTINUOUS_SUPPORT已处于使能状态,并且正确设置了宏KEY_DEFAULT_CONTINUOUS_INIT_TRRIGER_TIME和KEY_DEFAULT_CONTINUOUS_PERIOD_TRRIGER_TIME的值;

设置按键功能需要对连按进行检测,如:

KEY_PUBLIC_SIG_DEF(KEY_UP, &key_pin_sig[0], pin_level_get, KEY_FLAG_PRESS_CONTINUOUS)

使用例程:

if(key_check_state(KEY_UP, KEY_PRESS_CONTINUOUS))
{
    PRINTF("KEY_UP KEY_PRESS_CONTINUOUS\r\n");
}

扩展功能多击的使用

首先确保key_board_config.h文件中宏KEY_MULTI_SUPPORT已处于使能状态,并且正确设置了宏KEY_DEFAULT_MULTI_INTERVAL_TIME的值;

设置按键功能需要多击进行检测,如:

KEY_PUBLIC_SIG_DEF(KEY_UP, &key_pin_sig[0], pin_level_get, KEY_FLAG_PRESS_MULTI | KEY_FLAG_RELEASE_MULTI)

使用例程:

unsigned int res;
res = key_check_state(KEY_UP, KEY_PRESS_MULTI);
if(res)
{
    PRINTF("KEY_UP KEY_PRESS_MULTI:%d\r\n", res);
}
res = key_check_state(KEY_UP, KEY_RELEASE_MULTI);
if(res)
{
    PRINTF("KEY_UP KEY_RELEASE_MULTI:%d\r\n", res);
}

扩展功能组合状态(同一时间轴)

使用例程:

unsigned int key_down_release_long, key_up_release_long;
key_down_release_long = key_check_state(KEY_DOWN, KEY_RELEASE_LONG);
key_up_release_long = key_check_state(KEY_UP, KEY_RELEASE_LONG);
if(key_down_release_long && key_up_release_long)
{
    PRINTF("KEY_DOWN KEY_RELEASE_LONG && KEY_UP KEY_RELEASE_LONG\n");
}

扩展功能组合状态(非同一时间轴)

首先确保key_board_config.h文件中宏KEY_COMBINE_SUPPORT已处于使能状态,并且正确设置了宏KEY_DEFAULT_COMBINE_INTERVAL_TIME的值;

使用例程:

//用于保存注册后的组合状态id
static unsigned int test_id1, test_id2;

//定义要检测的状态
const struct key_combine_t test_combine1[] = {
    { .id = KEY_UP,     .state = KEY_PRESS },
    { .id = KEY_DOWN,   .state = KEY_PRESS_LONG },
    { .id = KEY_UP,     .state = KEY_PRESS },
};
//注册组合状态
test_id1 = key_combine_register(test_combine1, ARRAY_SIZE(test_combine1));

const struct key_combine_t test_combine2[] = {
    { .id = KEY_UP,     .state = KEY_PRESS },
    { .id = KEY_DOWN,   .state = KEY_PRESS },
    { .id = KEY_UP,     .state = KEY_PRESS },
    { .id = KEY_DOWN,   .state = KEY_PRESS },
};
test_id2 = key_combine_register(test_combine2, ARRAY_SIZE(test_combine2));

if(key_check_combine_state(test_id1))
{
    PRINTF("combine test_id1\r\n");
}

if(key_check_combine_state(test_id2))
{
    PRINTF("combine test_id2\r\n");
}

原文:https://gitee.com/wei513723/key_board

直接转载来源:嵌入式电子

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

围观 19

单片机的应用非常广泛,有的场景可能不需要RTOS,但很多场景下都需要数据的交互。今天分享一个用于单片机裸机情况下,实现的队列功能的模块:QueueForMcu

QueueForMcu

基于单片机实现的队列功能模块,主要用于8位、16位、32位非运行RTOS的单片机应用,兼容大多数单片机平台。

开源地址:https://github.com/xiaoxinpro/QueueForMcu 

一、特性

  • 动态创建队列对象
  • 动态设置队列数据缓冲区
  • 静态指定队列元素数据长度
  • 采用值传递的方式保存队列数据

二、快速使用

#include "queue.h"

#define Q_UART_BUFFER_SIZE  1024

QUEUE_HandleTypeDef qUartTx;
QUEUE_DATA_T BufferUartTx[Q_UART_BUFFER_SIZE];

int main(void)
{  
    QUEUE_DATA_T temp;    
    
    //初始化队列  
    Queue_Init(&qUartTx, BufferUartTx, Q_UART_BUFFER_SIZE);    
    
    while(1)  
    {    
        //入队    
        Queue_Push(&qUartTx, 'Q');    
        Queue_Push(&qUartTx, 'u');    
        Queue_Push(&qUartTx, 'e');    
        Queue_Push(&qUartTx, 'u');    
        Queue_Push(&qUartTx, 'e');        
        
        //出队    
        Queue_Pop(&qUartTx, &temp);    
        Queue_Pop(&qUartTx, &temp);    
        Queue_Pop(&qUartTx, &temp);    
        Queue_Pop(&qUartTx, &temp);    
        Queue_Pop(&qUartTx, &temp);  
    }
}

三、配置说明

目前QueueForMcu只有一个静态配置项,具体如下:

在文件 queue.h 中有一个宏定义 QUEUE_DATA_T 用于指定队列元素的数据长度,默认是 unsigned char ,可以根据需要更改为其他数据类型。

四、数据结构

队列的数据结构为 QUEUE_HandleTypeDef 用于保存队列的状态,源码如下:

typedef struct QUEUE_HandleTypeDef
{    
    unsigned int head;                      //队列头指针    
    unsigned int tail;                      //队列尾指针    
    unsigned int buffer_length;             //队列缓存长度(初始化时赋值)    
    QUEUE_DATA_T * buffer;                  //队列缓存数组(初始化时赋值)
}QUEUE_HandleTypeDef;

其中 QUEUE_DATA_T 为配置项中自定义的数据类型。

五、创建队列

1、创建队列缓存

由于我们采用值传递的方式保存队列数据,因此我们在创建队列前要手动创建一个队列缓存区,用于存放队列数据。

QUEUE_DATA_T BufferUartTx[1024];

以上代码即创建一个大小为 1024 的队列缓存区。

2、创建队列结构

接下来使用 QUEUE_HandleTypeDef 创建队列结构,用于保存队列的状态:

QUEUE_HandleTypeDef qUartTx;

3、初始化队列

准备好队列缓存和队列结构后调用 Queue_Init 函数来创建队列,该函数原型如下:

void Queue_Init(QUEUE_HandleTypeDef * hqueue, QUEUE_DATA_T * buffer, unsigned int len)

参数说明:

参数名描述
hqueue需要初始化的队列结构,如果二次初始化将清空原队列的内容。
buffer队列缓存的首地址指针
len队列长度,不能比队列缓存长度还要大。

参考代码:

Queue_Init(&qUartTx, BufferUartTx, Q_UART_BUFFER_SIZE);

六、压入队列

1、单数据压入

将数据压入队列尾部使用 Queue_Push 函数,该函数原型如下:

QUEUE_StatusTypeDef Queue_Push(QUEUE_HandleTypeDef * hqueue, QUEUE_DATA_T data)

参数说明:

参数名描述
hqueue需要压入数据的队列结构。
data待压入队列的数据。

返回值说明:

该函数会返回一个 QUEUE_StatusTypeDef 枚举数据类型,返回值会根据队列状态返回以下几个值:

返回值描述
QUEUE_OK数据压入队列成功。
QUEUE_OVERLOAD未压入数据到队列中,原因队列已满。

参考代码:

Queue_Push(&qUartTx, 'Q');
Queue_Push(&qUartTx, 0x51);
Queue_Push(&qUartTx, 81);

2、多数据压入

若需要将多个数据(数组)压入队列可以使用 Queue_Push_Array 函数,原理上循环调用 Queue_Push 函数来实现的,函数原型如下:

unsigned int Queue_Push_Array(QUEUE_HandleTypeDef * hqueue, QUEUE_DATA_T * pdatas, unsigned int len)

参数说明:

参数名描述
hqueue需要压入数据的队列结构。
pdatas待压入队列的数组首地址。
len待压入队列的数组长度。

当数组长度大于队列剩余长度时,数组多余的数据将被忽略。

返回值说明:

  • 该函数将返回实际被压入到队列中的数据长度。

  • 当队列中的剩余长度富余时,返回值将等于参数 len 的值。

  • 当队列中的剩余长度不足时,返回值为实际被压入到队列的数据长度。

七、弹出队列

1、单数据弹出

将队列头部数据弹出队列使用 Queue_Pop 函数,需要注意的是,弹出的数据将从队列中删除,该函数原型如下:

QUEUE_StatusTypeDef Queue_Pop(QUEUE_HandleTypeDef * hqueue, QUEUE_DATA_T * pdata)

参数说明:

参数名描述
hqueue需要弹出数据的队列结构。
pdata用于保存弹出数据变量的指针。

返回值说明:

该函数会返回一个 QUEUE_StatusTypeDef 枚举数据类型,返回值会根据队列状态返回以下几个值:

返回值描述
QUEUE_OK数据弹出队列成功。
QUEUE_VOID未弹出数据到队列中,原因队列为空。

参考代码:

QUEUE_DATA_T temp;
if(QUEUE_OK = Queue_Pop(&qUartTx, &temp))
{   
     // temp 为队列弹出的数据
}
else
{    
    // 弹出数据失败
}

2、多数据弹出

若需要将多个数据弹出队列可以使用 Queue_Pop_Array 函数,原理上循环调用 Queue_Pop 函数来实现的,需要注意的是,成功弹出的数据将从队列中删除,函数原型如下:

unsigned int Queue_Pop_Array(QUEUE_HandleTypeDef * hqueue, QUEUE_DATA_T * pdatas, unsigned int len)

参数说明:

参数名描述
hqueue需要弹出数据的队列结构。
pdatas用于保存弹出数据数组的首地址。
len需要弹出数据数组的长度。
当需要弹出数据的长度大于队列中的数据长度时,弹出数组多余的空间将不会被赋值。

返回值说明:

  • 该函数将返回实际从队列中弹出的数据长度。

  • 当队列中的数据长度足够时,返回值将等于参数 len 的值。

  • 当队列中的数据长度不足时,返回值为实际从队列中弹出的数据长度。

3、单数据复制

当需要从队列头部获取数据,但又不希望数据从队列中删除时,可以使用 Queue_Peek 函数来实现,该函数的参数与返回值与 Queue_Pop 完全相同。

使用 Queue_Peek 和 Queue_Pop 函数的区别在于:

  • Queue_Pop 得到队列中的数据后会删除队列中的数据。
  • Queue_Peek 得到队列中的数据后会保留队列中的数据。

4、多数据复制

当需要从队列头部获取多个数据,但又不希望数据从队列中删除时,可以使用 Queue_Peek_Array 函数来实现,该函数的参数与返回值与 Queue_Pop_Array 完全相同。

使用 Queue_Peek_Array 和 Queue_Pop_Array 函数的区别在于:

  • Queue_Pop_Array 得到队列中的数据后会删除队列中的数据。
  • Queue_Peek_Array 得到队列中的数据后会保留队列中的数据。

八、其他功能

1、清空队列

当需要清空队列数据时,无需弹出所有数据,只需要调用 Queue_Clear 即可快速清空指定队列,在创建队列时会调用此函数来初始化队列,因此对于刚创建完成的队列无需调用清空队列函数。

函数原型:

void Queue_Clear(QUEUE_HandleTypeDef * hqueue)

参数说明:

参数名描述
hqueue需要清空的队列结构。

2、获取队列数据数量

当需要获取队列中的数据长度时,调用 Queue_Count 函数,函数原型如下:

unsigned int Queue_Count(QUEUE_HandleTypeDef * hqueue)

参数说明:

参数名描述
hqueue需要获取数据长度的队列结构。

返回值说明:

  • 该函数将返回队列中的数据长度。
  • 返回值范围在0到创建队列时的长度之间。

来源:技术让梦想更伟大(作者:李肖遥)

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

围观 24

系统设计人员在为产品添加蓝牙功能时面临诸多障碍,从技术和资源限制到预算限制,从上市时间压力到具有挑战性的性能和集成要求。Microchip Technology Inc.(微芯科技公司)扩大旗下蓝牙低功耗产品组合,推出12 种新产品,旨在为设计人员提供广泛的选择,以帮助其应对独特的挑战,克服从最简单到最高级设计中的各类障碍。这些新增产品包括可直接用于射频应用的WBZ350模块和PIC32CX-BZ3 SoC,为在产品设计中集成蓝牙低功耗单片机(MCU)提供了最低的门槛。如需了解全部12款产品,请访问Microchip蓝牙低功耗网页:https://www.microchip.com/en-us/products/wireless-connectivity/bluetooth...

1.png

Microchip负责无线解决方案事业部的副总裁Rishi Vasuki 表示:“在任何类型的产品中添加蓝牙功能时,选择越来越重要。开发人员需要各种选项,并能根据自身技能和应用需求在不同选项之间进行切换。没有其他供应商生产的蓝牙低功耗产品能像我们的即插即用模块一样简单易用,也无法为更先进的设计提供如此广泛的选项,所有选项都来自同一家供应商,从而不必以功能换取简单易用或以创新牺牲快速开发。” 

除蓝牙MCU外,Microchip还推出了RNBD350即插即用模块,降低了在产品设计中添加蓝牙低功耗连接的成本和复杂性障碍。这些模块最大限度地减少了射频设计优化、法规认证和软件开发所需的时间、资金和工程资源。对于希望获得更大灵活性的资深工程师,Microchip 可提供强大的无线、多协议MCU片上系统 (SoC)选项。 

Microchip蓝牙低功耗器件的应用示例包括物联网智能家居和楼宇系统、工业物联网(IIoT)解决方案和汽车设计。设计人员在入门级应用中使用新扩展的蓝牙低功耗产品组合,可受益于简便的开发流程,包括内部支持服务和开发工具,而不会影响蓝牙功能。此外,Microchip全面的MCU专业知识还能帮助他们进行产品选择,在需要应对更复杂的设计挑战时,还可以轻松切换到更先进的工业级蓝牙低功耗产品。 

开发人员还可以利用Microchip不断扩大的无线产品组合来实现端到端解决方案。这些产品组合提供一系列采用Wi-Fi®、Zigbee®、Thread 和sub-GHz等流行无线技术的产品,可与蓝牙产品组合无缝协作。 

开发工具

Microchip为客户提供全面的开发工具、软件以及iOS®和Android®平台移动应用程序源代码、构建模块应用示例和演示,以及免费的设计检查服务,帮助客户快速启动产品开发。

供货与定价

如需了解更多信息或购买,请联系Microchip销售代表、全球授权分销商或访问Microchip采购和客户服务网站 www.microchipdirect.com

来源:Microchip微芯

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

围观 15

页面

订阅 RSS - 单片机