单片机/MCU论坛
直播中

jf_08251207

2年用户 26经验值
擅长:可编程逻辑 电源/新能源 模拟技术 测量仪表 EMC/EMI设计 嵌入式技术 存储技术 CRF/无线 接口/总线/驱动 处理器/DSP 光电显示 控制/MCU EDA/IC设计 RF/无线
私信 关注
[文章]

【RA4M2-SENSOR开发板评测】Analogue+Timers综合测试

DAC输出正弦波视频

很荣幸获得了RA生态工作室举办的RA4M2-SENSOR开发板评测活动,板子已经收到,由于我用不惯官方的IDE,所以评测都是以Keil为主。

本次进行任务1中的Analogue和Timers两部分的综合测试,电容式触摸感应单元(CTSU)功能在e2stduio上可以正常运行,keil上无法正常运行,问题还没有找到,解决了后续会分享相关测试。

板载资源介绍如下:

1.两个 USB 全速 2.0 主机(Type-C 连接器)

2.两种烧录方式

SWD 烧录口:编程或调试 PC

串口烧录:打印数据或烧录

3.用户指示灯和按钮

三个用户指示灯(红色和黄色

两个串口指示灯(红色)指示调试连接

两个电源指示灯(红色)

三个用户按键, 一个复位按键

4.外部晶振

低速晶振: 32.768KHz

高速晶振: 12MHz

5.生态系统扩展

微控制器全部引脚由 CN3、 CN4、 CN7、 CN8 引出

传感器(SENSOR)系列 扩展连接器

这次虽然只是进行Analogue和Timers两部分的综合测试,但是板载的LED、按钮、Uart一个不漏也全部测试一遍。

项目创建过程就不再赘述了,网上的教程多如牛毛,数不胜数。下面我们直接进行所有资源配置的相关介绍。

板载的LED、按钮、Uart的原理图如下:
image.png

image.png

image.png

根据原理图上的引脚定义进行配置,

1.按键配置如下:
image.png

image.png

image.png

2.uart配置如下:
image.png

image.png

3.led配置如下:
image.png

image.png

D3通过PWM控制,所以配置有所不同,如下:
image.png

4.ADC(只启用通道0)软件触发、连续扫描模式,配置如下:
image.png

引脚原理图如下:
image.png

image.png

image.png

image.png

5.DAC0配置如下:
image.png

P014引脚原理图如下:
image.png

image.png

6.Timer0 普通定时1s,控制LED D6闪烁,配置如下:
image.png

7.Timer2 PWM输出控制LED D3,配置如下
image.png

image.png

P103外引引脚原理图如下:

image.png

8.Timer5 输入捕获(PWM脉宽和周期测量,测试Timer2产生的PWM波),配置如下

image.png

image.png

image.png

image.png

P101外引引脚原理图如下:
image.png

如上,这次需要测试的功能模块的所有配置已经完成了,点击如下红框内的按钮就生成Keil工程代码
image.png

下面进行功能验证代码的介绍,

1.systick功能

提供系统时基,代码如下:

volatile uint64_t SysTickms = 0;

fsp_err_t SystickInit(void)
{
    /* 获取处理器时钟uwSysclk */
    uint32_t uwSysclk = R_BSP_SourceClockHzGet(FSP_PRIV_CLOCK_PLL);
    /* 计数周期为uwSysclk/1000 */
    if (SysTick_Config(uwSysclk / 1000) != 0)
    {
        return FSP_ERR_ASSERTION;
    }
    /* Return function status */
    return FSP_SUCCESS;
}

void SysTick_Handler(void)
{
    SysTickms += 1;
}

在主函数中调用SystickInit()即可。

2.uart

主要用于输出调试信息,

这里移植了一个letter_shell和lwrb库,letter_shell实现了类似linux控制台命令行的交互效果,源码地址如下:
https://github.com/NevermindZZT/letter-shell

lwrb库用来处理接收到的串口数据,直接丢入ringbuf中,需要的时候取出来数据使用即可,源码地址如下:
https://github.com/MaJerle/lwrb

具体移植方法可以阅读源码里的README.md文档。

工程中代码如下

#include "lwrb/lwrb.h"
#include "shell_port.h"
lwrb_t buff = {0};
uint8_t buff_data[256];
// 打开串口,入口函数里执行一次即可
err = g_uart9.p_api->open(g_uart9.p_ctrl, g_uart9.p_cfg);
    assert(FSP_SUCCESS == err);
   // 初始化lwrb
   lwrb_init(&buff, buff_data, sizeof(buff_data));  
    // 初始化letter_shell
    userShellInit();

串口中断函数如下

static volatile bool g_uart9_tx_complete = false;
static volatile bool g_uart9_rx_complete = false;
void user_uart9_callback (uart_callback_args_t * p_args)
{
    switch (p_args->event)
    {
        case UART_EVENT_TX_DATA_EMPTY:
        {
            // g_uart9_tx_complete  = true;
            break;
        }        
        case UART_EVENT_TX_COMPLETE:
        {
            g_uart9_tx_complete  = true;
            break;
        }
       case UART_EVENT_RX_COMPLETE:
       {
           g_uart9_rx_complete = true;
           break;
       }
        case UART_EVENT_RX_CHAR:
        {
            lwrb_write(&buff, (uint8_t*)&p_args->data, 1);
            break;
        }
        default:
        {
            break;
        }
    }
}

void uart9_wait_for_tx(void)
{
    while (!g_uart9_tx_complete);
    g_uart9_tx_complete = false;
}
void uart9_wait_for_rx(void)
{
   while (!g_uart9_rx_complete);
   g_uart9_rx_complete = false;
}

letter_shell其他接口移植函数如下:

/**
 * [url=home.php?mod=space&uid=1455510]@file[/url] shell_port.c
 * [url=home.php?mod=space&uid=40524]@author[/url] Letter (NevermindZZT@gmail.com)
 * [url=home.php?mod=space&uid=2666770]@Brief[/url] 
 * [url=home.php?mod=space&uid=644434]@version[/url] 0.1
 * @date 2019-02-22
 * 
 * @copyright (c) 2019 Letter
 * 
 */

#include "shell.h"
#include "hal_data.h"
//#include "cevent.h"
#include "log.h"


Shell shell;
char shellBuffer[256];
#if (HELL_TASK_WHILE == 1)
#include "FreeRTOS.h"
#include "task.h"
static SemaphoreHandle_t shellMutex;
#endif
/**
 * @brief 用户shell写
 * 
 * @param data 数据
 * @param len 数据长度
 * 
 * @return short 实际写入的数据长度
 */
short userShellWrite(char *data, unsigned short len)
{
 //   serialTransmit(&debugSerial, (uint8_t *)data, len, 0x1FF);
 
    g_uart9.p_api->write(g_uart9.p_ctrl, (uint8_t const *const)data, (uint32_t)len);
    /* 等待发送完毕 */
    uart9_wait_for_tx();
    return (short)len;
}


/**
 * @brief 用户shell读
 * 
 * @param data 数据
 * @param len 数据长度
 * 
 * @return short 实际读取到
 */
short userShellRead(char *data, unsigned short len)
{
	(void)data;
	(void)len;
 //   return serialReceive(&debugSerial, (uint8_t *)data, len, 0);
	return 0;
}

/**
 * @brief 用户shell上锁
 * 
 * @param shell shell
 * 
 * @return int 0
 */
int userShellLock(Shell *sh)
{
	(void)(sh);
#if (HELL_TASK_WHILE == 1)
    xSemaphoreTakeRecursive(shellMutex, portMAX_DELAY);
#endif
    return 0;
}

/**
 * @brief 用户shell解锁
 * 
 * @param shell shell
 * 
 * @return int 0
 */
int userShellUnlock(Shell *sh)
{	
	(void)(sh);
#if (HELL_TASK_WHILE == 1)
    xSemaphoreGiveRecursive(shellMutex);
#endif
    return 0;
}


void uartLogWrite(char *buffer, short len);
Log uartLog = {
    .write = uartLogWrite,
    .active = 1,
    .level = LOG_DEBUG
};

void uartLogWrite(char *buffer, short len)
{
    if (uartLog.shell)
    {
        shellWriteEndLine(uartLog.shell, buffer, len);
    }
}

/**
 * @brief 用户shell初始化
 * 
 */
void userShellInit(void)
{

#if (HELL_TASK_WHILE == 1)
    shellMutex = xSemaphoreCreateMutex();
    shell.lock = userShellLock;
    shell.unlock = userShellUnlock;
#endif
    shell.write = userShellWrite;
  //  shell.read = userShellRead;
    shellInit(&shell, shellBuffer, sizeof(shellBuffer));
    logRegister(&uartLog, &shell);
#if (HELL_TASK_WHILE == 1)
    if (xTaskCreate(shellTask, "shell", 256, &shell, 5, NULL) != pdPASS)
    {
        logError("shell task creat failed");
    }
#endif
}
// CEVENT_EXPORT(EVENT_INIT_STAGE2, userShellInit);
static const char *shellText[] =
{
#if SHELL_SHOW_INFO == 1
    [SHELL_TEXT_INFO] =
        "\\\\r\\\\n"
    #if 0
        " _         _   _                  _          _ _ \\\\r\\\\n"
        "| |    ___| |_| |_ ___ _ __   ___| |__   ___| | |\\\\r\\\\n"
        "| |   / _ \\\\\\\\ __| __/ _ \\\\\\\\ '__| / __| '_ \\\\\\\\ / _ \\\\\\\\ | |\\\\r\\\\n"
        "| |__|  __/ |_| ||  __/ |    \\\\\\\\__ \\\\\\\\ | | |  __/ | |\\\\r\\\\n"
        "|_____\\\\\\\\___|\\\\\\\\__|\\\\\\\\__\\\\\\\\___|_|    |___/_| |_|\\\\\\\\___|_|_|\\\\r\\\\n"
    #else
        "    ____  ___   __ __  __  ______       _____                           \\\\r\\\\n"          
        "   / __ \\\\\\\\/   | / // / /  |/  /__ \\\\\\\\     / ___/___  ____  _________  _____\\\\r\\\\n"
        "  / /_/ / /| |/ // /_/ /|_/ /__/ /     \\\\\\\\__ \\\\\\\\/ _ \\\\\\\\/ __ \\\\\\\\/ ___/ __ \\\\\\\\/ ___/\\\\r\\\\n"
        " / _, _/ ___ /__  __/ /  / // __/     ___/ /  __/ / / (__  ) /_/ / /    \\\\r\\\\n"
        "/_/ |_/_/  |_| /_/ /_/  /_//____/____/____/\\\\\\\\___/_/ /_/____/\\\\\\\\____/_/     \\\\r\\\\n"
        "                               /_____/                                  \\\\r\\\\n"
    #endif
        "\\\\r\\\\n"
        "Build:       "__DATE__" "__TIME__"\\\\r\\\\n"
        "Version:     "SHELL_VERSION"\\\\r\\\\n"
        "Copyright:   (c) 2020 Letter\\\\r\\\\n",
#endif
    [SHELL_TEXT_CMD_TOO_LONG] = 
        "\\\\r\\\\nWarning: Command is too long\\\\r\\\\n",
    [SHELL_TEXT_CMD_LIST] = 
        "\\\\r\\\\nCommand List:\\\\r\\\\n",
    [SHELL_TEXT_VAR_LIST] = 
        "\\\\r\\\\nVar List:\\\\r\\\\n",
    [SHELL_TEXT_USER_LIST] = 
        "\\\\r\\\\nUser List:\\\\r\\\\n",
    [SHELL_TEXT_KEY_LIST] =
        "\\\\r\\\\nKey List:\\\\r\\\\n",
    [SHELL_TEXT_CMD_NOT_FOUND] = 
        "Command not Found\\\\r\\\\n",
    [SHELL_TEXT_POINT_CANNOT_MODIFY] = 
        "can't set pointer\\\\r\\\\n",
    [SHELL_TEXT_VAR_READ_ONLY_CANNOT_MODIFY] = 
        "can't set read only var\\\\r\\\\n",
    [SHELL_TEXT_NOT_VAR] =
        " is not a var\\\\r\\\\n",
    [SHELL_TEXT_VAR_NOT_FOUND] = 
        "Var not Fount\\\\r\\\\n",
    [SHELL_TEXT_HELP_HEADER] =
        "command help of ",
    [SHELL_TEXT_PASSWORD_HINT] = 
        "Please input password:",
    [SHELL_TEXT_PASSWORD_ERROR] = 
        "\\\\r\\\\npassword error\\\\r\\\\n",
    [SHELL_TEXT_CLEAR_CONSOLE] = 
        "\\\\033[2J\\\\033[1H",
    [SHELL_TEXT_CLEAR_LINE] = 
        "\\\\033[2K\\\\r",
    [SHELL_TEXT_TYPE_CMD] = 
        "CMD ",
    [SHELL_TEXT_TYPE_VAR] = 
        "VAR ",
    [SHELL_TEXT_TYPE_USER] = 
        "USER",
    [SHELL_TEXT_TYPE_KEY] = 
        "KEY ",
    [SHELL_TEXT_TYPE_NONE] = 
        "NONE",
#if SHELL_EXEC_UNDEF_FUNC == 1
    [SHELL_TEXT_PARAM_ERROR] = 
        "Parameter error\\\\r\\\\n",
#endif
};

在while循环中调用如下函数就完成了letter_shell的功能,同时也完成了自带的log输出功能。

// shell
        if (lwrb_get_full(&buff))
        {
            char ch = '0';
            lwrb_read(&buff,&ch,1);
            shellHandler(&shell, ch);
        }

效果如下:
image.png

3.按键

移植了MultiButton库,实现按键的单击、双击、长按的处理,MultiButton源码地址如下:
https://github.com/0x1abin/MultiButton

具体移植方法可以阅读源码里的README.md文档。工程中代码如下

#include "multi_button.h"
enum Button_IDs {
	btn1_id = 0,
	btn2_id,
	btn3_id,
    btn_max,
};

struct Button btn[btn_max];
// 定义按键状态扫描函数
uint8_t read_button_GPIO(uint8_t button_id)
{
    // you can share the GPIO read function with multiple Buttons
    bsp_io_level_t btnState = BSP_IO_LEVEL_HIGH;
    switch(button_id)
	{
		case btn1_id:
            R_IOPORT_PinRead(&g_ioport_ctrl, BSP_IO_PORT_00_PIN_13, &btnState);
            break;
		case btn2_id:
            R_IOPORT_PinRead(&g_ioport_ctrl, BSP_IO_PORT_01_PIN_12, &btnState);
            break;
		case btn3_id:
            R_IOPORT_PinRead(&g_ioport_ctrl, BSP_IO_PORT_01_PIN_11, &btnState);
            break;
		default:
            break;
	}

    return (uint8_t)btnState;
}
// 定义按键事件回调函数
void Callback_btn_Handler(void *btn_ptr)
{
    Button *_btn = (Button *)btn_ptr;

    switch (_btn->event)
    {
    case PRESS_DOWN:
        logInfo("BTN[%d] press down!",_btn->button_id+1);
        break;
    case PRESS_UP:
        logInfo("BTN[%d] press up!",_btn->button_id+1);
        break;
    case PRESS_REPEAT:
        logInfo("BTN[%d] press repeat!",_btn->button_id+1);
        break;
    case SINGLE_CLICK:
        logInfo("BTN[%d] single click!",_btn->button_id+1);
        break;
    case DOUBLE_CLICK:
        logInfo("BTN[%d] double click!",_btn->button_id+1);
        break;
    case LONG_PRESS_START:
        logInfo("BTN[%d] long press start!",_btn->button_id+1);
        break;
    case LONG_PRESS_HOLD:
        logInfo("BTN[%d] long press hold!",_btn->button_id+1);
        break;
    default:
        break;
    }
}
// 循环初始化函数,在入口函数中执行一次即可
for (uint8_t i = 0; i < btn_max; i++) {
        button_init(&btn[i], read_button_GPIO, 0, i);    
        button_attach(&btn[i], PRESS_DOWN, Callback_btn_Handler);
        button_attach(&btn[i], PRESS_UP, Callback_btn_Handler);
        button_attach(&btn[i], PRESS_REPEAT, Callback_btn_Handler);
        button_attach(&btn[i], SINGLE_CLICK, Callback_btn_Handler);
        button_attach(&btn[i], DOUBLE_CLICK, Callback_btn_Handler);
        button_attach(&btn[i], LONG_PRESS_START, Callback_btn_Handler);
        button_attach(&btn[i], LONG_PRESS_HOLD, Callback_btn_Handler);

        button_start(&btn[i]);
    }
// 按键任务处理函数,需要在while循环中5ms周期调用
if (btn_tick < SysTickms)
        {
            button_ticks();
            btn_tick = SysTickms + 5;
        }

到此就完成了按键的功能测试,按下按键后终端控制台会打印相应的信息,效果如下:
image.png

3.ADC和TSN

uint16_t adc_val[8];
float temperature = 0.0f;
int gi_temperature = 0;
SHELL_EXPORT_VAR(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_VAR_INT), gettemperature, &gi_temperature, test);   // 用于通过shell获取温度,放大100倍显示
logDebug("Start Open adc0");
    err = g_adc0.p_api->open(g_adc0.p_ctrl,g_adc0.p_cfg);
    assert(FSP_SUCCESS == err);
    err = g_adc0.p_api->scanCfg(g_adc0.p_ctrl,g_adc0.p_channel_cfg);
    assert(FSP_SUCCESS == err);
    logDebug("Open adc0 end");
logDebug("Start adc0 scan");
    g_adc0.p_api->scanStart(g_adc0.p_ctrl);

如上代码在入口函数中执行一次即可。

adc中断函数如下:

// ADC转换完成标志
volatile bool scan_complete_flag = false;
void user_adc_callback(adc_callback_args_t * p_args)
{
   FSP_PARAMETER_NOT_USED(p_args);
   scan_complete_flag = true;
}

void adc_wait_for_scan(void)
{
    while (!scan_complete_flag);
    scan_complete_flag = false;
}

bool get_adc_scan_status(void)
{
    return scan_complete_flag;
}

void reset_adc_scan_state(void)
{
    scan_complete_flag = false;
}

4.ADC数据读取及TSN计算代码如下,

// adc & TSN
        if (get_adc_scan_status())   // 监测ADC是否转换完成
        {        
            g_adc0.p_api->read(g_adc0.p_ctrl,ADC_CHANNEL_0,&adc_val[0]);
            g_adc0.p_api->read(g_adc0.p_ctrl,ADC_CHANNEL_TEMPERATURE,&adc_val[1]);
            int32_t    cal127;
            adc_info_t adc_info;
            (void) R_ADC_InfoGet(&g_adc0_ctrl, &adc_info);
            cal127 = (int32_t) adc_info.calibration_data;
            float slope = 4.0f/1000.0f;
            float v1= 3.3f * (float)cal127 / 4096.0f;
            float vs = 3.3f * (float)adc_val[1] /4096.0f;
            temperature = (vs - v1) / slope + 127.0f;
            gi_temperature = (int)(temperature * 100.0f);   // 用于给shell反馈温度数字,放大100倍
            reset_adc_scan_state();
        }

ADC的通道上外接了了一个光敏电阻传感器模块,可以通过读取其数值来控制led D2的亮灭,代码如下:

// led1 100ms         
        if (led1_tick < SysTickms)
        {
            led1_tick = SysTickms + 100; 
            g_ioport.p_api->pinWrite(g_ioport.p_ctrl, BSP_IO_PORT_00_PIN_02, (adc_val[0]>2000));
        }

如上代码需要在while循环中周期执行,至此,ADC和TSN的代码介绍完毕,效果如下

通过shell获取温度

image.png

光敏电阻控制led,接近光影D2熄灭,拿开D2点亮。
2025-11-23T12_11_36.942Z-756053.gif

5.DAC0,输出正弦波代码如下:

//正弦波数据数组变量,根据计算得来
const uint16_t sindata[] = {
   2048, 2460, 2856, 3218, 3532, 3786, 3969, 4072, 4093, 4031, 3887, 3668,
   3382, 3042, 2661, 2255, 1841, 1435, 1054, 714, 428, 209, 65, 3, 24, 127,
   310, 564, 878, 1240, 1636, 2048
};
// 入口函数里执行1次即可,打开并启动DAC0
logDebug("Start Open and start dac0");
    err = g_dac0.p_api->open(g_dac0.p_ctrl,g_dac0.p_cfg);
    assert(FSP_SUCCESS == err);    
    err = g_dac0.p_api->start(g_dac0.p_ctrl);
    assert(FSP_SUCCESS == err);
    logDebug("Open dac0 end");
// 1ms dac-->sin  while循环中周期调用
        if (_ms != SysTickms) 
        {
            if (sindataindex >= 32) {
                sindataindex = 0;
            }
            g_dac0.p_api->write(g_dac0.p_ctrl,sindata[sindataindex++]);
           // DAC_SetVoltage(vol);
        }

通过示波器测量P014引脚,效果如头部视频所示。

6.Timer0,1s周期控制led D6闪烁,代码如下:

// 入口函数里执行1次即可,打开并启动Timer0
logDebug("Start Open and start timer0");
    err = g_timer0.p_api->open(g_timer0.p_ctrl,g_timer0.p_cfg);
    assert(FSP_SUCCESS == err);    
    err = g_timer0.p_api->start(g_timer0.p_ctrl);
    assert(FSP_SUCCESS == err);
    logDebug("Open timer0 end");
// timer0中断函数,1s进入一次
void user_timer0_callback(timer_callback_args_t * p_args)
{
    /* 定时器溢出事件 */
    static bsp_io_level_t level_led3 = BSP_IO_LEVEL_HIGH;  
    if (TIMER_EVENT_CYCLE_END == p_args->event)
    {        
        g_ioport.p_api->pinWrite(g_ioport.p_ctrl, BSP_IO_PORT_01_PIN_04, level_led3);
        level_led3 = !level_led3;
    }
}

将光敏电阻靠近光源来控制D2熄灭,突出D6的闪烁效果,如下:
2025-11-23T12_23_21.061Z-574884.gif

7.Timer2 输出PWM波形,代码如下:

uint8_t gpt2_pwma_duty = 20;
SHELL_EXPORT_VAR(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_VAR_CHAR), varPwm, &gpt2_pwma_duty, test);   // 用于通过shell设置占空比
/** 自定义函数:设置PWM占空比   需要循环调用
    @param duty 占空比范围:0~100 %
*/
void GPT2_PWMA_SetDuty(uint8_t duty)
{
    timer_info_t info;
    uint32_t current_period_counts;
    uint32_t duty_cycle_counts;

    if (duty > 100)
        duty = 100; //限制占空比范围:0~100

    /* 获得GPT的信息 */
    //R_GPT_InfoGet(&g_timer_gpt2_ctrl, &info);
    g_timer0.p_api->infoGet(g_timer2.p_ctrl, &info);

    /* 获得计时器一个周期需要的计数次数 */
    current_period_counts = info.period_counts;

    /* 根据占空比和一个周期的计数次数计算GTCCR寄存器的值 */
    duty_cycle_counts = (uint32_t)(((uint64_t) current_period_counts * duty) / 100);

    /* 最后调用FSP库函数设置占空比 */
    // R_GPT_DutyCycleSet(&g_timer_gpt2_ctrl, duty_cycle_counts, GPT_IO_PIN_GTIOCA);
    g_timer0.p_api->dutyCycleSet(g_timer2.p_ctrl, duty_cycle_counts, GPT_IO_PIN_GTIOCA);
}
//入口函数里执行1次即可,打开并启动Timer2输出pwm波形
logDebug("Start Open and start timer2 pwm");
    err = g_timer2.p_api->open(g_timer2.p_ctrl,g_timer2.p_cfg);
    assert(FSP_SUCCESS == err);    
    err = g_timer2.p_api->start(g_timer2.p_ctrl);
    assert(FSP_SUCCESS == err);
    GPT2_PWMA_SetDuty(gpt2_pwma_duty);
    logDebug("Open timer2 pwm end");

// while循环里周期调用,修改gpt2_pwma_duty的数值即可改变占空比,默认20%

GPT2_PWMA_SetDuty(gpt2_pwma_duty);

通过逻辑分析仪测量P103或者观察LED D3的光亮强度可以反馈占空比的大小。

30%
image.png

50%
image.png

80%
image.png

通过修改占空比,肉眼可见LED D3光亮强度会改变

2025-11-23T12_40_54.592Z-681051.gif

8.Timer5 输入捕获,测量PWM周期、频率、占空比,代码如下:

timer_info_t info;  //用于获取定时器参数信息
uint32_t period;    //输入捕获计数器的计数周期
/* 保存所测量的PWM信号的信息 */
uint32_t pwm_period;            //PWM周期
uint32_t pwm_high_level_time;   //PWM高电平的时间
uint32_t pwm_freq;              //PWM频率
uint32_t pwm_duty;              //PWM占空比
//入口函数里执行1次即可,打开并启动Timer5启用输入捕获功能
logDebug("Start Open and start timer5 cap");
    err = g_timer5.p_api->open(g_timer5.p_ctrl,g_timer5.p_cfg);
    assert(FSP_SUCCESS == err);    
    err = g_timer5.p_api->infoGet(g_timer5.p_ctrl,&info);
    assert(FSP_SUCCESS == err);    
    /* 获取计数周期:GPT的一个周期的计数次数 */
    period = info.period_counts;
    err = g_timer5.p_api->enable(g_timer5.p_ctrl);
    assert(FSP_SUCCESS == err);
    err = g_timer5.p_api->start(g_timer5.p_ctrl);
    assert(FSP_SUCCESS == err);
    logDebug("Open timer5 cap end");
// while循环中1s周期执行,打印结果
if (_sec < SysTickms) 
        {
            _sec = SysTickms + 1000;
            /* 计算PWM的频率 */
            pwm_freq = info.clock_frequency / pwm_period;

            /* 计算PWM的占空比 */
            pwm_duty = pwm_high_level_time * 100 / pwm_period;

            // 打印
            logInfo("High=%d, Period=%d, ", pwm_high_level_time, pwm_period);
            logInfo("Friquency = %dHz, Duty_Cycle = %d%%", pwm_freq, pwm_duty);
            pwm_period = pwm_high_level_time = pwm_freq = 0; //打印完后旧数据清零
        
        }
/* GPT5 输入捕获中断回调函数  结果在这里计算 */
 void user_timer5_callback(timer_callback_args_t * p_args)
 {
     static uint32_t a_time; // A 上升沿捕获的时间
     static uint32_t b_time; // B 下降沿捕获的时间
     static uint32_t c_time; // C 上升沿捕获的时间(其实也就是 A 可以用A'表示)
     static uint32_t overflow_times;     //计数器溢出次数
     static uint8_t  one_period_flag=0;  //用于表示是否完成对一个完整周期的测量


     switch(p_args->event)
     {
         /* 捕获到上升沿 -- 有可能是 A 或者 C (A') 位置 */
         case TIMER_EVENT_CAPTURE_A:
             /* A 开始对某个周期进行测量 */
             if (0 == one_period_flag)
             {
                 a_time = p_args->capture;   //记录捕获的时间 A
                 overflow_times = 0;         //初始化计数器溢出次数
                 one_period_flag ++;         //表示即将完成对某个周期的测量
             }
             /* C (A') 如果测量完了一个周期,则计算PWM信号周期和高电平的时间 */
             else if (1 == one_period_flag)
             {
                 c_time = p_args->capture + overflow_times * period; //记录捕获的时间 C

                 //计算 PWM 周期
                 pwm_period = c_time - a_time;

                 //清零所有标志位
                 overflow_times = 0;
                 one_period_flag = 0;         //标志位清0,重新进入下一轮的测量
             }
             break;

         /* 捕获到下降沿 -- 是 B 位置 */
         case TIMER_EVENT_CAPTURE_B:
             //如果是在测量周期内检测到下降沿
             if (1 == one_period_flag)
             {
                 b_time = p_args->capture + overflow_times * period; //记录捕获的时间 B

                 pwm_high_level_time = b_time - a_time;  //计算高电平时间
             }
             break;

         /* 定时器计数溢出事件 */
         case TIMER_EVENT_CYCLE_END:
             /* 输入捕获期间计数器溢出,则记录溢出次数+1 */
             overflow_times++;
             break;

         default:
             break;
     }
 }

直接测量Timer2 P103引脚的信号,将P103和P101连接在一起,效果如下:

默认20%,20KHz.
image.png

视频效果见底部视频

keil整体工程:*附件:RA4M2_Sensor.7z

测量PWM波形shell

更多回帖

发帖
×
20
完善资料,
赚取积分