Menu Close

RISC-V C语言编程2(1)数码管工程

数码管工程主要使用RISC-V CPU V2.03 及以后版本。数码管工程可在文章末尾点击下载。

1.原理图分析

 

相关参考文章:

RISC-V教学教案

 

数码管本工程实验中会被使用(数码管原理及显示译码的详细介绍,点击这里),对原理图的分析有助于理解C语言工程里的相关变量赋值,如下图所示。

数码管显示有两种类型的FPGA接线:位选和段选。

  • 由于位选连接到P沟道场效应晶体管(FET),因此栅极必须比源极小典型值0.78V ,以便VCC可以流经数码管。 因此,要点亮数码管,位选必须处于低电平状态。
  • 同样,如果段选也处于低压状态(假设相应的位选处于低压状态,则VCC可以流经数码管),那么段选和VCC之间存在电压差,VCC可以从数码管的右侧降压到左侧。 相反,如果段选择为高电压,则数码管的左右两侧之间没有电压差,数码管也因此没有显示。

%title插图%num

图1 数码管原理图接线

2.地址图

图2截取了部分RISC-V地址分配(完整地址图点击这里),主要用于编写C语言工程时的地址赋值查询。

 

%title插图%num

图2 地址分配

 

3.RISCV_seg_cnt工程

 

如图3所示,首先,在Freedom Studio下选择File> Import,导入窗口将弹出。 在General 页面下,选择Existing Projects into Workspace,然后单击Next。 在图4中,点击Browse浏览找到相应的工程,勾选Copy projects into workspace项,然后点击Finish完成。 RISCV_seg_cnt工程会出现在Project Explorer选项下,如图5所示。

%title插图%num

图3 Import窗口(1)

%title插图%num

图4 Import窗口(2)

%title插图%num

图5 RISCV_seg_cnt工程在Project Explorer选项下出现

将项目添加到工作区后,右键点击Project Explorer下的工程文件夹,点击Clean Project(每次在实际构建项目之前都尽量执行此操作)。 然后点击Build Project。 控制台Console显示Build Finished后,点击Refresh刷新界面,如图6和图7所示。调试需要右键点击该项目,然后选择Debug As> Debug Configurations

 

%title插图%num

图6 Clean、build、refresh工程

%title插图%num

图7 调试

%title插图%num

图8 调试配置

在图8所示的弹出窗口中,按照C语言编程1所述设置调试配置。在GDB OpenOCD Debugging下,通过点击左上角的图标添加新的启动配置。 通常在Build项目和Refresh后,C / C ++ Application栏将自动填充相应的* .elf文件。 如果没有,点击下面的Search Project来搜索。 否则使用Browse,在调试文件夹下浏览搜索(绝对路径)。 完成所有这些操作后,单击ApplyDebug。 可以观察到数码管持续计数的实验现象,如下所示:

 

接下来,将在每个代码块中注释点亮数码管软件代码的详细说明。 如下:

  • Platform.h
#ifndef __PLATFORM_H 

#define __PLATFORM_H

    #ifdef __cplusplus

     extern "C" {

    #endif 

    #include "fii_types.h"           //数据类型定义
    #include "fii_gpio.h"            //gpio定义
    #include "fii_uart.h"            //uart定义
    #include "fii_irq.h"             //中断定义
    #include "encoding.h"            //参数定义

    #define RAM_ADDR   0x90000000    //定义DTCM地址

    

    //定义访问地址的便捷方式
    #define  GPIO_REG(offset)   (*(volatile unsigned int *)(GPIO_ADDR + offset))
    #define  RAM_REG(offset)    (*(volatile unsigned int *)(RAM_ADDR + offset))
    #define  UART_REG(offset)   (*(volatile unsigned int *)(UART_BASE + offset))
    #define  TIME_REG(offset)   (*(volatile unsigned int *)(TIME_ADDR + offset))

    #ifdef __cplusplus

    }

    #endif 

#endif  // __PLATFORM_H

 

  • Fii_types.h
/*

C 数据类型     字节大小(RV32)   字节大小(RV64)

C type      | Bytes in RV32  |  Bytes in RV64

char        |       1        |        1
short       |       2        |        2
int         |       4        |        4
long        |       4        |        8
long long   |       8        |        8
void*       |       4        |        8
float       |       4        |        4
double      |       8        |        8
long double |       16       |        16

*/




//声明数据类型, 旨在消除与他人合作时的代码冲突

#ifndef __FII_TYPES_H 

#define __FII_TYPES_H

    #ifdef __cplusplus

     extern "C" {

    #endif 

    

    #define  RV_TYPE  RV32            //定义RISCV CPU是32位

    typedef unsigned int        uintptr_t;
    typedef unsigned long long  u64_t;
    typedef unsigned int        u32_t;
    typedef unsigned short      u16_t;
    typedef unsigned char       u8_t;
    typedef long long           s64_t;
    typedef int                 s32_t;
    typedef short               s16_t;
    typedef char                s8_t;

#if 0
    #if (RV_TYPE == RV32)
        typedef unsigned long       u32_t;
        typedef long                s32_t;
    #elif (RV_TYPE == RV64)
        typedef unsigned long       u64_t;
        typedef long                s64_t;
    #endif
#endif

    #ifdef __cplusplus

    }

    #endif 

#endif  // __FII_TYPES_H

 

  • Fii_gpio.h
#ifndef __FII_GPIO_H

#define __FII_GPIO_H




#ifdef __cplusplus

 extern "C" {

#endif 

//声明数码管字模
//实际的赋值与RISCV GPIO定义有关
//示例:
//SEG_C:0X39
//DP G F E D C B A
//0  0 1 1 1 0 0 1

#define  SEG_S   0x6D
#define  SEG_A   0x77              
#define  SEG_B   0x7C              
#define  SEG_C   0x39              
#define  SEG_D   0x5E              
#define  SEG_E   0x79              
#define  SEG_F   0x71              
#define  SEG_0   0x3F              
#define  SEG_1   0x06              
#define  SEG_2   0x5B              
#define  SEG_3   0x4F              
#define  SEG_4   0x66              
#define  SEG_5   0x6D              
#define  SEG_6   0x7D              
#define  SEG_7   0x07              
#define  SEG_8   0x7F              
#define  SEG_9   0x67              
#define  SEG_j   0x40        
#define  SEG_p   0x80              
#define  SEG__   0x00              

#define BYTE_DELAY 0x00200000         //宏定义
#define GPIO_ADDR  0xf0000000         //定义GPIO基地址

//定义GPIO基地址的偏移量

#define LED_VAL  0x00                 //LED数值
#define LED_DIR  0x04                 //LED 方向(1输入/0输出)

#define SEAT_VAL 0x08                 //数码管位选数值
#define SEAT_DIR 0x0C                 //数码管位选方向(1输入/0输出)

#define SEG_VAL  0x10                 //数码管段选数值(A,B,C,D,E,F,G,DP)
#define SEG_DIR  0x14                 //数码管段选方向(1输入/0输出)

#define BUT_VAL  0x18                 //按键数值
#define BUT_DIR  0x1C                 //按键方向(1输入/0输出)

//===============================================

//===============================================

#define SEG_POS   0x07

#ifdef __cplusplus

}

#endif

#endif /* end __FII_GPIO_H */

 

  • Fii_uart.h
#ifndef __FII_UART_H 

#define __FII_UART_H




#ifdef __cplusplus

 extern "C" {

#endif 

//===============================================

#define UART_BASE  0xe0000000                //定义UART基地址

//定义UART基地址的偏移量

#define UART_VER  0x00                       //定义UART版本寄存器
#define UART_RUN  0x04                       //定义UART使能寄存器
#define UART_DATA 0x08                       //定义UART发送寄存器
#define UART_RDY  0x0C                       //定义UART状态寄存器

//===============================================

int send_to_uart(const void* ptr, int len);     //声明UART发送函数

//===============================================

#ifdef __cplusplus

}

#endif 

#endif /* __FII_UART_H */

 

  • Main.c
#include <stdio.h>                 //引用标准的I / O库,主要用于声明printf函数
#include "platform.h"              //用户自定义的函数库,包括GPIO配置,内存空间以及相关的子函数和参数的定义


#define  NOP_DELAY  0x100          //定义一个宏

const unsigned char font[] =       //索引显示数码管的数组,这里使用十六进制(0〜f)
{
    SEG_0, SEG_1, SEG_2, SEG_3,
    SEG_4, SEG_5, SEG_6, SEG_7,
    SEG_8, SEG_9, SEG_A, SEG_B,
    SEG_C, SEG_D, SEG_E, SEG_F
};

//延迟函数

void delay_cnt (int cnt)
{
    u32_t  i;

    for(i = 0; i < cnt ; i ++ )
        asm("nop");

    return;
};

int main(void)                     //定义主函数
{
    //数码管初始化

    unsigned int  curr_seat = 0x01;              //初始化数码管初始位置
    unsigned int  char_num = 0;                  //数码管显示的值
    unsigned int  curr_num = 0;                  //数码管点亮停留时间
    unsigned int  char_pos = 0;                  //引索数码管字模

    //UART输出字符串

    printf("\r\nRiscV Program : Display segment number and print it. \r\n");

    //GPIO初始化
    GPIO_REG(LED_VAL) = ~0L;         //led_value数值为0,取反参见原理图接线
    GPIO_REG(LED_DIR) = 0;           //LED方向为输出
    GPIO_REG(SEAT_DIR) = 0;          //数码管位选方向为输出
    GPIO_REG(SEG_DIR) = 0;           //数码管段选方向为输出

    while ( 1 )//主循环
    {
        GPIO_REG(SEG_VAL) = ~font[char_pos & 0xf];     //设置数码管为字模中的一个值,取反参见原理图接线
        GPIO_REG(SEAT_VAL) = ~curr_seat;               //指定数码管位置,取反参见原理图接线

        delay_cnt (NOP_DELAY);               //时间延迟
        curr_seat = curr_seat << 1;          //点亮的数码管左移一位

        if(curr_seat == 0x40)                //决策判断数码管是否显示到最高位
        {
            curr_seat = 0x01;                //重新设置数码管显示为最低位
            curr_num ++ ;                    //计时器加一

            if(curr_num % 1000 == 9)         //避免多次输出,每一千次循环输出一次
            {
                char_num ++ ;                //数码管显示的内容加一
                printf("seg cnt num = 0x%06x \r\n", char_num);
            }

            char_pos = char_num;             //将数码管显示的内容赋值给char_pos,字模索引
        }
        else char_pos = char_pos >> 4;       //当数码管显示位置不是最高位,当前显示的内容右移4位(16进制右移一位)

    }

}

 

 数码管工程下载:

RISCV_seg_cnt

Posted in RISC-V 教案, 教材与教案

发表评论

相关链接