1. 前言
前言
很荣幸参加了这次瑞萨 CPK-RA6M4 的试用活动,感谢EEPW提供的这次宝贵机会。作为一名嵌入式开发爱好者,我一直对瑞萨的 MCU 产品线充满兴趣,这次能够亲身体验 CPK-RA6M4 的强大功能,我感到非常兴奋。
开箱
厚厚的泡沫板中间夹着CPK-RA6M4本体和两本书,可以说很细心了。
再此分享一点试用心得:
欢迎讨论。
2. 资料
2.1 RA产品家族
见
2.2 RA6M4 mcu
使用 200MHz Arm Cortex-M33 TrustZone 内核,并集成了以太网和 OctaSPI(用于扩展扩展闪存与RAM)。
2.3 CPK-RA6M4
是用于中国本地的评估板。
板载资源:
命名方法:
RA6M4 存储器映射:
2.4 FSP 简介
FSP 是瑞萨提供的用于MCU开发的固件库。全称为 “Flexible Software Package”,中文译为“灵活配置软件包”。
FSP 旨在以较低的内存占用量提供快速高效的驱动程序和协议栈。 FSP 集成了中间件协议栈、独立于 RTOS 的硬件抽象层(HAL)驱动程序, 以及最基础的板级支持包(BSP)驱动程序。FSP 还支持 FreeRTOS™ 实时操作系统(RTOS)。
2.5 资料汇总
文档:
原理图 自己去网站申请 【CPK-RA6M4 设计文件包】
教程
这个博主写了很多篇RA相关的教程,很有参考价值
4. hello world
点灯。
4.1 新建项目
新建一个RA C/C++ 项目 :
器件选择:R7FA6M4AF3CFB 144pins
next next 一通之后,(记得选 NO RTOS) …… 确定。
4.2 运行和调试
RA推荐的是J-Link,板子自带J-Link 很香。
点锤子开始编译,点调试就能进入调试模式:
如果需要配置,则点:
确保这里为J-Link ARM,点击【Debug】就可以进入调试模式。
运行时时钟问题 在运行时一直不会执行hal_entry() 函数,会停到这里 等待PLL稳定。
看看时钟配置
PLL时钟源来自外部晶振,然而,板子上的X1是没有焊接的
所以改一下,用片上晶振(HOCO)就可以了
点灯代码如下:
void hal_entry(void)
{
// 取消写保护
R_PMISC->PWPR = 0; ///< Clear BOWI bit - writing to PFSWE bit enabled
R_PMISC->PWPR = 1U << BSP_IO_PWPR_PFSWE_OFFSET;
// 配置LED引脚对应的PFS寄存器
R_PFS->PORT[BSP_IO_PORT_01_PIN_06>>8].PIN[BSP_IO_PORT_01_PIN_06 & 0xFF].PmnPFS =
IOPORT_CFG_PORT_DIRECTION_OUTPUT | IOPORT_CFG_PORT_OUTPUT_LOW;
while(1)
{
/* 翻转LED灯:LED1 */
R_PORT1->PODR ^= 1<<(BSP_IO_PORT_01_PIN_06 & 0xFF);
R_BSP_SoftwareDelay(1000, BSP_DELAY_UNITS_MILLISECONDS);
}
}
5. RTOS下检测按键和点灯
5.1 新建FreeRTOS项目
略,看文档中的配置。到选择系统那一步选择FreeRTOS即可。
5.2 使用FSP配置GPIO
led 连接的 P106 引脚,配置如下
在线程中增加代码:
void led_thread_entry(void *pvParameters)
{
FSP_PARAMETER_NOT_USED (pvParameters);
/* TODO: add your own code here */
while (1)
{
//vTaskDelay (1);
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_01_PIN_06, BSP_IO_LEVEL_LOW);
R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS); //延时1秒
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_01_PIN_06, BSP_IO_LEVEL_HIGH);
R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS); //延时1秒
}
}
5.3 使用按键控制led
#include "led_thread.h"
/* LED Thread entry function */
/* pvParameters contains TaskHandle_t */
void led_thread_entry(void *pvParameters)
{
FSP_PARAMETER_NOT_USED (pvParameters);
uint8_t led_level = BSP_IO_LEVEL_HIGH;
// 打开并启用 IRQ0
g_external_irq0.p_api->open(g_external_irq0.p_ctrl, g_external_irq0.p_cfg);
g_external_irq0.p_api->enable(g_external_irq0.p_ctrl);
/* 也可以用下面的方法
// Open ICU module
err = R_ICU_ExternalIrqOpen(&g_external_irq9_ctrl, &g_external_irq9_cfg);
// 允许中断
err = R_ICU_ExternalIrqEnable(&g_external_irq9_ctrl);
*/
while (1)
{
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_01_PIN_06, led_level);
if(led_level == BSP_IO_LEVEL_HIGH){
led_level = BSP_IO_LEVEL_LOW;
}else{
led_level = BSP_IO_LEVEL_HIGH;
}
xSemaphoreTake(g_s1_semaphore, portMAX_DELAY);
}
}
/* Callback function */
void external_irq0_callback(external_irq_callback_args_t *p_args)
{
FSP_PARAMETER_NOT_USED (p_args);
xSemaphoreGiveFromISR(g_s1_semaphore, NULL);
}
6. 串口打印
可以通过配置USB中间件来进行串口打印。(但是我没有配置成功) 也可以直接配置串口并且重定向print可参考: ,
6.1 配置USB外设
选择New Stack -> Connectivity -> USB PCDC
将添加4个模块到项目中:
用于全速USB的PCDC驱动 (r_usb_pcdc)
基本USB驱动 (r_usb_basic)
两个粉色的模块是,用于传输和接收数据的DMAC(直接内存访问控制器)
现在r_usb_basic 红色警告是因为UCLK(USB模块的时钟必须为48MHz),配置时钟为48MHz即可。
使用内部时钟配置锁相环。PLL2配置为240M,PLL最大为200M速率。
疑惑:
PLL也配好了,但是这里显示不支持。还有好几个设置不能更改。放弃!
6.2 配置串口
选择这两个引脚来做串口 P101(TX0) P410(RX0)
(为什么不把P101 和 P100 放一块呢,本来直接用这两个引脚最好)
增加串口模块
配置:
PIN配置:
重定向printf配置:
在 project -> c/c++ project settings 中 删掉配置(--specs=rdimon.specs)
原因是:如果在链接器脚本文件中使用了--specs=rdimon.specs参数,则编译器会使用rdimon.specs文件中的系统调用函数来实现printf函数。 在这种情况下,printf函数的输出会被重定向到一个固定的地址
串口代码:
#include "hal_data.h"
#include <stdio.h>
FSP_CPP_HEADER
void R_BSP_WarmStart(bsp_warm_start_event_t event);
FSP_CPP_FOOTER
// 发送回调函数
fsp_err_t err = FSP_SUCCESS;
volatile bool uart_send_complete_flag = false;
void uart0_callback (uart_callback_args_t * p_args)
{
if(p_args->event == UART_EVENT_TX_COMPLETE)
{
uart_send_complete_flag = true;
}
}
// printf 重定向
#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_uart0_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;
}
/*******************************************************************************************************************//**
* main() is generated by the RA Configuration editor and is used to generate threads if an RTOS is used. This function
* is called by main() when no RTOS is used.
**********************************************************************************************************************/
void hal_entry(void)
{
// 开启并初始化UART
err = R_SCI_UART_Open(&g_uart0_ctrl, &g_uart0_cfg);
assert(FSP_SUCCESS == err);
while(1){
printf("test");
//R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS);
}
}
7. I2C 驱动 OLED
7.1 IIC 配置
添加 IIC 模块
配置IIC:
7.2 I2C 回调函数
回调函数
i2c_master_event_t i2c_event ;
void IIC_master_callback(i2c_master_callback_args_t *p_args)
{
i2c_event = p_args->event;
if(I2C_MASTER_EVENT_RX_COMPLETE == p_args->event)
{
// 接收完成
// gI2CRxCplt = true;
}
else if(I2C_MASTER_EVENT_TX_COMPLETE == p_args->event)
{
// 发送完成
// gI2CTxCplt = true;
}
}
通过 I2C_MASTER_EVENT_TX_COMPLETE == p_args->event 来判断发送完成。
7.3 写数据和写命令
// IIC Write Command
void Write_IIC_Command(unsigned char IIC_Command)
{
uint8_t ii[2]={0x00,0x00};
ii[1] = IIC_Command;
err = R_IIC_MASTER_Write(&g_i2c_master0_ctrl, ii, 0x02, false);
assert(FSP_SUCCESS == err);
while ((I2C_MASTER_EVENT_TX_COMPLETE != i2c_event) && timeout_ms1>0)
{
R_BSP_SoftwareDelay(1U, BSP_DELAY_UNITS_MICROSECONDS);
timeout_ms1--;
}
if (I2C_MASTER_EVENT_ABORTED == i2c_event)
{
__BKPT(0);
}
i2c_event = I2C_MASTER_EVENT_ABORTED;
timeout_ms1 = 1000;
}
/**********************************************
// IIC Write Data
**********************************************/
void Write_IIC_Data(unsigned char IIC_Data)
{
uint8_t ii[2]={0x40,0x00};
ii[0] = 0x40;
ii[1] = IIC_Data;
err = R_IIC_MASTER_Write(&g_i2c_master0_ctrl, ii, 0x02, false);
assert(FSP_SUCCESS == err);
while ((I2C_MASTER_EVENT_TX_COMPLETE != i2c_event) && timeout_ms1>0){
R_BSP_SoftwareDelay(1U, BSP_DELAY_UNITS_MICROSECONDS);
timeout_ms1--;
}
if (I2C_MASTER_EVENT_ABORTED == i2c_event)
{
__BKPT(0);
}
i2c_event = I2C_MASTER_EVENT_ABORTED;
timeout_ms1 = 1000;
}
void OLED_WR_Byte(unsigned dat,unsigned cmd)
{
if(cmd){
Write_IIC_Data(dat);
}
else {
Write_IIC_Command(dat);
}
}
其他OLED方法封装一下即可。
8. 光强度采集
板子上有一个 ISL29035 感光模块,可以读取可见光强度。
参考:
8.1 I2C配置
原理图
设备地址为:0x44(0b1000100)
写数据
设备地址+操作地址+数据
// 写数据
void ISL29035_writeRegister( int reg_address, uint8_t val) {
uint8_t ii[2]={reg_address, val};
err_isl29035 = R_IIC_MASTER_Write(&g_i2c_isl29035_ctrl, ii, 0x02, false);
assert(FSP_SUCCESS == err_isl29035);
/* Since there is nothing else to do, block until Callback triggers*/
//while ((I2C_MASTER_EVENT_TX_COMPLETE != i2c_event) && timeout_ms)
while ((I2C_MASTER_EVENT_TX_COMPLETE != i2c_isl29035_event) && timeout_ms2>0)
{
R_BSP_SoftwareDelay(1U, BSP_DELAY_UNITS_MICROSECONDS);
timeout_ms2--;
}
if (I2C_MASTER_EVENT_ABORTED == i2c_isl29035_event)
{
__BKPT(0);
}
/* Read data back from the I2C slave */
i2c_isl29035_event = I2C_MASTER_EVENT_ABORTED;
timeout_ms2 = 500;
}
读数据 设备地址+操作地址+设备地址+数据读取
// 读数据
uint8_t DigitalLightISL29035_readRegister(uint8_t reg_address) {
uint8_t value;
err_isl29035 = R_IIC_MASTER_Write(&g_i2c_isl29035_ctrl, ®_address, 0x01, false);
assert(FSP_SUCCESS == err_isl29035);
/* Since there is nothing else to do, block until Callback triggers*/
while ((I2C_MASTER_EVENT_TX_COMPLETE != i2c_isl29035_event) && timeout_ms2>0)
{
R_BSP_SoftwareDelay(1U, BSP_DELAY_UNITS_MICROSECONDS);
timeout_ms2--;
}
if (I2C_MASTER_EVENT_ABORTED == i2c_isl29035_event)
{
__BKPT(0);
}
/* Read data back from the I2C slave */
i2c_isl29035_event = I2C_MASTER_EVENT_ABORTED;
timeout_ms2 = 500;
/* Read data from I2C slave */
err_isl29035 = R_IIC_MASTER_Read(&g_i2c_isl29035_ctrl, &value, 0x01, false);
assert(FSP_SUCCESS == err_isl29035);
while ((I2C_MASTER_EVENT_RX_COMPLETE != i2c_isl29035_event) && timeout_ms2)
{
R_BSP_SoftwareDelay(1U, BSP_DELAY_UNITS_MILLISECONDS);
timeout_ms2--;
}
if (I2C_MASTER_EVENT_ABORTED == i2c_isl29035_event)
{
__BKPT(0);
}
i2c_isl29035_event = I2C_MASTER_EVENT_ABORTED;
timeout_ms2 = 500;
return value;
}
8.2 初始化和读取数据
int DigitalLightISL29035_init(void) {
uint8_t reg = 0;
// 读取设备ID
reg = ISL29035_readRegister(CHIP_ID);//CHIP_ID->0x0f
uint8_t chip_id = (reg >> 3) & 0x7;
if (chip_id != 0x5) {
return -1;
}
//清除BOUT位
ISL29035_writeRegister(CHIP_ID, reg & 0x7f);//CHIP_ID->0x0f
//确保芯片处于停止模式
ISL29035_writeRegister( COMMAND_I, 0);//COMMAND_I->0x00
//设置分辨率
ISL29035_writeRegister(COMMAND_II, 0x06 );//COMMAND_2->0x01=0x06 = 0b00000110 adc分辨率01为12位 范围10=16000,
// 范围和分辨率的时间
full_scale_lux_range = 2; // 1000 4000 16000 64000
integration_time = 1; // 16 12 8 4
//设置为单次模式
ISL29035_writeRegister( COMMAND_I, OPMODE_ALS_ONCE);//COMMAND_I->0x00 = 0b00100000 010 周期内仅测量一次
return 0;
}
uint32_t DigitalLightISL29035_readIRLux(void) {
uint16_t data = 0;
uint8_t l, h;
//设置为单次模式
ISL29035_writeRegister( COMMAND_I, OPMODE_ALS_ONCE);
//等待时间
if(integration_time==0)
{
R_BSP_SoftwareDelay(105, BSP_DELAY_UNITS_MILLISECONDS);
}
else if(integration_time==1 || integration_time==2)
{
R_BSP_SoftwareDelay(7, BSP_DELAY_UNITS_MILLISECONDS);
}
else if(integration_time==3)
{
R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS);
}
l=ISL29035_readRegister(DATA_L);//DATA_L->0x02
h=ISL29035_readRegister(DATA_H);//DATA_H->0x03
data=(h << 8) | l;
uint32_t ranges=0;
if(full_scale_lux_range==0)
ranges=1000;
else if(full_scale_lux_range==1)
ranges=4000;
else if(full_scale_lux_range==2)
ranges=16000;
else if(full_scale_lux_range==3)
ranges=64000;
uint32_t adc_count_max=0;
if(integration_time==0)
adc_count_max=65536;
else if(integration_time==1)
adc_count_max=4096;
else if(integration_time==2)
adc_count_max=256;
else if(integration_time==3)
adc_count_max=16;
return ranges * (uint32_t)data /adc_count_max;
}
8.3 显示数据到OLED
void hal_entry(void)
{
char msg[100] = {0,};
// oled i2c 初始化
fsp_err_t err = R_IIC_MASTER_Open(&g_i2c_master0_ctrl, &g_i2c_master0_cfg);
assert(FSP_SUCCESS==err);
// ISL29035 i2c 初始化
err = R_IIC_MASTER_Open(&g_i2c_isl29035_ctrl, &g_i2c_isl29035_cfg);
assert(FSP_SUCCESS==err);
// oled
OLED_Init();
OLED_Clear();
OLED_ShowString(2,0,"hello,", 16);
OLED_ShowZh(2+6*8, 0, 0); // 瑞 6*8为上面字符宽度
OLED_ShowZh(2+6*8+16+2, 0, 1); // 萨 +16(上个汉字宽度)+2(间隙)
// 恭喜发财
OLED_ShowZh(2, 13, 2);
OLED_ShowZh(2+(2+16)*1, 13, 3);
OLED_ShowZh(2+(2+16)*2, 13, 4);
OLED_ShowZh(2+(2+16)*3, 13, 5);
// 光电传感器
DigitalLightISL29035_init();
while(1){
lux = DigitalLightISL29035_readIRLux();
sprintf(msg, "lux=%04d ", lux);
OLED_ShowString(20,10, msg, 16);
R_BSP_SoftwareDelay(1000, BSP_DELAY_UNITS_MILLISECONDS);
}
光强度:
9. 动态显示福
大过年的,显示个福,旋转的福。福到了。
参考:
素材
拆分: 在这个在线网站处理 https://ezgif.com/split 。
图很多,只要最左侧一列。后面发现边框好看但是转换后基本没有了,所以只取中间。这个软件也可以裁剪
取其中8张。
使用Image2Lcd转换
将图片保存为 x.bmp
使用 PCtoLCD 软件转换
注:如果生成的数据有大括号记得去掉。
绘制图片:
const unsigned char gImage_a1[128] = {
0x00,0x00,0x00,0x00,0x80,0xC0,0xE0,0xF0,0xF8,0x38,0x3C,0x7C,0xFC,0xFC,0x7E,0x7E
,0x7E,0x7C,0x7C,0x7C,0x7C,0x7C,0x78,0x78,0xF0,0xE0,0xC0,0x80,0x00,0x00,0x00,0x00
,0x00,0xE0,0xFC,0xFF,0xFF,0xFF,0xF1,0xF3,0x31,0x11,0x00,0x41,0x71,0xFF,0xFE,0x84
,0x06,0x86,0xB6,0x96,0x92,0x82,0x82,0x82,0xF7,0xFF,0xFF,0xFF,0xFE,0xFC,0x00,0x00
,0x00,0x03,0x3F,0xFF,0xFF,0xF7,0xF3,0xF8,0x08,0x00,0x00,0xFC,0xF8,0xF8,0x00,0x01
,0x01,0xCD,0x01,0x01,0x40,0x24,0x00,0x00,0x80,0xFF,0xFF,0xFF,0x7F,0x1F,0x00,0x00
,0x00,0x00,0x00,0x00,0x01,0x03,0x07,0x0F,0x1E,0x1C,0x3C,0x3F,0x3F,0x3F,0x3E,0x3C
,0x3E,0x3E,0x3E,0x3E,0x3E,0x1E,0x1C,0x0C,0x07,0x07,0x03,0x00,0x00,0x20,0x00,0x00
};
void OLED_BMP(unsigned char x0, unsigned char y0, unsigned char x1, unsigned char y1, unsigned char BMP[])
{
unsigned int j=0;
unsigned char x,y;
if(y1%8==0)
{
y=y1/8;
}
else
y=y1/8+1;
for(y=y0;y<y1;y++)
{
OLED_Set_Pos(x0,y);
for(x=x0;x<x1;x++)
{
OLED_WR_Byte(BMP[j++],OLED_DATA);
}
}
}
// 绘制,图片尺寸 32*32
OLED_BMP(0,0,32,4, gImage_a1);
再简单修改一下,用循环把这几张图跑起来就成动态的“福”了,效果:
有奖活动 | |
---|---|
【有奖活动】分享技术经验,兑换京东卡 | |
话不多说,快进群! | |
请大声喊出:我要开发板! | |
【有奖活动】EEPW网站征稿正在进行时,欢迎踊跃投稿啦 | |
奖!发布技术笔记,技术评测贴换取您心仪的礼品 | |
打赏了!打赏了!打赏了! |
打赏帖 | |
---|---|
基于MSPM0L1306的MODBUS-RTU协议通讯实验被打赏50分 | |
每周了解几个硬件知识+485硬件知识分享被打赏10分 | |
【换取手持示波器】树莓派PICOW网页烟花被打赏18分 | |
【换取手持示波器】树莓派PICO逻辑分析仪被打赏16分 | |
【分享评测,赢取加热台】教你五分钟本地部署蒸馏版本的DeepSeekR1-7B被打赏12分 | |
【换取手持示波器】蓝牙音箱更换电池被打赏8分 | |
【换取手持示波器】鼠标更换微动开关被打赏10分 | |
【我要开发板】4.传感器移植被打赏50分 | |
【换取手持数字示波器】RISC-VGCC使用-fno-omit-frame-pointer编译选项实现栈回溯被打赏29分 | |
【分享评测,赢取加热台】+开关电源简述被打赏7分 |