124|0

5

帖子

3

TA的资源

一粒金砂(中级)

楼主
 

【Follow me第二季第3期】EK_RA6M5任务汇总 [复制链接]

  本帖最后由 wiwhh 于 2024-12-10 16:05 编辑

【Follow me第二季第3期】EK_RA6M5任务汇总

下载附件  保存到相册

7 天前 上传


        

下载附件  保存到相册

7 天前 上传

  • 更改安装目录,不能含有空格

  • 直接统统下一步等待安装完成,同时设置桌面快捷方式

  • 下载附件  保存到相册

    7 天前 上传

  • 接着在stacks中添加对应的stack,也就是外部中断9和10

  • 配置他们的属性,包括中断名称,触发方式,中断优先级和回调函数

  • 我们新建一个Key_driver.c文件来实现按键的功能。主要就两个对外的接口,一个就是中断的初始化函数。另外一个就是在中断函数中注册回调函数。这样就能实现按键功能的解耦。下面就是该文件的代码。

  • #include "driver_key.h"
    
    /* KEY 外部中断初始化函数 */
    void Key_IRQ_Init(void)
    {
    fsp_err_t err = FSP_SUCCESS;
    
    /* Open ICU module */
    err = R_ICU_ExternalIrqOpen(&g_external_irq9_ctrl, &g_external_irq9_cfg);
    err = R_ICU_ExternalIrqOpen(&g_external_irq10_ctrl, &g_external_irq10_cfg);
    /* 允许中断 */
    err = R_ICU_ExternalIrqEnable(&g_external_irq9_ctrl);
    err = R_ICU_ExternalIrqEnable(&g_external_irq10_ctrl);
    }
    
    
    
    // 注册回调函数的接口
    void key_register_callbacks(callback_func_t key0_cb, callback_func_t key1_cb) {
    callbacks.key0_cb = key0_cb;
    callbacks.key1_cb = key1_cb;
    }
    
    // key0 的中断处理函数
    void key0_callback(external_irq_callback_args_t *p_args) {
    if (callbacks.key0_cb != NULL) {
    callbacks.key0_cb();
    }
    }
    
    // key1 的中断处理函数
    void key1_callback(external_irq_callback_args_t *p_args) {
    if (callbacks.key1_cb != NULL) {
    callbacks.key1_cb();
    }
    }
    1. 在LED线程中调用按键的接口,注册回调函数来调节LED的闪烁频率。
    static uint8_t Led_frequencyindex = 0;
    static uint32_t Led_frequency[] = {10, 300, 500, 1000, 1300};
    
    void my_key0_handler(void) {
    Led_frequencyindex = (Led_frequencyindex+1)%5;
    }
    
    void my_key1_handler(void) {
    if(Led_frequencyindex == 0)
    {
    Led_frequencyindex = sizeof(Led_frequency)/sizeof(Led_frequency[0]) - 1;
    }
    else
    {
    Led_frequencyindex = (Led_frequencyindex-1)%(sizeof(Led_frequency)/sizeof(Led_frequency[0]));
    }
    }
    void blinky_thread_entry (void * pvParameters)
    {
    FSP_PARAMETER_NOT_USED(pvParameters);
    
    /* LED type structure */
    bsp_leds_t leds = g_bsp_leds;
    Key_IRQ_Init();
    key_register_callbacks(my_key0_handler, my_key1_handler);
    /* If this board has no LEDs then trap here */
    if (0 == leds.led_count)
    {
    while (1)
    {
    ; // There are no LEDs on this board
    }
    }
    
    /* Holds level to set for pins */
    bsp_io_level_t pin_level = BSP_IO_LEVEL_LOW;
    
    while (1)
    {
    /* Enable access to the PFS registers. If using r_ioport module then register protection is automatically
    * handled. This code uses BSP IO functions to show how it is used.
    */
    R_BSP_PinAccessEnable();
    
    /* Update all board LEDs */
    for (uint32_t i = 0; i < leds.led_count; i++)
    {
    /* Get pin to toggle */
    uint32_t pin = leds.p_leds[i];
    
    /* Write to this pin */
    R_BSP_PinWrite((bsp_io_port_pin_t) pin, pin_level);
    }
    
    /* Protect PFS registers */
    R_BSP_PinAccessDisable();
    
    /* Toggle level for next write */
    if (BSP_IO_LEVEL_LOW == pin_level)
    {
    pin_level = BSP_IO_LEVEL_HIGH;
    }
    else
    {
    pin_level = BSP_IO_LEVEL_LOW;
    }
    
    vTaskDelay(Led_frequency[Led_frequencyindex]);
    }
    }
    1. 全局变量定义:定义了两个全局变量Led_frequencyindex和Led_frequency。Led_frequencyindex用于记录当前选择的闪烁频率索引,而Led_frequency数组存储了五个不同的闪烁频率(单位为毫秒),分别为10ms、300ms、500ms、1000ms和1300ms。
    2. 按键处理函数:
      1. my_key0_handler:当按下键0时,Led_frequencyindex递增1,然后取模5,确保其值始终在0到4之间循环,对应于Led_frequency数组中的五个频率。
      2. my_key1_handler:当按下键1时,如果Led_frequencyindex已经是0,则将其设置为最大索引值(即4),否则减1并取模5,同样保证索引值在有效范围内。
    3. 线程入口函数blinky_thread_entry
      1. 初始化按键中断和注册按键回调函数my_key0_handler和my_key1_handler,这样当用户按下键0或键1时,会分别调用这两个函数来更新Led_frequencyindex。

    下载附件  保存到相册

    7 天前 上传

    下载附件  保存到相册

    6 天前 上传

     

        ​​​​​​​      

    • 接下来打开配置界面,确认pin脚配置是否正确

        ​​​​​​​      

     

    下载附件  保存到相册

    6 天前 上传

     

    • 打开xshell,通过串口连接。

        ​​​​​​​    

     

    • 通过命令4开始执行上面的代码,结果打印在控制台。可以看到OSPI的通信速率要比QSPI快很多。  我尝试了将HIGH_SPEED_MODE定义为0,结果程序卡死了。

        ​​​​​​​      

    2.4 DAC测试

    • 添加DAC设备,设置通道0,同时设置引脚。

        ​​​​​​​    

     

    • 因为没有示波器,我们也添加一个ADC设备

        ​​​​​​​    

     

    • 通过杜邦线将两个引脚连接起来

        ​​​​​​​    

     

    • 编写测试代码
    /**初始化DAC*/
    fsp_err_t common_init(void)
    {
        fsp_err_t fsp_err = FSP_SUCCESS;
        fsp_err = R_DAC_Open(&g_dac0_ctrl, &g_dac0_cfg);
        fsp_err = R_DAC_Start(&g_dac0_ctrl);
        fsp_err = adc_initialize();
        ....
    }
    /**初始化ADC*/
    static fsp_err_t adc_initialize(void)
    {
        fsp_err_t fsp_err = FSP_SUCCESS;
    
        fsp_err = R_ADC_Open (&g_adc_ctrl, &g_adc_cfg);
        if (FSP_SUCCESS != fsp_err)
        {
            return fsp_err;
        }
    
        fsp_err = R_ADC_ScanCfg (&g_adc_ctrl, &g_adc_channel_cfg);
        if (FSP_SUCCESS != fsp_err)
        {
            return fsp_err;
        }
    
        fsp_err = R_ADC_ScanStart (&g_adc_ctrl);
        if (FSP_SUCCESS != fsp_err)
        {
            return fsp_err;
        }
    
        /* Read TSN cal data (value written at manufacture, does not change at runtime) */
        //fsp_err = R_ADC_InfoGet (&g_adc_ctrl, &g_adc_info_rtn);
    
        return fsp_err;
    }
    uint16_t Read_ADC_Voltage_Value(void)
    {
       uint16_t adc_data;
    
       (void)R_ADC_ScanStart(&g_adc_ctrl);
       while (!scan_complete_flag) //等待转换完成标志
       {
          ;
       }
       scan_complete_flag = false; //重新清除标志位
    
       /* 读取通道0数据 */
       R_ADC_Read(&g_adc_ctrl, ADC_CHANNEL_0, &adc_data);
    
       return adc_data;
    }
    void gpt_blue_callback(timer_callback_args_t * p_args)
    {
        /* Void the unused params */
        FSP_PARAMETER_NOT_USED(p_args);
        double angle = 2 * M_PI * i / TABLE_SIZE;
        double sine_value = sin(angle);
        int mapped_value = (sine_value + 1.0) * 0.5 * MAX_VALUE;
        R_DAC_Write(&g_dac0_ctrl, mapped_value);
        i = (++i)%TABLE_SIZE;
    
        SEGGER_RTT_printf(0, "adcvalue :%d\r\n",Read_ADC_Voltage_Value());
      .....
      }
    
    
    • 打印结果显示

        ​​​​​​​    

     

    3 进阶任务:示例程序中新增命令打印信息

    3.1 例程中命令实现原理 

           在示例程序中,定义了一个名为 menu_fn_tbl 的命令结构体,用于封装每个命令的名称及其对应的功能实现。此结构体包含两个成员:一个是命令名称的字符串指针 p_name,另一个是指向函数的指针 p_func,该函数负责执行与命令相关的操作。

          同时,示例程序中还定义了一个名为 s_menu_items[] 的结构体变量数组。每一个数组元素都是一个 menu_fn_tbl 类型的实例,代表菜单中的一个条目。要添加新的命令到菜单中,只需在这个数组中加入相应的命令名称和关联的功能实现即可。

          当用户在控制台选择特定命令时,程序会查找 s_menu_items[] 数组,找到匹配项后调用其 p_func 成员指向的函数来执行相应功能。

    typedef struct menu_fn_tbl
    {
        char_t * p_name; /*<! Name of Test */
        test_fn ( * p_func)(void); /*<! Pointer to Test Function */
    } st_menu_fn_tbl_t;
    
    /* Table of menu functions */
    static st_menu_fn_tbl_t s_menu_items[] =
    {
        {"Kit Information"                         , kis_display_menu},
        {"Web Server"                              , eth_emb_display_menu},
        {"Network Name Lookup"                     , eth_www_display_menu},
        {"Quad-SPI and Octo-SPI Speed Comparison"  , ext_display_menu},
        {"Cryptography and USB High speed (MSC)"   , enc_display_menu},
        {"Next Steps", ns_display_menu },
        {"", NULL }
    };

    3.2 具体命令功能实现

    本命令的目标是在控制台窗口中显示通过ADC采集到的数据。在之前的小任务中,我们已经完成了ADC功能的实现,因此在这个任务中,我们将直接读取ADC的值,并将其输出到控制台上。为了达到这个目的,定义了一个名为 showADCdata 的函数,它是一个测试函数 (test_fn),负责执行以下操作:

    • 清屏并将光标移至屏幕起始位置。

    • 在一个无限循环中:

      • 将当前ADC值格式化为字符串,并发送到控制台进行显示。

      • 暂停500毫秒,以防止更新速度过快导致显示混乱。

      • 再次清屏并重置光标位置,确保每次更新时数据都在相同的位置显示。

    test_fn showADCdata(void)
    
    {
    
    int8_t c = -1;
    
    uint16_t temp[50];
    
    uint8_t i;
    
    uint16_t adcvalue;
    
    sprintf (s_print_buffer, "%s%s", gp_clear_screen, gp_cursor_home);
    
    
    
    /* ignoring -Wpointer-sign is OK when treating signed char_t array as as unsigned */
    
    print_to_console((void*)s_print_buffer);
    
    
    
    while(1)
    
    {
    
    sprintf (s_print_buffer, "%s:%d\r\n", "ADC:", g_adcvalue);
    
    print_to_console((void*)s_print_buffer);
    
    sprintf (s_print_buffer, "%s%s", gp_clear_screen, gp_cursor_home);
    
    vTaskDelay(500);
    
    print_to_console((void*)s_print_buffer);
    
    
    }
    
    
    
    return (0);
    
    }

    3.3 功能展示

     

     

    我们将ADC引脚连接至3.3V,可以看到控制台正确输出了adc的值。

     

    4 扩展任务:信号发生器

    4.1 功能流程

    简单的功能流程图如下图所示,详细的功能介绍会在代码部分详解。

        ​​​​​​​    

    4.2 工程配置

     

     

    为了方便功能的实现,我有连接了一个额外的按键模块

     

    通过RASC配置初始工程,用到的模块如图左边所示。

    4.3 代码部分

    main 函数

    void hal_entry(void)
    {
        /* TODO: add your own code here */
        uint16_t i = 0;
        uint16_t j = 0;
        uint32_t FlashID = 0;
        uint32_t FlashDeviceID = 0;
        uint16_t u16temp;
        Debug_UART4_Init(); // SCI4 UART 调试串口初始化
        printf("hellow world\r\n");
        QSPI_Flash_Init();  // 串行FLASH初始化
        adc_init();
        dac_init();
        R_ICU_ExternalIrqOpen(&g_external_irq9_ctrl, &g_external_irq9_cfg);
        R_ICU_ExternalIrqOpen(&g_external_irq10_ctrl, &g_external_irq10_cfg);
        /* 允许中断 */
        R_ICU_ExternalIrqEnable(&g_external_irq9_ctrl);
        R_ICU_ExternalIrqEnable(&g_external_irq10_ctrl);
        while(1)
        {
            u16temp = getadcvalue();
            writebuf[i++] = u16temp;
            printf("line1=%d\r\n",u16temp);
            if(i == 1000)
            {
                i = 0;
                QSPI_Flash_Write((uint8_t *)writebuf, 0x000000, 2000);
            }
            if(readflag == 1)
            {
                readflag = 0;
                QSPI_Flash_BufferRead((uint8_t *)readbuf,0x000000,2000);
                for( j = 0; j<1000; j++)
                {
                    printf("line2=%d\r\n",readbuf[j]);
                }
            }
            R_BSP_SoftwareDelay(1,BSP_DELAY_UNITS_MICROSECONDS);
        }
    #if BSP_TZ_SECURE_BUILD
        /* Enter non-secure code */
        R_BSP_NonSecureEnter();
    #endif
    }
    
    // key0 的中断处理函数
    void key0callback(external_irq_callback_args_t *p_args) {
        if(get_wave_type() == WAVE_SINE)
        {
            set_wave_type(WAVE_SQUARE);
        }
        else
        {
            set_wave_type(WAVE_SINE);
        }
    }
    
    // key1 的中断处理函数
    void key1callback(external_irq_callback_args_t *p_args) {
    readflag = 1;
    
    }
    uint8_t amplitude_index;
    // key2 的中断处理函数
    void key2callback(external_irq_callback_args_t *p_args) {
        set_wave_amplitude(amplitude_index++);
        amplitude_index = amplitude_index%4;
    
    }
          
    uint32_t g_frequency = 500;
    // key3 的中断处理函数
    void key3callback(external_irq_callback_args_t *p_args) {
        set_wave_frequency(g_frequency);
        g_frequency+=200;
        g_frequency = g_frequency%1500;
    
    }

    该工程没有使用操作系统,只有主函数一个线程,这部分代码主要实现以下功能:

    • 初始化外设,包含以下这些外设:

      • 初始化uart4,用于输出调式信息和用于输出ADC波形。

      • 初始化QSPI,主要用于存储历史波形,存储的数据为adc采集到的波形。

      • 初始化DAC,用于输出0-3.3v的自定义波形,设置的波形目前只有正弦波和方波。

      • 初始化ADC,由于手头没有示波器工具,只能使用板载的ADC,由于测试使用ADC的功能在主函数中实现,采样率只有勉强1khz。

      • 开启外部中断,实现按键功能,用于调整波形和输出历史波形。

    • 开启一个1ms执行一次的循环,实现的功能如下:

      • 获取ADC采样的数据,adc的值存储在一个全局变量中。

      • 将ADC的值存储到一个数组中,当数组存了大概1s的数据后,存储到外部flash中,该过程可能比较耗时,因为擦除这个过程花费很多时间,会稍微影响其他任务,但不影响DAC的波形输出。

      • 如果检测到读取历史波形的按键按下,会读取flash中的数据,并打印出来。

    定时器中断

    DAC的功能主要依赖定时器中断,下面列出这部分代码

    volatile uint32_t timer_ticks = 0;  // 用于计数定时器中断次数
    uint32_t wave_frequency = 1000;  // 波形频率,默认1kHz
    uint64_t pclkd_freq_hz           //时钟频率
    uint16_t wave_amplitude = 2048;  // DAC最大值
    WaveType current_wave_type = WAVE_SINE;  // 默认为正弦波
    uint16_t dac_value = 0;
    // 定时器中断回调函数
    void dac_timercallback(timer_callback_args_t * p_args) {
        FSP_PARAMETER_NOT_USED(p_args);
        switch (current_wave_type) {
            case WAVE_SINE:
                // 正弦波的计算
                dac_value = (wave_amplitude * (1 + sin(2 * M_PI * timer_ticks / wave_frequency))) / 2;
                break;
            case WAVE_SQUARE:
                // 方波的计算
                dac_value = (timer_ticks < wave_frequency / 2) ? wave_amplitude : 0;
                break;
        }
    
        R_DAC_Write(&g_dac0_ctrl,dac_value);
        timer_ticks++;
        if (timer_ticks >= wave_frequency) {
            timer_ticks = 0;
        }
    }
    // 设置波形频率
    void set_wave_frequency(uint32_t freq) {
        // 重新配置定时器以适应新的频率
        uint32_t period_counts =(uint32_t) (((uint64_t) pclkd_freq_hz ) / freq)-1;
        // 这里需要根据实际使用的定时器进行相应的配置
        R_GPT_PeriodSet(&dac_timer_ctrl, period_counts);
    }
    
    // 设置波形幅值
    void set_wave_amplitude(Amplitude amp) {
        switch(amp)
        {
            case AMP_500:
                wave_amplitude = 500;
                break;
            case AMP_1500:
                wave_amplitude = 1500;
                break;
            case AMP_2500:
                wave_amplitude = 2500;
                break;
            case AMP_3500:
                wave_amplitude = 3500;
                break;
        }
    }
    
    // 设置波形类型
    void set_wave_type(WaveType type) {
        current_wave_type = type;
    }
    
    WaveType get_wave_type(void) {
        return current_wave_type;
      }

    在dac_timercallback定时器中断回调函数中实现的功能如下。

    • 根据用户选择的波形计算DAC输出值

      • 正弦波

        • 使用 sin() 函数生成周期性的波动。
        • 将时间(timer_ticks)映射到正弦波的一个完整周期。
        • 调整输出范围以匹配DAC的输出值,确保输出在0到最大幅值之间。
      • 方波
        • 根据定时器计数值 (timer_ticks) 判断当前是否处于半个周期内。
        • 如果是前半个周期,则输出最大幅值; 否则输出0,形成高、低电平的交替。

    同时,我们还提供了以下的几个外部接口,用以调整波形

    • set_wave_frequency:设置波形,主要利用R_GPT_PeriodSet接口来设置定时器的周期,通过R_FSP_SystemClockHzGet接口可以获取定时器的时钟频率,利用时钟频率和需求的定时器频率就可以计算出定时器的溢出值。

    • set_wave_amplitude:设置幅值,这部分简单,调整wave_amplitude变量就能实现DAC波形的变化。

    • set_wave_type:设置波形的类型,即用以输出正弦波和方波。

    adc中断

    uint16_t getadcvalue(void)
    {
        return g_adcvalue;
    }
    
    void g_adc0_callback(adc_callback_args_t * p_args)
    {
        FSP_PARAMETER_NOT_USED(p_args);
        R_ADC_Read(&g_adc0_ctrl, ADC_CHANNEL_0, &g_adcvalue);
        (void)R_ADC_ScanStart(&g_adc0_ctrl);
    }

    该中断的任务比较简单,就是在ADC转换完成后,读取adc的值存储到g_adcvalue变量中,并重新开启adc转换。 同时提供外部一个接口getadcvalue用以获取g_adcvalue中的值。

    4.4 功能展示

    我们使用串口打印adc的值,利用串口工具将数值通过波形展示,

     

     

     

    蓝色的线条为输出的历史波形,目前只做了保存1s。

     

    总结

    参加这项活动还是非常开心的,能够和很多大佬一起交流,得捷提供的开发板不得不说性能非常强大,我只是简单测试了一些基本的功能,希望以后能够好好利用这款开发板,进一步学习这块开发板。

    视频链接

    【Follow me第二季第3期】任务汇总-【Follow me第二季第3期】任务汇总-EEWORLD大学堂

    代码链接

    【 Follow me第二季第3期】任务1-嵌入式开发相关资料下载-EEWORLD下载中心

    【Follow me第二季第3期】任务2、3-嵌入式开发相关资料下载-EEWORLD下载中心

    【Follow me第二季第3期】任务4-嵌入式开发相关资料下载-EEWORLD下载中心

     

    点赞 关注
     
     

    回复
    举报
    您需要登录后才可以回帖 登录 | 注册

    随便看看
    查找数据手册?

    EEWorld Datasheet 技术支持

    相关文章 更多>>
    关闭
    站长推荐上一条 1/10 下一条

     
    EEWorld订阅号

     
    EEWorld服务号

     
    汽车开发圈

    About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

    站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

    北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

    电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
    快速回复 返回顶部 返回列表