感谢面包板社区和瑞萨生态工作室提供的瑞萨 RA6E2 功能板评测机会。
原本计划是移植MicroPython到该开发板,但是拿到板子后才发现其芯片只有40k RAM,无法满足MicroPython移植的最低要求(60k RAM),只好放弃,改变计划为移植Letter Shell到本开发板。
1、 开发环境的配置
这是我第一次使用瑞萨的产品,所以刚开始花了一些研究了其开发工具的使用。瑞萨的产品可以使用其自研的e2studio开发,也可以使用Keil。两者的开发都依赖于RA灵活配置软件包 (FSP),这个工具类似于意法半导体提供的STM32 CubeMX,可以方便地配置各种参数并生成代码,总体感觉是不错的。
有关开发工具的使用,可以参考以下资料:
l 《瑞萨RA系列FSP库开发实战指南——基于野火启明开发板》,该书的电子版可以访问https://doc.embedfire.com/mcu/renesas/fsp_ra/zh/latest/。
虽然这些资料都不是完全针对RA6E2开发板的,但是基本都适用。
我本人使用的Keil+FSP的解决方案。
这块瑞萨 RA6E2 功能板没有板载的JLink,但是支持串口下载,要是使用串口下载,就需要使用瑞萨的Flash Programmer软件和下图中橙色圈中的USB口,并按照板子上标注的调整橙色圈中的跳线设置。
如果有CMSIS-DAP下载器,也可以用于该处理板,使用DAP下载器更方便调试。
这块板子虽然带有USB转串口的接口芯片,但是要想使用串口进行通信,需要注意将红色圈中的跳线加上(默认没有加,在包装袋中可以找到),并使用红色圈中的串口进行通信。
2、 硬件参数设置
硬件参数都可以通过FSP软件进行配置。
1) LED灯
从原理图中可以看出,LED1连接在P207管脚,而LED2连接在P113管脚。
点击FSP中的Pins标签,在其中找到对应的管脚,设置其Mode为Output mode。
2) 串口
该芯片共有2个串口(通道0和通道9)。根据原理图可以知道,USB转串口芯片是连接在该芯片的通道9,所以在FSP的Stacks标签中点击New Stack并从菜单中选择UART(如下图)。
接着在属性设置页面设置好通道编号,波特率这些基本参数。特别是要指定中断的回调函数user_uart_callback。
同时,要在Pins指明所使用的RXD和TXD管脚。
设置完成后点击Generate ProjectContent,生成相应的代码。
3、 Shell工具的移植
在Shell工具中,我们选择了国产的Letter Shell开源软件,它有很强的功能,上手也容易。不过我们没有选择其最新的3.2版本,而是用了2.0版本:https://github.com/NevermindZZT/letter-shell/tree/shell2.x。主要原因是其2.0版本的功能已经可以满足要求,而3.x的一些新特性不太需要而需要更多的资源。此外,其新版本存在一些bug。首先将其2.0版本的2个c文件和3个头文件加入到工程中。
1) 串口输出处理
串口输出部分比较简单,主要是调用R_SCI_UART_Write函数写入输出缓冲区,然后在中断函数中检测数据是否被发送完毕。
fsp_err_t err = FSP_SUCCESS;
volatile bool uart_send_complete_flag = false;
/* Callback function */
void user_uart_callback(uart_callback_args_t *p_args)
{
/* TODO: add your own code here */
if(p_args->event == UART_EVENT_TX_COMPLETE)
{
uart_send_complete_flag = true;
}
}
#ifdef __GNUC__ //?????
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
err = R_SCI_UART_Write(&g_uart9_ctrl, (uint8_t *)&ch, 1);
if(FSP_SUCCESS != err) __BKPT();
while(uart_send_complete_flag == false){}
uart_send_complete_flag = false;
return ch;
}
int _write(int fd,char *pBuffer,int size)
{
for(int i=0;i<size;i++)
{
__io_putchar(*pBuffer++);
}
return size;
}
复制代码我们还按照Letter Shell的要求,封装了一个SumpleWrite函数,
void simpleWrite(const char ch)
{
__io_putchar(ch);
}
复制代码在hal_entry函数中,对串口进行了初始化,创建了Shell对象,并指定其写函数为SimpleWrite。
void hal_entry(void)
{
/* TODO: add your own code here */
err = R_SCI_UART_Open(&g_uart9_ctrl, &g_uart9_cfg);
assert(FSP_SUCCESS == err);
shell.write = simpleWrite;
shellInit(&shell);
复制代码2)串口输入处理
串口输入部分要麻烦一些,由于工作机制不同,无法直接使用 R_SCI_UART_Read函数。按照Letter Shell的文档,应该在中断函数中收到用户输入时调用ShellInput。但是,由于ShellInput的代码过于复杂,不适合在中断中调用。我们采用了一个简单的环形队列,在中断函数中,将收到的数据写入环形队列,而在主循环中读出环形队列的数据,再调用ShellInput,实际效果不错。
下面是相关代码:
void user_uart_callback(uart_callback_args_t *p_args)
{
/* TODO: add your own code here */
if(p_args->event == UART_EVENT_RX_CHAR)
{
userChars[twrite] = (char)(p_args->data);
twrite++;
if(twrite >= RING_LEN)
twrite = 0;
}
复制代码……
while(1)
{
R_BSP_SoftwareDelay(100, BSP_DELAY_UNITS_MILLISECONDS); // NOLINT
while(tread != twrite)
{
shellInput(&shell, userChars[tread]);
tread++;
if(tread >= RING_LEN)
tread = 0;
}
}
复制代码3)自定义的Shell命令
我们自定义了一个led命令,该命令有2个参数:第一个参数是LED的编号,可以是1或者2;第二个参数是表示开关状态,ON为点亮LED灯,OFF为关灯。我们没有使用LetterShell的自动解析功能,因为我们的参数比较简单,采用自动解析反倒容易出错。为此需要修改shell_cfg.h中的SHELL_AUTO_PRASE为0.
函数代码比较简单:
int led(int argc, char *argv[])
{
int ret = 0;
if(argc < 3)
{
ret = -1;
}
else
{
bsp_io_level_t status;
bsp_io_port_pin_t id;
switch(argv[1][0])
{
case '1':
id = BSP_IO_PORT_02_PIN_07;
break;
case '2':
id = BSP_IO_PORT_01_PIN_13;
break;
default:
ret = -2;
break;
}
if(ret == 0)
{
if(strcasecmp(argv[2], "ON") == 0)
{
status = BSP_IO_LEVEL_HIGH;
}
else if(strcasecmp(argv[2], "OFF") == 0)
{
status = BSP_IO_LEVEL_LOW;
}
else
{
ret = -3;
}
if(ret == 0)
R_IOPORT_PinWrite(&g_ioport_ctrl, id, status);
}
}
if(ret)
shellDisplay(&shell, "Invalid parameters!\r\n");
return ret;
}
SHELL_EXPORT_CMD(led, led, turn on/off led. Example: led 2 on);
复制代码最后运行的结果如下图所示。