71|0

6834

帖子

11

TA的资源

版主

楼主
 

【Follow me第二季第3期】基础任务 [复制链接]

  本帖最后由 lugl4313820 于 2024-11-18 11:06 编辑

【任务目标】

1、分享创建开发环境。

2、分享下载并实现调试。

3、分享按键来控制板载的LED灯。

【实现步骤】

任务1:

1、此次设计我采用MDK作为编译调试工具。配合瑞萨的RASC来创建、配置工程模块。

2、MDK的安装,目前只要是做单片机开发的,基本上都是安装好了的。所以此次略过。

3、RASC的安装,首先到瑞萨的官方下载FSP安装程序。网址为RA 可扩展性强的配置软件包 (FSP) | Renesas 瑞萨电子。目前这个版本升级非常快,前几天我下载的最新的是5.50,载此到目前已经升级到了5.6.0了,大家也可以选择最新的版本,以前的版本也是可以的。

4、安装一路NEXT就行了,但是要记住安装的目录,我的是放到C:\Renesas\RA\sc_v2024-07_fsp_v5.5.0。这在以后的工程配置需要用到。

5、安装好后,需要安装keil的pack包,大家可以到keil的官网去下载,安装也是非常简单。此处略过。

6、配置MDK的工具,打开工程后,如果需要使用FSP添加或减少模块,需要使用FSP来打开工程,这就需要配置工具。

7、打开Tools->Customize Tools Menu 添加一个新项目,在Command一栏指向刚才我们安装的RSAC的路径,在Initfile Folade在填入$P,在Arguments中填入:--device $D --compiler ARMv6 configuration.xml。再添回一个新选项:Device Partition Manager  ,command跟上面一样,Initiflile也是$P,在参数一栏填入:-application com.renesas.cdt.ddsc.dpm.ui.dpmapplication configuration.xml

  这样,我们在需要打开FSP时就可以使用Tool下的命令就可以直接打开了。

8、使用FSP创建一个基础工程。

首先打开RASC,在第一个对象框中输入工程名称与要存放的目录。

 

9、在接下来的对话框中,选择开发板:EK-RA6M5,同时选择MKD 5,点击下一步。

  10、在接下来的对话中,我们选择第一个,无代码保护:

  11、在操作系统选择中,我们这次选择无操作系统,他可以匹配FreeRTOS,如果有需要,也可以选择。

  12、在这一步,我们需要选择一个模块,一个是有LED闪灯的模版,一个是空模版,我这里选了BLINK。

  最后按一下结束键,就可以创建工程结速了。

【任务2】编译调试

1、打开MDK工程:

  2、点用编译,无警告无错误:

  3、下载,在debug下面,需要添加下载算法:

4、修改地址:

   

4、点击下载按键就可以把工程下载到开发板上了。

5、点击MDK的调试按键就可以进入调试:

 

到此编译下载就完成了。

【添加按键任务】

首先移植一个开源的按键驱动,添加key_driver以个key_manage到工程中:

 

其代码key_manage.h如下:

#ifndef __KEY_MANAGE_H__
#define __KEY_MANAGE_H__

#ifdef __cplusplus
extern "C" {
#endif

#include "key_driver.h"
#include <string.h>

#define __KEY_EVENT_CALL(event)                 \
    do {                                        \
        if (key->event_callback[event]) {       \
            key->event_callback[event](key);    \
        }                                       \
    } while (0U)


#define CHECK_TICK                      5                      // 按键状态机检查间隔时间(ms)
#define DEBOUNCE_TICK                   (15 / CHECK_TICK)      // 消抖时间(ms) [最大40/5且DEBOUNCE_TICK < SHORT_PRESS_START_TICK/2]
#define SHORT_PRESS_START_TICK          (300  / CHECK_TICK)    // 连击态触发间隔时间(ms) [最大40 / 5]
#define LONG_PRESS_START_TICK           (1000 / CHECK_TICK)    // 长按态触发时间(ms)
#define LONG_HOLD_CYCLE_TICK            (500 / CHECK_TICK)     // 长按态保持下连续触发长按事件的间隔(ms)

/**
 * [url=home.php?mod=space&uid=159083]@brief[/url]   按键对象状态
 */
enum key_state {
  Init_None_State = 0,        /* 初始未按下状态 */
  Init_Press_State,           /* 初次按下状态 */
  Press_Check_State,          /* 连击检查状态 */
  Continuous_Press_State,     /* 连续按下状态 */
  Long_Press_State,           /* 长按状态 */
};

/**
 * @brief   按键对象事件
 */
enum key_event {
  Press_Down = 0,             /* 按键按下,每次按下都触发 */
  Press_Up,                   /* 按键弹起,每次松开都触发 */
  Singe_Click,                /* 单击触发(仅触发一次) */
  Double_Click,               /* 双击触发(仅触发一次) */
  Short_Press_Repeat,         /* 每次短按时都会触发(按下次数>=2) */
  Long_Press_Start,           /* 首次进入长按状态触发(仅触发一次) */
  Long_Press_Hold,            /* 长按保持状态触发(每经过一个循环长按间隔触发一次) */
  Event_Sum,                  /* 事件总数 */
  None_Press                  /* 未按下 */
};

/**
 * @brief   按键对象句柄结构体
 */
struct key_handle
{
  uint16_t tick;                                          	  /* 按键系统时间片 */
  uint8_t repeat_cnt     : 4;                             	  /* 按键短按次数 */
  uint8_t event   	     : 4;                             	  /* 触发事件 */
  uint8_t state          : 3;                             	  /* 按键状态 */
  uint8_t debounce_tick  : 3;                             	  /* 消抖时间片 */
  uint8_t active_level   : 1;                                   /* 按键有效按下电平 */
  uint8_t key_level      : 1;                             	  /* 按键引脚当前电平 */
  uint8_t (* pin_read)(void);                         	      /* 获取按键引脚电平 */
  void (* event_callback[Event_Sum])(struct key_handle* key);   /* 按键事件回调函数 */
  struct key_handle* next;                                      /* 单向链表next指针 */
};
typedef struct key_handle *key_handle_t;

/* 按键管理函数接口 */
int8_t key_init(struct key_handle *key, uint8_t (*gpio_pin_read)(void), uint8_t active_level);
int8_t key_handle_register(struct key_handle *key);
int8_t key_handle_detach(struct key_handle *key);
int8_t key_event_callback_register(struct key_handle *key, uint8_t event, void (* event_callback)(key_handle_t key));
void key_tick(void);

#ifdef __cplusplus
}
#endif
#endif /* __KEY_MANAGE_H */

2、其代码key_manage.c如下:

#include "key_manage.h"

static key_handle_t _key_slist_head = NULL;  // 按键管理单链表头结点

/**
 * @brief   初始化按键对象
 * @param   key             按键对象句柄
 * @param   gpio_pin_read   获取按键电平函数指针
 * @param   active_level    按键按下有效电平
 * [url=home.php?mod=space&uid=784970]@return[/url]  0: succeed -1: failed
 */
int8_t key_init(struct key_handle *key, uint8_t (*gpio_pin_read)(void), uint8_t active_level)
{
  if (key == NULL)
    return -1;
  memset(key, 0, sizeof(struct key_handle));
  key->event = None_Press;
  key->active_level = active_level;
  key->pin_read = gpio_pin_read;
  key->key_level = key->pin_read();

  return 0;
}

/**
 * @brief   注册按键:将按键对象插入到按键管理链表中
 * @param   key 按键对象句柄
 * @return  0: succeed -1: failed
 */
int8_t key_handle_register(struct key_handle *key)
{
  struct key_handle *key_slist_node = _key_slist_head;  // 获取头指针的地址 (无头结点单链表)

  if (key == NULL)
    return -1;

  // 尾插(不带头结点的单链表, 头指针需做特殊判断)
  if (_key_slist_head == NULL) // 头指针为空==表空
  {
    _key_slist_head = key;
    key->next = NULL;
    return 0;
  }

  while(key_slist_node)
  {
    if (key_slist_node == key) return -1;       // 重复注册
    if(key_slist_node->next == NULL) break;     // 已经遍历到最后一个节点,必须在此跳出循环, 否则key_slist_node==NULL
    key_slist_node = key_slist_node->next;
  }
  key_slist_node->next = key;
  key->next = NULL;

  return 0;
}

/**
 * @brief   脱离按键:将按键对象从按键管理链表中脱离
 * @param   key 按键对象句柄
 * @return  0: succeed -1: failed
 */
int8_t key_handle_detach(struct key_handle *key)
{
  // 解1级引用指向指针变量, 解2级引用指向指针变量所指向的变量
  struct key_handle **key_slist_node = &_key_slist_head;  // 指向头指针, 直接操作原指针变量(不然最后无法修改头指针)
  struct key_handle *node_temp;
  if (key == NULL || _key_slist_head == NULL)
    return -1;

  while(*key_slist_node && *key_slist_node != key)
  {
    node_temp = *key_slist_node;
    if((*key_slist_node)->next == NULL) break;
    key_slist_node = &node_temp->next;     // 不能直接解1级引用赋值,会破坏原链表
  }

  if (*key_slist_node != key)
    return -1;
  *key_slist_node = (*key_slist_node)->next;

  return 0;
}

/**
 * @brief   注册按键事件触发回调函数
 * @param   key             按键对象句柄
 * @param   event           触发事件类型
 * @param   event_callback  事件回调函数
 * @return  0: succeed -1: failed
 */
int8_t key_event_callback_register(struct key_handle *key, uint8_t event, void (* event_callback)(key_handle_t key))
{
  if (key == NULL || event >= Event_Sum)
    return -1;
  key->event_callback[event] = event_callback;

  return 0;
}

/**
 * @brief   处理所有按键对象的状态机
 * @param   key 按键对象句柄
 * @return  None
 */
static void key_handler(struct key_handle *key)
{
  uint8_t key_level_temp = key->pin_read();

  if(key->state != Init_None_State) key->tick++;

  /* 按键消抖(按键状态发生变化保持DEBOUNCE_TICK时间片开始保存按键引脚电平) */
  if(key_level_temp != key->key_level)
  {
    if(++(key->debounce_tick) >= Double_Click)
    {
      key->key_level = key_level_temp;
      key->debounce_tick = 0;
    }
  }
  else
  {
    key->debounce_tick = 0;
  }

  /* 按键状态机 */
  switch (key->state)
  {
  case Init_None_State:
    /* 初始态-> 初始按下态  Press_Down */
    if(key->key_level == key->active_level)
    {
      key->event = (uint8_t)Press_Down;
      __KEY_EVENT_CALL(Press_Down);
      key->tick  = 0;
      key->repeat_cnt = 1;
      key->state  = Init_Press_State;
    }
    else
    {
      key->event = (uint8_t)None_Press;
    }
    break;

  case Init_Press_State:
    /* 第一次按下松开:初始按下态->连击检查态  Press_Up */
    if(key->key_level != key->active_level)
    {
      key->event = (uint8_t)Press_Up;
      __KEY_EVENT_CALL(Press_Up);
      key->tick = 0;
      key->state = Press_Check_State;

    }
    /* 第一次按下后长按(>LONG_PRESS_START_TICK):初始按下态->长按态  Long_Press_Start */
    else if(key->tick > LONG_PRESS_START_TICK)
    {
      key->event = (uint8_t)Long_Press_Start;
      __KEY_EVENT_CALL(Long_Press_Start);
      key->state = Long_Press_State;
    }
    break;

  case Press_Check_State:
    /* 松开后再次按下:连击检查态->连击态 Press_Down & Short_Press_Repeat */
    if(key->key_level == key->active_level)
    {
      key->event = (uint8_t)Press_Down;
      __KEY_EVENT_CALL(Press_Down);
      key->repeat_cnt++;
      __KEY_EVENT_CALL(Short_Press_Repeat);
      key->tick = 0;
      key->state = Continuous_Press_State;
    }
    /* 松开后再次没有按下(>SHORT_PRESS_START_TICK):连击检查态->初始态 repeat_cnt=1: Singe_Click; repeat_cnt=2: Double_Click */
    else if(key->tick > SHORT_PRESS_START_TICK)
    {
      if(key->repeat_cnt == 1)
      {
        key->event = (uint8_t)Singe_Click;
        __KEY_EVENT_CALL(Singe_Click);
      }
      /* 连击态松开后会返回此条件下触发  todo: <可以做n连击判断> */
      else if(key->repeat_cnt == 2)
      {
        key->event = (uint8_t)Double_Click;
        __KEY_EVENT_CALL(Double_Click);
      }
      key->state = Init_None_State;
    }
    break;

  case Continuous_Press_State:
    /* 连击后松开:连击态->连击检查态(< SHORT_PRESS_START_TICK)) : 连击态->初始态(>= SHORT_PRESS_START_TICK) */
    if(key->key_level != key->active_level)
    {
      key->event = (uint8_t)Press_Up;
      __KEY_EVENT_CALL(Press_Up);

      if(key->tick < SHORT_PRESS_START_TICK)
      {
        key->tick = 0;
        key->state = Press_Check_State;
      }
      else
      {
        key->state = Init_None_State;
      }
    }
    /* 连击后长按(>SHORT_TICKS): 连击态 -> 初始态 */
    else if(key->tick > SHORT_PRESS_START_TICK)
    {
      key->state = Init_Press_State;  // 可以回到Init_None_State/Init_Press_State
    }
    break;

  case Long_Press_State:
    /* 长按保持  Long_Press_Hold */
    if(key->key_level == key->active_level)
    {
      key->event = (uint8_t)Long_Press_Hold;
      if (key->tick % LONG_HOLD_CYCLE_TICK == 0)
      {
        __KEY_EVENT_CALL(Long_Press_Hold);
      }
    }
    /* 长按松开:长按态-> 初始态 */
    else
    {
      key->event = (uint8_t)Press_Up;
      __KEY_EVENT_CALL(Press_Up);

      key->state = Init_None_State;
    }
    break;
  }
}

/**
 * @brief   每经过一个滴答周期调用一次按键处理函数(裸机放1ms中断, OS放线程或中断)
 * @param   None
 * @return  None
 */
void key_tick(void)
{
  struct key_handle *key_slist_node;
  static uint8_t tick_cnt = 0;

  if (++tick_cnt < CHECK_TICK)
    return;

  for (key_slist_node = _key_slist_head; key_slist_node != NULL; key_slist_node = key_slist_node->next)
  {
    key_handler(key_slist_node);
  }
  tick_cnt = 0;
}

key_driver.h:

#ifndef __KEY_DRIVER_H__
#define __KEY_DRIVER_H__

#ifdef __cplusplus
extern "C" {
#endif

#include "hal_data.h"
#define KEY1_SW2_PIN    BSP_IO_PORT_00_PIN_04
#define KEY2_SW3_PIN    BSP_IO_PORT_00_PIN_05

/* 可以为单个引脚设置电平和读取电平 */


uint8_t key_sw1_read_pin(void);
uint8_t key_sw2_read_pin(void);

#ifdef __cplusplus
}
#endif
#endif /* __KEY_DRIVER_H */

key_driver.c:

#include "key_driver.h"

uint8_t key_sw1_read_pin(void)
{
  bsp_io_level_t p_port_value_port_004;
  R_IOPORT_PinRead(&g_ioport_ctrl, BSP_IO_PORT_00_PIN_04, &p_port_value_port_004);
  if(p_port_value_port_004)
		return 1;
	else
		return 0;
}

uint8_t key_sw2_read_pin(void)
{
  bsp_io_level_t p_port_value_port_005;
  R_IOPORT_PinRead(&g_ioport_ctrl, BSP_IO_PORT_00_PIN_05, &p_port_value_port_005);
  if(p_port_value_port_005)
		return 1;
	else
		return 0;
}

在hal_entry.c中引入驱动库,并注册两个按键以及按键的加调函数,这里我注册了按下与抬起的两个事件:

static struct key_handle _key1_sw2;
static struct key_handle _key2_sw3;

static void key1_sw2_callback(key_handle_t key)
{
  switch (key->event) {
  case Press_Down:
    APP_PRINT("SW Press Down\r\n");
		break;
	case Press_Up:
		APP_PRINT("SW Press UP\r\n");
		break;
  }
}

【添加SEGGRTT】

添加SEGGER_RTT的四个文件到工程中:

 

这样我们就可以不配置串口,实现RTT打印日志了。

下载到开发板后,就可以实时的查看到按键的按下与抬起的事件:

 

【闪灯程序】

由于按键需要及时的检测,我这里添加了非阻塞的闪灯程序,即添加一个tick计数器,当计数器大于某个数值,才进行灯的翻转。代码如下,这样就可以实现闪灯与按键的任务了。代码如下:

/*
* Copyright (c) 2020 - 2024 Renesas Electronics Corporation and/or its affiliates
*
* SPDX-License-Identifier: BSD-3-Clause
*/

#include "hal_data.h"
#include "key_manage.h"
#include "common_utils.h"

void R_BSP_WarmStart(bsp_warm_start_event_t event);

extern bsp_leds_t g_bsp_leds;
static struct key_handle _key1_sw2;
static struct key_handle _key2_sw3;

static void key1_sw2_callback(key_handle_t key)
{
  switch (key->event) {
  case Press_Down:
    APP_PRINT("SW Press Down\r\n");
		break;
	case Press_Up:
		APP_PRINT("SW Press UP\r\n");
		break;
  }
}
/*******************************************************************************************************************//**
 * @brief  Blinky example application
 *
 * Blinks all leds at a rate of 1 second using the software delay function provided by the BSP.
 *
 **********************************************************************************************************************/
void hal_entry (void)
{
#if BSP_TZ_SECURE_BUILD

    /* Enter non-secure code */
    R_BSP_NonSecureEnter();
#endif
	int tick;
	
    SEGGER_RTT_Init();
    /* Define the units to be used with the software delay function */
    const bsp_delay_units_t bsp_delay_units = BSP_DELAY_UNITS_MILLISECONDS;

    /* Set the blink frequency (must be <= bsp_delay_units */
    const uint32_t freq_in_hz = 2;

    /* Calculate the delay in terms of bsp_delay_units */
    const uint32_t delay = bsp_delay_units / freq_in_hz;

    /* LED type structure */
    bsp_leds_t leds = g_bsp_leds;

    key_init(&_key1_sw2, key_sw1_read_pin, 0);
    key_event_callback_register(&_key1_sw2,
                                Press_Down,
                                key1_sw2_callback);
																key_event_callback_register(&_key1_sw2,
                                Press_Up,
                                key1_sw2_callback);
    key_handle_register(&_key1_sw2);
    /* 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)
    {
			tick ++;
        /* 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();
				if(tick >0xFFFFF)
				{
					/* Update all board LEDs */
					for (uint32_t i = 0; i < leds.led_count; i++)
					{
							/* Get pin to toggle */
							uint32_t pin = leds.p_leds;

							/* 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;
					}
					tick = 0;
			}

        /* Delay */
      //  R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS);
				key_tick();
    }
}

 

补充内容 (2024-11-18 11:25): 同时我添加一个翻转的代码到按键抬起的事件中,就可以实现按键控制LED3的翻转了。 R_BSP_PinWrite((bsp_io_port_pin_t) leds.p_leds[2], !R_BSP_PinRead((bsp_io_port_pin_t) leds.p_leds[2]));
点赞 关注
 
 

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

随便看看
查找数据手册?

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
快速回复 返回顶部 返回列表