单片机

智能电表和HVAC控制器等连接到网络的设备不仅需要具备实时性能,以同时实现复杂的系统执行和网络通信。还需要定期更新固件(FW),以确保持续稳定的应用。

也就是说,负责设备主控的单片机也需要上述性能和功能。那么您是否希望拥有一款在单个芯片中实现这些特性的单片机,以及一款能够对其进行轻松评测的套件呢?

今天,我为大家介绍的就是能够满足上述要求的32位单片机RX66N,以及能够轻松快速地进行评测和PoC试制的评测套件Target board for RX66N。

RX66N卓越的实时性能完全得益于它搭载的CPU和闪存。由RX系列独有RXv3内核组成的CPU在120MHz的主频下可提供707CoreMark的卓越性能,而4MB闪存能够确保CPU以零等待周期运行,因此即使运行的是闪存中存储的代码,也能够保持较高的处理速度。

“32位单片机RX66N帮您解决联网设备面临的难题"

另外,4MB闪存可以实现方便固件更新的双存储区配置,再配合后台操作功能,在系统运行过程中也可以将更新固件下载到闪存中。

“32位单片机RX66N帮您解决联网设备面临的难题"

此外,RX66N还配备有Target board for RX66N套件,可对上述特性进行轻松评测。该套件搭载有仿真电路,只需使用USB线与PC连接即可轻松评测RX66N卓越的实时性能。并且该套件通过将Wi-Fi模块连接到板载Pmod连接器,可以实现基于实际场景中RTOS操作的云连接,以及构建基于网络的固件更新系统。

“32位单片机RX66N帮您解决联网设备面临的难题"

利用针对RX系列的云解决方案以及固件更新用中间件*和样例程序,还可简单快捷地进行评测和PoC试制。

*点击链接查看:RX产品家族系列技术干货分享(八)-介绍可以轻松实现RX系列FOTA(Firmware Update Over-The-Air)固件更新的中间件

如果您对RX66N Target Board感兴趣,并希望体验卓越的实时性能以及可轻松实现固件更新的闪存,那么RX66N绝对是您的不二选择。在下次的相关文章中,我将实际使用RX66N Target Board来为大家展示它的固件更新功能。敬请期待!

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

围观 21

嵌入式编程的需求千变万化,要做到系统稳定,又要代码可复用,就要做到高内聚低耦合。

前言

我们通常认为,在中断中,不能执行耗时的操作,否则会影响系统的稳定性,尤其对于嵌入式编程。对于带操作系统的程序而言,可以通过操作系统的调度,将中断处理分成两个部分,耗时的操作可以放到线程中去执行,但是对于没有操作系统的情况,又应该如何处理呢

比较常见的,我们可能会定义一些全局变量,作为flag,然后在mainloop中不停的判断这些flag,再在中断中修改这些flag,最后在mainloop中执行具体的逻辑,但是这样,无疑会增加耦合,增加程序维护成本。

cpost

cpost正是应用在这种情况下的一个简单但又十分方便的工具,它可以特别方便的进行上下文的切换,减少模块耦合。

cpost链接:

https://github.com/NevermindZZT/cpost

cpost借鉴的Android的handler机制,通过在mainloop中跑一个任务,然后在其他地方,可以是中断,也可以是模块逻辑中,直接抛出需要执行的函数,使其脱离调用处的上下文,运行在mainloop中。cpost还支持延迟处理,可以指定函数在抛出后多久执行

使用

cpost的使用十分简单,这里以使用在嵌入式无操作系统中为例,主要用作中断延迟处理的情况

1、配置系统tick

配置cpost.h中的宏CPOST_GET_TICK(),配置成获取系统tick,以stm32 hal为例

#define     CPOST_GET_TICK()            HAL_GetTick()

2、配置处理进程

在mainloop调用cpostProcess函数

int main(void)
{
    ...
    while (1)
    {
        cpostProcess();
    }
    return 0;
}

3、抛出任务

在中断等需要进行上下文切换的地方调用cpsot接口,使其在mainloop中运行

cpost(intHandler);

原理解析

cpost的原理其实很简单,其代码量也十分少,总共加起来就只有几十行代码,cpost维护了一个而全局的数组

CpostHandler cposhHandlers[CPOST_MAX_HANDLER_SIZE] = {0};

其中,数组的每一个元素表示包含了需要执行的函数和参数,当调用cpost接口时,被post的函数和参数会被保存在这个数组中,然后mainloop中运行的cpostProcess函数会遍历这个数组,当满足条件时,执行对应的函数,从而达到上下文切换的目的

void cpostProcess(void)
{
    for (size_t i = 0; i < CPOST_MAX_HANDLER_SIZE; i++)
    {
        if (cposhHandlers[i].handler)
        {
            if (cposhHandlers[i].time == 0 || CPOST_GET_TICK() >= cposhHandlers[i].time)
            {
                cposhHandlers[i].handler(cposhHandlers[i].param);
                cposhHandlers[i].handler = NULL;
            }
        }
    }
}

其实,cpost的方式,和一开始提到的使用全局的flag进行上下文切换的方法很像,只不过,cpost通过一个数组的维护和直接post函数的方式,省去了维护flag的成本,也不需要将需要执行的函数耦合到mianloop中,从而变得简单易用。

完美解耦 - cevent应用

对于模块化编程来说,如何实现各模块间的解耦一直是一个比较令人头疼的问题,特别是对于嵌入式编程,由于控制逻辑复杂,并且对程序体积有控制,经常容易写出各独立模块之间相互调用的问题。由此,cpost中的cevent组件,通过模仿Android系统中的广播机制,提供了一种非常简单的模块间解耦实现。

原理

cevent借鉴的是Android系统的广播机制,一方面,各模块在工作的时候,都会有多个具体的事件点,在高耦合的编程中,可能会在这些地方调用其他模块的功能,比如说,在通信模块接收到指令的时候,需要闪烁一下指示灯。

使用cevent,我们可以在这些地方抛出一个事件,当前模块不需要关心在这各地方需要执行哪些其他模块的逻辑,由其他模块,或者用户定义一个事件监听,当具体的事件发生时,执行相应的动作。

使用

cevent使用注册的方式监听事件,会依赖于编译环境,目前支持keil,iar,和gcc,对于gcc,需要修改链接文件(.ld),在只读数据区添加:

_cevent_start = .;
KEEP (*(cEvent))
_cevent_end = .;

1、初始化cevent

系统初始化时,调用ceventInit

ceventInit();

2、注册cevent事件监听

在c文件中,调用CEVENT_EXPORT导出事件监听

CEVENT_EXPORT(0, handler, (void *)param);

3、发送cevent事件

在事件发生的地方,调用ceventPost抛出事件

ceventPost(0);

使用cevent解耦模块初始化

嵌入式编程中,我们习惯会在程序启动的时候,调用各个模块的初始化函数,其实这也是一种耦合,会造成main函数中出现很长的初始化代码,借助cevent,我们可以对初始化进行优化解耦。

1、定义初始化事件

定义初始化事件的值,对于初始化,有些模块可能会依赖于其他模块的初始化,会有一个先后顺序要求,所以这里我们可以把初始化分成两个阶段,定义两个事件,当然,如果有更复杂的要求,可以再多分几个阶段,只需要多定义几个事件就行

#define     EVENT_INIT_STAGE1       0
#define     EVENT_INIT_STAGE2       1

2、初始化cevent,抛出事件

在main函数中初始化cevent,并抛出初始化事件

int main(void)
{
    ...
    ceventInit();

    ceventPost(EVENT_INIT_STAGE1);
    ceventPost(EVENT_INIT_STAGE2);
    ...
    return 0;
}

3、注册事件监听

对所有需要初始化的函数注册事件监听,这里我以对letter-shell注册事件监听为例,分为两个部分,初始化串口和初始化shell。

在serial模块中,将串口初始化注册到初始化第一阶段,cevent支持将不大于7个的参数直接传递到注册的监听函数中,下面的注册方式,相当于在EVENT_INIT_STAGE1事件发生的地方,也就是main函数中对应的位置,调用serialInit(&debugSerial)

CEVENT_EXPORT(EVENT_INIT_STAGE1, serialInit, (void *)(&debugSerial));

然后再shell模块中,将shell初始化函数注册到初始化第二阶段。

CEVENT_EXPORT(EVENT_INIT_STAGE1, shellInit);

使用cevent解耦mainloop

再无操作系统的嵌入式编程中,我们如果同时希望运行多个模块的逻辑,通常是在mainloop中循环调用,这种将函数写入mainloop的做法,也会增加耦合

int main(void)
{
    ...

    while (1)
    {
        // 写在mainloop中的模块逻辑
        shellTask(&shell);
        LedProcess();
        ...
    }
    return 0;
}

通过使用cevent,也可以很方便的消除这种耦合

1、定义mainloop事件

定义mainloop事件的值

#define     EVENT_MAIN_LOOP         3

2、在mainloop中抛出事件

去掉mainloop中对其他模块的调用,改为排除mainloop事件

int main(void)
{
    ...

    while (1)
    {
        ceventPost(EVENT_MAIN_LOOP);
    }
    return 0;
}

3、在各模块中注册事件监听

分别在各个模块中,注册对mainloop事件的监听

CEVENT_EXPORT(EVENT_MAIN_LOOP, shellTask, (void *)(&shell));
CEVENT_EXPORT(EVENT_MAIN_LOOP, LedProcess);

结语

cevent是一个非常小的模块,本身代码及其简单,但是,通过模仿广播机制,让cevent可以发挥很强大的功能,通过,还可以结合cpost,实现延迟事件等功能。

来源:
https://blog.csdn.net/qq_34245464/article/details/111804661

围观 42

瑞萨电子微控制器产品重点系列

“瑞萨电子单片机一览"
  • 为满足客户不断增长的需求,瑞萨电子推出的微控制器产品,可让客户充分利用现有资源,同时提供出色的可扩展性。

  • 瑞萨微控制器提供多种存储器和封装选项,具有运行快速、可靠性高、低成本和环保的特性。

  • 融合最新工艺技术、集成大容量闪存、应用范围广泛,包括需要高质量和高可靠性的要求严苛应用领域。

  • 配备强大的支持系统,以帮助减少研发成本,缩短研发时间。包含各种开发工具,包括来自其他公司的产品,并受到广泛的技术文档、软件库和活跃用户社区的支持。作为全球位列前茅的MCU供应商,瑞萨在各种微控制器 (MCU) 的基础上,带来最出色、最强大的解决方案。

RL78系列-真正的低功耗微控制器

“RL78:为下一个十年提供动力"
RL78:为下一个十年提供动力

RL78 8/16位微控制器(MCU)具有业界领先的低功耗特点,正常工作期间功耗可低至41μA/MHz(RL78/G23),时钟工作期间为0.355μA,从而大大提高了电源效率。高精度(±1%)高速片上振荡器、可重写 100 万次的后台操作数据闪存、温度传感器和用于多个电源的接口端口等内置功能有助于降低系统成本和尺寸。

RX系列:性能市场领导者

“瑞萨电子单片机一览"

RX系列由四个产品子系列组成, 可提供从小规模到大规模应用的无缝可扩展性。

  • 旗舰RX700系列,具有最快的性能和最先进的功能。

  • 标准RX600系列和RX200系列,在功率效率和高性能之间实现了最佳平衡。

  • 入门级RX100系列,具有极低的功耗。

RA系列:引领物联网革命

“瑞萨电子单片机一览"
  • 瑞萨电子(RA)32位微控制器(MCU)是业界领先的32位MCU,具有Arm® Cortex®-M33、-M23、-M4处理器内核和PSA认证。

  • 与竞争对手Arm Cortex-M MCU相比,RA通过提供更强的嵌入式安全性、卓越的CoreMark® 性能和超低功耗操作,进而使得自身具有关键优势。PSA认证为客户提供了快速部署安全物联网端点和边缘设备以及面向工业4.0智能工厂设备的信心和保证。

  • RA系列MCU的独特之处在于极低功耗、一流的安全选项,包括Arm TrustZone®技术以及支持所有RA系列产品的瑞萨电子灵活软件程序(FSP)。

  • FSP包括高效的驱动程序和中间件,以简化通信和安全性的实施。FSP的GUI简化并加速了开发过程。它支持灵活使用旧代码,并可与其他RA系列设备轻松兼容和扩展。使用FSP的设计人员还可以访问广泛的Arm生态系统、提供广泛的工具、帮助加快上市时间以及瑞萨电子广泛的合作伙伴网络。

RA:灵活的软件包 (FSP),支持安全设备和物联网连接

生产就绪型外设驱动程序

- 用于访问MCU外设和所需功能的HAL API,ARM 信任区已启用

- 直观的配置器和代码生成器

- 经过测试的单元和系统

- 使用行业标准工具进行静态和动态分析

使用实时操作系统

- 最新的Azure RTOS、FreeRTOS和Flexible软件包集成

- 工具可配置的RTOS资源(线程、互斥锁等)

- 包括裸机支持

连接

- 包括Azure RTOS NetX Duo&add-ons、FreeRTOS TCP/IP stack、Secure Sockets、Wi-Fi&BLE

- 包括MQTT和TLS

- 支持与所有主要云平台的连接

- 用于CDC、MSC、HID(主机和外设)的USB中间件

安全性

- 基于Arm Mbed PSA、NetX Crypto和FSP Crypto API的加密API

- 支持加密硬件加速

- PSA 2级认证

- 安全调试

Renesas MCU生态系统:以技术为导向的广泛应用

“瑞萨电子单片机一览"

瑞萨电子正在实现一个全面的合作伙伴生态系统,以提供一系列软件和硬件构建模块,这些构建模块将与瑞萨电子MCU一起开箱即用。瑞萨电子MCU生态系统将有助于加速物联网应用的开发,包括安全性、连接性和HMI等核心技术。

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

围观 114

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到创建队列时的长度之间。

License

Copyright © 2020 QueueForMcu Released under the GPL-3.0 License.

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

围观 37

在单片机系统里对模拟量的处理要比数字量稍显复杂,但是只要掌握了使用技巧,使用起来也很简单,很多朋友一开始比较纠结于单片机的底层语言,非要先弄个明白才罢休,其实大可不必,重要的是我们要先学会怎么应用。

现以铅酸电池电压检测及充电电流检测为例讲解模拟量的硬件和程序的设计。

如图1为28节铅酸电池的电压检测电路,1--14节组成电池组1,15--28节组成电池组2;第1节正极为BAT+,14与15节之间为BATM,第28节负极为BAT-。输入端的8个二极管的作用是钳位作用;电路计算如图所示。

“图1:电池组电压检测电路"
图1:电池组电压检测电路

如图2为铅酸电池的充电电流检测电路,TA1为工频电流互感器,输入的4个二极管为整流二极管,电流流过R37(510Ω)形成压差△V。电路计算如图所示。

“图2:电池组充电电流检测电路"
图2:电池组充电电流检测电路

如图3为单片机STM32F103CBT6,图1和图2的模拟信号输入至单片机的PA5、PA6、PA7。

“图3:STM32F103CBT6单片机"
图3:STM32F103CBT6单片机

由于代码较多,为便于浏览,我就把其中一部分以截图的形式展示

如图4为单片机adc.c文件的底层配置,把PA5、PA6、PA7端口配置成模拟输入模式。

“图4:配置端口模式"
图4:配置端口模式

如图5对以上三个模拟量进行模数转换并缓存入数组ADC_ConvertedValue[3],得到的AD值的范围是0~4096。

“图5:模数转换并缓存"
图5:模数转换并缓存

如图6把以上两个配置函数整合在一起,定义成模拟量的初始化函数void ADC1_Init(void)。

“图6:初始化"
图6:初始化

如图7在adc.h文件里声明函数void ADC1_Init(void),另外几个函数也在adc的c文件里定义的,后面附上源程序(非截图)。

“图7:声明函数"
图7:声明函数

如图8在main()主函数里调用ADC1_Init()初始化函数(要去掉void),初始化函数一定要放在while(1)的前面,表示在进入while(1)无限循环前只执行一次。Analog_Processing()为模拟量处理函数,要放在while(1)无限循环里面(该函数在下面讲)。

“图8:函数调用"
图8:函数调用

以下为模拟量在main.c文件里的定义。

s16 Charging_Current;		 //充电电流实际值
s16 Battery1_Voltage;		 //电池组1电压实际值
s16 Battery2_Voltage;		 //电池组2电压实际值
s16 Battery_Voltage; 		 //电池组总电压值

下面三个函数的定义都在adc.c文件里面定义的。

以下代码为模拟量处理函数:①对数组ADC_ConvertedValue[3]缓存值进行滤波处理;②对滤波后的AD值转换为实际值。

/******************************
模拟量处理函数
******************************/
void Analog_Processing(void)
{
//对AD值进行滤波
ADC_Charging_Current=Filter(ADC_ConvertedValue[0],ADC_Charging_Current,1,10);
ADC_Battery1_Voltage=Filter(ADC_ConvertedValue[1],ADC_Battery1_Voltage,1,10);
ADC_Battery2_Voltage=Filter(ADC_ConvertedValue[2],ADC_Battery2_Voltage,1,10);
//AD值转换为实际值
Charging_Current = Adc_To_Act(ADC_Charging_Current, 10, 4096, 0, 220);//22.0A
Battery1_Voltage = Adc_To_Act(ADC_Battery1_Voltage, 10, 4096, 0, 267);//267V
Battery2_Voltage = Adc_To_Act(ADC_Battery2_Voltage, 10, 4096, 0, 267);//267V
//两组电压相加得到总电压
Battery_Voltage = Battery1_Voltage + Battery2_Voltage;
}

以下代码为滤波函数,滤波函数有很多,采用合适的才是最实用的(该函数滤波后的值是连续变化的,有些滤波函数滤波后的值是跳变的)。

/******************************
滤波函数(base/k越大,容性越大)
该函数相当于是一个电容,通常取值k=1,base=10
******************************/
u16 Filter(u16 NewData, u16 OldData, u8 k, u8 base)
{
	u16 uiResult;
	if (NewData > OldData)
	{
		uiResult = NewData - OldData;
		uiResult *= k;
		uiResult += base >> 2;
		uiResult /= base;
		uiResult = OldData + uiResult; 
	}
	else if (OldData > NewData)
	{
		uiResult = OldData - NewData;
		uiResult *= k;
		uiResult += base >> 2;
		uiResult /= base;
		uiResult = OldData - uiResult; 
	}
	else
	{
		uiResult = NewData;
	}
	
	return(uiResult);
}

使用方法如下:NewData表示最新采用的模拟量;OldData表示滤波后的模拟量。

ADC_Battery1_Voltage=Filter(ADC_ConvertedValue[1],ADC_Battery1_Voltage,1,10);

为便于逻辑计算、控制及显示,以下代码是把AD值转换为实际值,

/******************************
AD值转换实际值函数
******************************/
s16 Adc_To_Act(s16 Adc_Value, s16 Pre_Adc_Min, s16 Pre_Adc_Max, s16 Pre_Act_Min, s16 Pre_Act_Max)
{
s32 _temp;
s32 _range;
_temp = (s32)((Adc_Value - Pre_Adc_Min) * (Pre_Act_Max - Pre_Act_Min) / (Pre_Adc_Max-Pre_Adc_Min)) + Pre_Act_Min;
_temp = Adc_Value - Pre_Adc_Min;
_range = Pre_Act_Max - Pre_Act_Min;
_temp = _temp * _range;
_range = Pre_Adc_Max - Pre_Adc_Min;
_temp = _temp + _range / 2;
_temp = _temp / _range;
_temp = _temp + Pre_Act_Min;
return(_temp);
}

使用方法如下:Adc_Value表示要转换的模拟量;Pre_Adc_Min表示模拟量AD值的最小值;Pre_Adc_Max表示模拟量AD值的最大值;Pre_Act_Min表示转换后实际值的最小值;Pre_Act_Max表示转换后实际值的最大值;(以下最大实际值220表示22.0A,是因为数码管显示需要小数表示)。

Charging_Current = Adc_To_Act(ADC_Charging_Current, 10, 4096, 0, 220);//22.0A

要点:

① 模拟量的采样电路,我多采用运放的差分放大电路,原因是被测电压可以和运放不用共地,且可有效抑制共模噪声,可达到较高的精确线性测量,比如以上电池组的被测电压的误差与实际相差在0.3V左右;

② 电池组输入至运放的8个1M的电阻是两个为一组的,且功率至少1/4W以上,因为在高压下的电阻容易老化,为保险起见,通常一个电阻的最大压差在100V以下为宜;

③ 电池组分为两组检测,一是为了降低元件所承受的电压,二是为了监视两组电池电压之间是否平衡,达到保护电池目的。

④ 函数应功能模块化,且具备通用性质,便于移植和调用,对于很多朋友应先学会如何使用,底层代码只要会配置就完全足够了。

当然,以上提供的设计是我通常的做法,能满足大多数的常规应用。

来源:今日头条(版主:电卤药丸)
链接:
https://www.toutiao.com/a6635851850929144323/
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 50

续接上期说的华大单片机设计特别注意事项

6. 使用内部RC主时钟RCH切换频率

方案一:切换RCH时必须遵循逐级调高4M->8M->16M->22.12M/24M,或者逐渐降低22.12M/24M->16M->8M->4M的过程,不能一步到位。

方案二:先切到RCL做系统时钟再改RCH。否则会影响产品可靠性,造成大批量时出现不良。

注意:进行时钟切换的时候要把全部中断都关掉。

7. I/O口电压不能大于VCC

华大L110/L13X/L17X/L19X/L07X;F003/F005/F030/F17X/F19X/F07X系列I/O没有5V tolerant功能,所以I/O电压不能大于VCC。

当I/O电压大于VCC时,最先出现的特征是ADC不准。工程师调试时喜欢用PC+USB-UART+串口调试助手来查看UART送出的数据。

这时如果UART电平大于MCU电源电压VCC,ADC数据会不准。

8. I2C模块使用特别注意

I2C模块在初始化的时候,在使能I2C时钟后,对模块进行一次复位,再对I2C其它的寄存器进行初始化。

9. HC32L110/HC32F003/HC32F005复位管脚

HC32L110/HC32F003/HC32F005芯片的RESET管脚可以复用为带上拉的GPIO数字输入端口,外围的复位电路需要保留。

L13X/L17X/L19X/L07X/F030/F17X/F19X/F07X系列芯片RESET管脚不可以利用为GPIO。

10. 低功耗模式程序调试

在应用程序中,如果需要进入低功耗模式,程序将无法进行调试。

如果程序中需要使用该功能,建议在调试开发阶段,在程序一开始添加几秒钟的延时程序,或者添加外部 IO 控制程序等方法来决定是否执行该段程序,或者增加外部唤醒机制,以便在二次调试开发时 SWD 功能能够正常使用。

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

围观 248

各位工程师小伙伴在使用华大单片机设计产品时一定要注意以下事项:

1. Vcap管脚

核电压输出管脚(电压值1.5V左右),必须接电容接地,不能用作其他功能(不可以当参考源给其它电路使用)或悬空。

注意:如果客户希望能采用小一点的电容,L110/F003/F005系列最小4.7uF,L13X/F03X系列最小1uF。

2. MODE(Boot、MD)管脚

(1) L13X/L17X/L19X/L07X/F03X/F17X/F19X/F07X系列正常工作时MODE脚必须接10K电阻接地。MCU上电后检测MODE脚状态,拉低时直接进入用户主程序,拉高时进入华大的引导程序(用于UART口烧录)。

注意:上电时MODE脚不拉低不能进入用户主程序。

(2) L110/F003/F005系列没有MODE脚,默认上电后先执行华大的引导程序,未发现离线烧录器握手信号后自动执行用户主程序(上电到执行用户程序约费时12mS)。

(3)当SWD接口失效无法连接时,需要用UART口才能重新烧录程序。

(4) 造成SWD口失效的情况有:

a. 用户主程序一上电就进入深度休眠模式且不能唤醒;
b. 烧录用户程序并且加密,下一次SWD就无法连接;
c. 用户程序中上电后关掉了SWD功能。

(5) 不建议MODE脚用于其他功能。

3. 离线烧录用的UART口

用于离线烧录的UART口在MCU管脚上是固定的:L110/F003/F005系列对应P35,P36 ;L13X/F03X系列对应PA09,PA10。

注:新版本引导程序中将UART定义到SWD管脚,从而实现SWD和UART用同一个烧录口。有这个需求的请务必和业务人员提出(采购新版引导程序的货)

4. 得到极致的超低功耗

L13X系列为例,我们有28pin-64pin的不同型号,其中28pin/32pin/48pin的型号要按照64pin把未封装出的管脚也设置为输出低。否则小批量时会发现一定比率的芯片表现功耗偏高,且问题跟着芯片走。L110的16pin同理也要按照20pin设置未封装出的管脚。L17X/L19X/L07X同理。相关配置代码可以找华大半导体FAE要。

芯片未使用到的管脚设置为上拉输入,或者输出低电平。

5. 用户程序对内部FLASH擦写特别注意

用户程序需要对内部FLASH擦写时,用户这段擦写程序源代码位置必须定位在0-32K空间内。
对FLASH进行初始化的操作,要把芯片的中断全部关掉。

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

围观 342

首先来说,220V交流电的负载是多大,是感性负载负载还是阻性负载,正常输出功率是多大等这些都要考虑进去。

1、对于阻性负载

比如普通的灯泡,一般是30到40W左右,如果用220V交流电来控制通断,简单点的就用一个双向可控硅直接控制,BT137电流达到7A,耐压值600V,驱动灯泡足够了

“如何用单片机控制220V交流电的通断?"

也可以加一个光耦

“如何用单片机控制220V交流电的通断?"

2、对于感性负载

比如电动机,因为它的内部有线圈,100W的电动机在启动的时候可能达到1000W,因此这类电器电路就要加多一个阻容吸收电路,必要时候同时加一个压敏电阻,可以使10471,根据实际间距选择合适的压敏电阻,因为瞬导通时候电压很高,这样就有起到过压保护,以防一通电或者关断时候产生感应电动势产生的电压把可控硅击穿,有时候还会串联一个电感。

“使用可控硅三极管MOS管的单片机控制220V交流电通断电路图解"
使用可控硅三极管MOS管的单片机控制220V交流电通断电路图解

使用单片机控制220V交流电的通断,方法非常多。使用继电器是最方便的,但是继电器通断会有声音,很不好,而且继电器有次数限制,容易坏。

下面提供几种方法吧,供大家参考

(1)使用双向可控硅,注意是交流电,使用双向可控硅而不是单向可控硅。

“如何用单片机控制220V交流电的通断?"

这种情况比较简单,但是电路可靠性不高,220V和单片机电源必须共地,电路故障很容易高压烧毁低压端的单片机。

“如何用单片机控制220V交流电的通断?"

低压控制高压,最好做隔离,上图为使用光耦隔离的控制方式,也可以使用其它物理隔离芯片。

(2)使用三极管、MOS管的控制方式

“如何用单片机控制220V交流电的通断?"

上图是使用MOS管作开关的电路原理图,因为是交流电,使用两个N沟道的MOS管背靠背连接,该图只是一部分示意图,真正的电路还有很多关键技术,比如采样交流电的极性、判断零点,实现过零开通、断开,以减少对设备的损耗。以及过流、短路保护,区分容性负载上电瞬间的波形与过流、短路波形的区别,防止误保护。使用三极管的原理也是类似的,由于篇幅的原因,就不详细说明。

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

围观 291

页面

订阅 RSS - 单片机