芯查查logo
  • 物料选型
  • 数据服务
    1. 新品推荐
    2. 查替代料
    3. 丝印反查
    4. 查品牌
    5. PCN/PDN
    6. 查代理
    7. 数据合作
  • 应用方案
  • SaaS产品
      SaaS产品
    1. 供应链波动监测预警
    2. 半导体产业链地图
    3. 智能BOM管家
    4. 解决方案
    5. 汽车电子
    6. 政府机构
    7. 数据API
  • 商城
  • 行业资讯
    1. 资讯
    2. 直播
  • 论坛社区
    1. 论坛
    2. 学习
    3. 测评中心
    4. 活动中心
    5. 积分商城
  • 查一下
正在使用账号登录iCEasy商城
从前的电阻Mia
RA4L1-SENSOR(02)使用段式LCD(段码管)

RA4L1设计有段式液晶屏的硬件驱动 SLCDC(Segment LCD Controller),为驱动段式液晶显示器(如常见的数码管、字符段码屏等)设计,可适配不同分辨率的段式屏,并通过内部寄存器直接管理显示数据的刷新,通过硬件逻辑实现对各显示段(Segment)和公共电极(Common)的驱动控制。


一、驱动原理

开发板配套的LCD段码屏管脚定义如下:COMn是公共端口,pin1~13控制显示的段,可以看作表格的行和列,当pin1~13某一个管脚,与COM1~4某一个管脚形成电势差时(有相对压差),其对应的LCD段就会显示出对应的图形;具体某个管脚对应的位置可以查表得到,驱动代码的结构在后续段落详解。

 

● SEG(Segment):控制 LCD 具体显示的段,如数码管的 A-G 段。
● COM(Common):LCD 的 公共信号,决定哪个段被驱动,多 COM 则允许减少 I/O 引脚数量。

 

在这里插入图片描述

由开发板原理图可得,LCD屏幕映射到板上对应的SLCDC外设管脚如下:

比如要在第一位显示0,我们需要点亮的是1A,1B,1C,1D,1E,1F六个位,查表得以下结果:

我们应该将SEG3与COM0,COM2,COM3组合,SEG11与COM0,COM2,COM3组合,即可在第一位显示0。具体代码在后续段落讲解。

 

二、RASC配置

新建文件的方法不再赘述,我们仍然使用Non-Trust Zone,No RTOS的工程模板。

 

首先更改时钟树外部晶振频率,设置为8MHz:
Stacks→New Stack→Graphics→Segment LCD(r_slcdc) 添加新配置。


Stack→Properties 设置

 

切换到Pins栏,找到Peripherals→HMI:SLCDC,进行管脚配置,
Segment LCD Pins 管脚配置如下:

 

三、代码编写

 

查看手册:

按以下方式打开RA4L1用户手册:

Modules  →  Graphics → Segment LCD (r_slcdc)

或者直接搜索slcdc。

FSP库下驱动SLCDC模块用到的函数有:

以下是FSP用户手册查找到的函数声明和注释,可供参考:


1.R_SLCDC_Open():

打开SLCDC驱动的函数,确保 SLCDC 可以开始正常运行。运行后还需要执行 R_SLCDC_Start() ,SLCDC才会开始运行。

2.R_SLCDC_Start():

开始输出LCD信号。

3.R_SLCDC_Stop():

停止输出LCD信号。

4.R_SLCDC_Write():

将显示数据序列写入段数据寄存器,是一个批量写入连续段数据的 API,用于高效更新段式 LCD 的多个连续段显示内容。

  • start_segment:指定起始段寄存器的编号,即从哪个段开始写入数据。
  • p_data:指向uint8_t类型的数据缓冲区指针,存储了要写入段寄存器的一系列数据,不能为空。
  • segment_count:指定要连续写入的段数量,即从start_segment开始,一共写入多少个段的内容。

 

5.R_SLCDC_Modify():

基于掩码和所需数据修改单个段寄存器。

  • data:要写入到段寄存器的目标数据(即希望该段寄存器最终呈现的值)。
  • data_mask:数据掩码,用于控制 “哪些位需要被修改”。掩码的作用是:只有当 data_mask中某一位为1时,data中对应位的值才会写入寄存器;若data_mask中某一位为 0,则寄存器中该位的值保持不变。

 

6.R_SLCDC_Close():

关闭SLCDC驱动程序。


(代码示例1)使用R_SLCDC_Write在第一位显示数字1

因为显示1比显示0简单,我们首先编写代码来在第一位显示1:

首先,查表得到要使LCD的第一位显示1,我们需要对SEG11的COM1、COM2两位置为1,即SEG11的值为0b0110,转化为十六进制为0x06:

编写代码如下:

首先使用R_SLCDC_Open打开SLCDC模块:

 err = R_SLCDC_Open(&g_slcdc0_ctrl, &g_slcdc0_cfg);
  assert(FSP_SUCCESS == err);

然后使用R_SLCDC_Start启用SLCDC的数据传输:

  err = R_SLCDC_Start(&g_slcdc0_ctrl);
  assert(FSP_SUCCESS == err);

再然后使用R_SLCDC_Write对SEG11的值进行写入,函数定义如下:

fsp_err_t R_SLCDC_Write(slcdc_ctrl_t * const p_ctrl,
                       uint8_t const        start_segment, //开始段编号,比如SEG11则填写11
                       uint8_t const      * p_data,                //写入数据的指针
                       uint8_t const        segment_count);

uint8_t segment_data_num1[]={0x06};
  R_SLCDC_Write(&g_slcdc0_ctrl, 11, segment_data_num1, sizeof(segment_data_num1));

注意我们要操作的是SEG11,所以R_SLCDC_Write的第二个参数 start_segment我们填入11;第三个参数是一个uint8_t*类型的指针,所以我们必须填入的是地址或者数组名称,故初始化了一个数组用于存储显示num1的值。

在hal_entry中写入上述代码,完整代码如下:

void hal_entry(void)
{
    /* TODO: add your own code here */
  fsp_err_t err;
  err = R_SLCDC_Open(&g_slcdc0_ctrl, &g_slcdc0_cfg);
  assert(FSP_SUCCESS == err);
  R_BSP_SoftwareDelay(5,BSP_DELAY_UNITS_MILLISECONDS);
  
  err = R_SLCDC_Start(&g_slcdc0_ctrl);
  assert(FSP_SUCCESS == err);
  
  uint8_t segment_data_num1[]={0x06};
 
  R_SLCDC_Write(&g_slcdc0_ctrl, 11, segment_data_num1, sizeof(segment_data_num1));
  R_BSP_SoftwareDelay(1000,BSP_DELAY_UNITS_MILLISECONDS);
  
  
#if BSP_TZ_SECURE_BUILD
    /* Enter non-secure code */
    R_BSP_NonSecureEnter();
#endif
}

(代码示例2)使用R_SLCDC_Write在第二位显示数字2

查表得,要在第二位显示数字2,应该把SEG15与COM1,COM2,COM3组合,SEG16与COM0,COM1组合,因此定义数组:

uint8_t segment_data_num2[]={0x0E, 0x03};

并在第15位起调用 R_SLCDC_Write写入此数组,来显示这两位数,即

 R_SLCDC_Write(&g_slcdc0_ctrl, 15, segment_data_num2, sizeof(segment_data_num2));

完整的 hal_entry函数如下:

void hal_entry(void)
{
   /* TODO: add your own code here */
 fsp_err_t err;
 err = R_SLCDC_Open(&g_slcdc0_ctrl, &g_slcdc0_cfg);
 assert(FSP_SUCCESS == err);
 R_BSP_SoftwareDelay(5,BSP_DELAY_UNITS_MILLISECONDS);
 
 err = R_SLCDC_Start(&g_slcdc0_ctrl);
 assert(FSP_SUCCESS == err);

 uint8_t segment_data_num1[]={0x06};
 uint8_t segment_data_num2[]={0x0E, 0x03};
 R_SLCDC_Write(&g_slcdc0_ctrl, 11, segment_data_num1, 1);
 R_SLCDC_Write(&g_slcdc0_ctrl, 15, segment_data_num2, sizeof(segment_data_num2));
 R_BSP_SoftwareDelay(1000,BSP_DELAY_UNITS_MILLISECONDS);    
#if BSP_TZ_SECURE_BUILD
   /* Enter non-secure code */
   R_BSP_NonSecureEnter();
#endif
}

(代码示例3)使用R_SLCDC_Modify在第一位交替显示数字1和0

R_SLCDC_Modify函数定义如下:

fsp_err_t R_SLCDC_Modify(slcdc_ctrl_t * const p_ctrl,
                        uint8_t const        segment_number,  //单个段编号,比如SEG11则填写11
                        uint8_t const        data,  //写入的数据
                        uint8_t const        data_mask); //掩码,比如0xFF则完全覆盖寄存器的数据,0x00则保留数据,叠加本次数据

我们已经知道要在第一位显示数字0,必须对SEG3和SEG11两个段的寄存器分别写入数据,如果使用R_SLCDC_Write,预先定义的数组需要从第3位开始,写到第11位,即

uint8_t segment_data_num0[]={0x0D,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07};

非常冗长,所以我们可以使用R_SLCDC_Modify函数直接操作单个位,跳过中间无启用的寄存器。

 

在示例1代码的基础上,我们交替对SEG3和SEG11寄存器进行操作,实现在0和1之间切换的效果:

uint8_t segment_data_num1[]={0x06};
  while(1){
  //第一位显示1
  R_SLCDC_Modify(&g_slcdc0_ctrl,3,0x00,0xFF);
  R_SLCDC_Write(&g_slcdc0_ctrl, 11, segment_data_num1, 1);

  R_BSP_SoftwareDelay(1000,BSP_DELAY_UNITS_MILLISECONDS);
    //第二位显示0
  R_SLCDC_Modify(&g_slcdc0_ctrl,3,0x0D,0xFF);
  R_SLCDC_Modify(&g_slcdc0_ctrl,11,0x07,0xFF);
  R_BSP_SoftwareDelay(1000,BSP_DELAY_UNITS_MILLISECONDS);
  }

完整hal_entry实现如下:

void hal_entry(void)
{
    /* TODO: add your own code here */
  fsp_err_t err;
  err = R_SLCDC_Open(&g_slcdc0_ctrl, &g_slcdc0_cfg);
  assert(FSP_SUCCESS == err);
  R_BSP_SoftwareDelay(5,BSP_DELAY_UNITS_MILLISECONDS);
  
  err = R_SLCDC_Start(&g_slcdc0_ctrl);
  assert(FSP_SUCCESS == err);
 
  uint8_t segment_data_num1[]={0x06};
  while(1){
  R_SLCDC_Modify(&g_slcdc0_ctrl,3,0x00,0xFF);
  R_SLCDC_Write(&g_slcdc0_ctrl, 11, segment_data_num1, 1);

  R_BSP_SoftwareDelay(1000,BSP_DELAY_UNITS_MILLISECONDS);
    
  R_SLCDC_Modify(&g_slcdc0_ctrl,3,0x0D,0xFF);
  R_SLCDC_Modify(&g_slcdc0_ctrl,11,0x07,0xFF);
  R_BSP_SoftwareDelay(1000,BSP_DELAY_UNITS_MILLISECONDS);
  }
#if BSP_TZ_SECURE_BUILD
    /* Enter non-secure code */
    R_BSP_NonSecureEnter();
#endif
}

(代码示例4)段码LCD显示串口接收的数据

1.查表函数的实现

我们可以发现此LCD屏幕每一位均对应两个SEG管脚,并且每个段相对于COMn管脚的映射是一定的(比如A段一定对应COM0,并且出现在第二列),因此我们可以先用二维数组表示0到9对应的COM的值,分成两列,每一列对应其中一个SEG管脚,每个值是COM3-0组成的四位二进制数:

uint8_t Seg_num[10][2]={
                       {0x0D,0x07},//0
                        {0x00,0x06},//1
                        {0x0E,0x03},//2
                        {0x0A,0x07},//3
                        {0x03,0x06},//4
                        {0x0B,0x05},//5
                        {0x0F,0x05},//6
                        {0x00,0x07},//7
                        {0x0F,0x07},//8
                        {0x0B,0x07},//9
                        };

封装为查询函数,可以方便直接获取某个数字的值,函数如下:

uint8_t* get_seg_num(uint8_t num) {
   if (num < 1 || num > 9) {
       return NULL; // 无效输入返回空指针
   }
   return Seg_num[num]; // 返回Seg_num[num]的首地址
}

同样的,我们可以用二维数组表示六个位,分别对应的SEGn引脚的标号值:

uint8_t Seg_pos[6][2]={
                       {03,11},//1p
                       {15,16},//2p
                       {22,23},//3p
                       {24,29},//4p
                       {30,39},//5p
                       {40,41},//6p
                     };

查询函数如下,因为我希望数码管位数的编号从1开始,而数组的编号是从0开始,所以最后的返回值进行了减1操作:

uint8_t* get_seg_pos(uint8_t pos){
    if (pos < 1 || pos > 6) {
        return NULL; // 无效输入返回空指针
    }
    return Seg_pos[pos-1]; // 返回Seg_num[num]的首地址
} 

要查询相关数据用于显示,可以直接调用上面两个函数;于是我们可以编写一个程序,从左到右依次实现从0到9的显示,并且当刷新到显示屏最后一位(第六位)时,重新从第一位开始显示:

int i = 0;
int j = 1;
 while(1){
 /*每一次循环,显示的数字+1,显示的位数+1*/
  for(i=0;i<10;i++){
  R_SLCDC_Modify(&g_slcdc0_ctrl,get_seg_pos(j)[0],get_seg_num(i)[0],0xFF);
  R_SLCDC_Modify(&g_slcdc0_ctrl,get_seg_pos(j)[1],get_seg_num(i)[1],0xFF);
  R_BSP_SoftwareDelay(1000,BSP_DELAY_UNITS_MILLISECONDS);    
  j=(j%6)+1; //光标,每次显示的位置都加1,并且大于6时重新设置为1
  }
  i=0;
  }

2.串口接收及显示

我们现在可以灵活控制在段码LCD的任意位上显示任意不同数字,接下来的代码则是通过串口输入任意数字,并在LCD上显示出来。

①串口配置

首先在Stacks→New stack→Connectivity→UART(r_sci_uart)加载UART模块:

串口配置如下:

设置BSP → Property → RA Common → Heap size为0x2000,保证串口缓冲区大小:

 

②串口接收实现

  • 宏定义和全局变量声明
#define TRANSFER_LENGTH 6 //接受6个字符

uint8_t i;
uint8_t j=1;
uint8_t  g_out_of_band_received[TRANSFER_LENGTH];
volatile bool g_receive_complete  = false;
uint32_t g_out_of_band_index = 0;
uint8_t num_array[6] = {0};

TRANSFER_LENGTH: 定义接收数据长度为6个字符

g_out_of_band_received: 存储原始接收的字符数据

num_array: 存储转换后的数字值

g_receive_complete: 标记是否完成接收(volatile确保多线程可见性)

g_out_of_band_index: 记录当前接收的字符位置

 

2. UART回调函数实现

以下函数必须是UART的数据位是8bits才能直接使用,如果设置的是9bits的话,需要用两个字节进行处理:

void USER_UART_Callback(uart_callback_args_t * p_args)
{
    switch (p_args->event)
    {
        case UART_EVENT_RX_CHAR: //接收到一个字节则开始以下工作
            if (sizeof(g_out_of_band_received) > g_out_of_band_index)
            {
                uint8_t received_char = (uint8_t)p_args->data;
                
                // 只接收数字字符,过滤非数字字符
                if (received_char >= '0' && received_char <= '9')
                {
                    g_out_of_band_received[g_out_of_band_index++] = received_char;
                }
        
                // 收到足够字符(6个)后设置完成标志
                if (g_out_of_band_index >= TRANSFER_LENGTH)
                {
                    g_receive_complete = 1;
                }
            }
            break;
        default:
            break;
    }
}

③接收数据到显示

  • 硬件初始化:打开SLCDC控制器→打开UART9→启动SLCDC显示
void hal_entry(void)
{
    fsp_err_t err;
    // SLCDC液晶显示控制器初始化
    err = R_SLCDC_Open(&g_slcdc0_ctrl, &g_slcdc0_cfg);
    assert(FSP_SUCCESS == err);
    R_BSP_SoftwareDelay(5,BSP_DELAY_UNITS_MILLISECONDS);
    
    // UART串口初始化
    err = R_SCI_UART_Open(&g_uart9_ctrl, &g_uart9_cfg);
    assert(FSP_SUCCESS == err);
    
    // 启动SLCDC显示
    err = R_SLCDC_Start(&g_slcdc0_ctrl);
    assert(FSP_SUCCESS == err);

显示初始化:使用get_seg_pos(j)获取第j个显示位置的段码地址,使用get_seg_num(num_array[j-1])获取对应数字的段码模式,初始显示全0(因为num_array初始化为全0)

    // 初始显示设置:显示num_array的初始值,最开始num_array全0
    for(j=1;j<=6;j++){
        R_SLCDC_Modify(&g_slcdc0_ctrl,get_seg_pos(j)[0],get_seg_num(num_array[j-1])[0],0xFF);
        R_SLCDC_Modify(&g_slcdc0_ctrl,get_seg_pos(j)[1],get_seg_num(num_array[j-1])[1],0xFF);
    }
  • 主循环数据处理

 检查是否完成6个字符的接收→ 

 if (g_receive_complete){

将ASCII字符('0'-'9')转换为数值(0-9)→ 

( 串口接受到的数据是字符串格式,需要转换为数字作为数组下标,可以用传输的数据减去字符'0',比如'1'-'0'得到的就是1)

    for (i = 0; i < TRANSFER_LENGTH; i++)       
     {            
     num_array[i] = g_out_of_band_received[i] - '0';        
     }

将转换后的数字显示到对应的6个LCD位置→

for(j=1;j<=TRANSFER_LENGTH;j++){           
 R_SLCDC_Modify(&g_slcdc0_ctrl,get_seg_pos(j)[0],get_seg_num(num_array[j-1])[0],0xFF);            R_SLCDC_Modify(&g_slcdc0_ctrl,get_seg_pos(j)[1],get_seg_num(num_array[j-1])[1],0xFF);       
  }

清除完成标志和索引,准备下次接收。

g_receive_complete = false;        
g_out_of_band_index = 0;

完整的while循环如下:

while(1){
    if (g_receive_complete){
        // 字符到数字转换
        for (i = 0; i < TRANSFER_LENGTH; i++)
        {
            num_array[i] = g_out_of_band_received[i] - '0';
        }
        
        // 更新LCD显示
        for(j=1;j<=TRANSFER_LENGTH;j++){
            R_SLCDC_Modify(&g_slcdc0_ctrl,get_seg_pos(j)[0],get_seg_num(num_array[j-1])[0],0xFF);
            R_SLCDC_Modify(&g_slcdc0_ctrl,get_seg_pos(j)[1],get_seg_num(num_array[j-1])[1],0xFF);
        }
        
        // 重置接收状态
        g_receive_complete = false;
        g_out_of_band_index = 0;
    }
    R_BSP_SoftwareDelay(10, BSP_DELAY_UNITS_MILLISECONDS);
}

代码放在附件中,另附一份LCD操作手册。

效果展示(拿相机拍手有点抖):

当发送的数据累计达到6位时显示在LCD上。


测评总结

RA4L1的SLCDC模块在低功耗显示应用中表现出色。其硬件自动刷新机制显著降低CPU占用率,只需要对寄存器进行操作,无需软件干预即可维持显示,极大提升了系统能效。驱动能力强劲,可直接控制多段LCD,简化外围电路设计。配合RA4L1的低功耗特性,特别适合电池供电的仪表、家电等长期运行设备。并且SLCDC显示效果稳定无闪烁,刷新效率远超软件模拟方案,是低功耗人机交互应用的理想选择。

工程附件
FSP_RA4L1_Segment.zip
r01an7730eu0100-segment-lcd-application-ra4l1.pdf
开源社区
版块: 开发板测评与实战
2025/11/09 21:39
  • 举报
😁😂😃😄😅😆😉😊😋😌😍😏😒😓😔😖😘😚😜😝😞😠😡😢😣😤😥😨😩😪😫😭😰😱😲😳😵😷😸😹😺😻😼😽😾😿🙀🙅🙆🙇🙈🙉🙊🙋🙌🙍🙎🙏✂✅✈✉✊✋✌✏✒✔✖✨✳✴❄❇❌❎❓❔❕❗❤➕➖➗➡➰🚀🚃🚄🚅🚇🚉🚌🚏🚑🚒🚓🚕🚗🚙🚚🚢🚤🚥🚧🚨🚩🚪🚫🚬🚭🚲🚶🚹🚺🚻🚼🚽🚾🛀Ⓜ🅰🅱🅾🅿🆎🆑🆒🆓🆔🆕
@好友

全部评论

加载中
游客登录通知
已选择 0 人
自定义圈子
移动
发布帖子
发布动态
发布问答
最新帖子
学而思可多编程掌机改装2PCB设计翻车实录:我踩过的5个大坑,每个都值一万块从0到1学PCB:硬件工程师Layout实战避坑指南从0到1做硬件:智能硬件产品经理全流程实战指南《图解功率半导体》书评
热门版块
查看更多
萤火工场
电子元器件
单片机/MCU论坛
问型号
飞腾
开源硬件项目
每日打卡
电子DIY
维修技术交流
抄图联盟

34

收藏

分享

微信扫码
分享给好友

评论