前言

资源准备

龙芯集成开发环境.zip

24-龙芯集成开发环境安装与使用_V1.3.pdf

阿里云盘

龙芯官网

龙芯1B手册

龙芯软件最新版

龙芯1B — NOR Flash烧录PMON

安装环境

  • 首先安装 01_msys2_full_install.exe,默认安装在C盘不用管,安装路径:C:\msys32
  • 安装完就配置环境变量,安装时默认已经配了只需把对应环境变量的前缀小写c改成大写C即可,环境变量:C:\msys32\usr\binC:\msys32\mingw32\bin
  • 然后安装 02_loongide_1.2_beta1_setup.exe,安装路径默认D盘,可自己改,但是 安装路径不能有中文和空格!

“龙芯1x 嵌入式集成开发环境”使用 SDE Lite for MIPS 工具链来实现项目的编译和调试。

用户可以在 LoongIDE 中安装一个或者多个工具链,使用时根据项目的实际情况来选择适用的工具链。可以在设置里调成中文页面

软件安装完成后 工具链默认已经配置好,如果新建工程或者编译代码有问题可以按照以下操作重新进行工具链导入:

导入成功后是这样:

  • 新建项目

工程代码在线调试

通过 Mini USB 电缆,将龙芯 1B 开发板的 USB 接口PC/ 笔记本电脑的USB 接口 进行连接,并且通过外部 +5V 电源和电源开关给龙芯 1B 开发板供电。该步骤使得龙芯 1B 开发板处于正常的工作状态和调试准备状态。

双击运行LoongIDE安装路径下的 ftditest.exe ,然后点击 Load Driver 再点击 Do Test 如果出现了 driver is working fine. 说明驱动工作正常

开发板程序固化

点击 齿轮 图标进行编译,编译无误后,点击 三角 图标,将程序下载到内存之中。【注意:此时代码没有下载到 nand flash 之中,按下复位键后,程序会消失】

使用 EJTAG 下载程序会在动态内存中运行,断电后丢失,使用 LoongIDE Nand Flash编程工具可以将程序下载到 Nand Flash 将程序固化

PMON 是一个兼有 BIOS 和 bootloader 部分功能的开放源码软件,多用于嵌入式系统。

NAND Flash 用于存储用户应用程序,通过 bootloader 装载运行

  • 使用 Nand Flash 编程之前,首先需要 设置电脑与开发板至同一网段内,保证开发板与电脑可以使用 ping 命令进行测试。
  1. 打开Windows 网络配置,设置电脑有线网卡为固定 IP , 注意:系统可能有多个网卡请选择与开发板连接的网卡进行修改。
设置本机IP 地址 设置子网掩码 设置默认网关
192.168.1.X(X是任意数) 255.255.255.0 192.168.1.1
  1. 设置完IP 地址之后,使用网线连接开发板 以太网接口 与电脑以太网接口 打开Windows CMD 窗口,输入 ping 192.168.1.2 ,看看是否通了

需要注意的是在电脑与开发板进行ping 命令测试时, 一定要让开发板处于 PMON 命令模式 ,如果开发板目前在运行程序,是无法 ping 通的, 如果开发板内目前有程序运行,可以先将开发板复位或者擦除开发板 Nand Flash 内的程序

  1. 打开Nand Flash 工具后,会自动加载当前打开工程的可执行文件,点击确定即可

  1. 开发板启动以后,串口输出以下数据,代表固化成功,开机自动运行。通常来说,在产品发布阶段或者作品提交时才会进行代码固化,Flash 的擦写次数是有限的,且固化程序之后,想要再次固化或者调试,需要先擦除掉已有的固化程序。

开发板固化程序擦除

方法1

擦除固化时候,首先打开Nand Flash 编程工具 ,按住键盘 Ctrl 按键,窗口上方会提示 “清除 PMON 的自动运行设置”,点击确定,再次点击是,即可将固化程序擦除。【过程中不用松开Ctrl】

开发板启动以后,串口输出 “PMON>” 即代表固化程序被擦除,自动进入到PMON命令行模式。

方法2

打开串口助手,连接开发板串口调试接口。

串口助手连接成功以后,将开发板复位,在开发板启动阶段,通过串口助手 连续点击发送“ unset al ”字符注意需要勾选发送新行

一端连接开发板的 UART5,另一端连接上位机

串口控制台参数: 1152008N1(8位数据、无校验、1位停止位)

芯片介绍

主要
集成 2个 SPI控制器,支持系统启动
集成 AC97控制器
集成 1个全功能串口 、 1个四线串口 和 10个两线串口
集成 3路 I2C控制器,兼容 SMBUS
集成 2个 CAN总线控制器
集成 61个 GPIO端口
集成 1个 RTC接口
集成 4个 PWM控制器【数据宽度32位】
集成看门狗电路
核心板参数
SoC:龙芯1B200
内存:64M DDR2(其存储技术为DDR2)
NOR Flash:512K
NAND Flash:128M
尺寸:64mm×46mm
连接器:molex

开发板板载 EJTAG 调试器,通过USB连接开发主机。

LoongIDE工程介绍

  • 这个是龙芯1B外设库函数,开发板设备的通用驱动,包含 LS1B 所有控制器

  • core:启动文件和 LS1B 的管脚定义

    libc:库文件

    include:头文件

  • 一个设备在使用前必需执行 initialize,完成硬件初始化、创建mutex、安装中断等的初始化工作;有些设备还需要执行open操作,才可以进行读写操作【如果设备是通过复用功能配置的,必需在initialize中执行初始化操作】

  • 新建 .c .h 需要在编译设置那添加头文件路径然后重启项目

  • 在函数上使用 Ctrl + 鼠标左键 可跳转

项目文件后缀
.lxp 项目文件
.layout 项目配置文件
.S 大写,MIPS汇编语言源文件
.c/.cpp 项目源文件
.h/.hpp 项目头文件

LoongIDE对c/c++_源文件进行预处理,约定 "" < >的使用:

#include "xxx": 查找顺序为当前目录、本地搜索路径、系统搜索路径

#include <xxx>: 查找顺序为系统搜索路径、当前目录、本地搜索路径

GPIO结构表

GPIO

  • 芯片的61个GPIO都可以复用为外部功能,GPIO对应的所有PAD都是 推拉方式,内部上拉;输出高电平为 3.3V,输出低电平为 0V;作为输入时,外部可为 3.3~5V,输入低电平为 0V
  • PAD和PIN 的区别在于PAD是一种物理连接,它可以用来连接外部设备,而PIN是一种电气连接,它可以用来连接处理器内部的电路
  • GPIO 引脚编号: GPIO00~GPIO61,但是没有 GPIO31,共计 61 个引脚,相应函数在 #include "ls1b_gpio.h"
//使用时需要包含头文件
#include "ls1b_gpio.h"

//初始化GPIO,设置方向
//参数1:端口序号(0~61)
//参数2:方向 DIR_IN: 输入, DIR_OUT: 输出
static inline void gpio_enable(int ioNum, int dir)
    
//读取管脚电平
//参数:管脚序号(0~61)    
//返回值:1-高电平 0-低电平    
static inline int gpio_read(int ioNum)    
    
//输出电平
//参数1:管脚序号(0~61)
//参数2:1-高电平 0-低电平    
static inline void gpio_write(int ioNum, int val)    
    
//恢复GPIO管脚状态
//参数:管脚序号(0~61)       
static inline void gpio_disable(int ioNum)    
//GPIO中断使能
//参数:管脚序号
//返回值:成功返回0 失败返回-1
int ls1x_enable_gpio_interrupt(int gpio)
    
//GPIO禁止中断
//参数:管脚序号
//返回值:成功返回0 失败返回-1    
int ls1x_disable_gpio_interrupt(int gpio)    
    
//GPIO中断配置触发模式与中断服务函数
//参数1:管脚序号
//参数2:触发方式(上升沿 下降沿 高电平 低电平)
//参数3:中断向量(中断服务函数)
//参数4:附带参数(传进中断处理函数的参数,不需要时填NULL)
//返回值:成功返回0 失败返回-1    
int ls1x_install_gpio_isr(int gpio, int trigger_mode, void (*isr)(int, void *), void *arg)    
    
//GPIO删除中断
//参数:管脚序号    
int ls1x_remove_gpio_isr(int gpio)    

static inline 修饰的函数是一种内联函数,它可以提高程序的执行效率,减少函数调用的开销,从而提高程序的性能,一般来说,那些经常被调用的函数,如简单的数学运算函数、字符串处理函数等,都可以使用static inline void修饰; 但是只能提高程序的执行效率,而不能改变程序的结构和算法;一般来说,不建议使用static inline修饰复杂的函数,因为这样会增加程序的代码量,从而降低程序的执行效率。此外,也不建议使用static inline修饰那些经常被修改的函数,因为这样会导致程序的可维护性变差。

一般 static inline 修饰的函数放在 .h 文件里,方便其他文件调用

printk 函数主要做两件事情:第一件就是将信息记录到log中第二件事就是调用控制台驱动来将信息输出;功能类似 printf

printk相比printf来说还多了个:日志级别的设置, 用来控制printk打印的这条信息是否在终端上显示的,当日志级别的数值小于控制台级别时,printk要打印的信息才会在控制台打印出来,否则不会显示在控制台

LED

硬件连接

高电平亮

GPIO 用途
34 红LED
35 蓝LED
37 绿LED

程序编写

led.h
#ifndef _LED_H
#define _LED_H
#include "all.h"

//管脚定义
#define LED1 34 //红
#define LED2 37 //绿
#define LED3 35 //蓝


typedef struct
{
    uint8_t Red_Led;
    uint8_t Green_Led;
    uint8_t Blue_Led;
    void (*vLED_init)(void);
    void (*vLED_set)(uint8_t,uint8_t);
}LED_TypeDef;

extern LED_TypeDef Led_Data;


void vLED_init(void);
void vLED_set(uint8_t led_color,uint8_t swch);
#endif
led.c
/*
 * led.c
 *
 * created: 2023-03-23
 *  author:
 *  module: LED区
 */
#include "led.h"

//初始化结构体
LED_TypeDef Led_Data =
{
    .Red_Led = LED1,
    .Green_Led =LED2,
    .Blue_Led = LED3,
    .vLED_init = &vLED_init,
    .vLED_set = &vLED_set
};

//LED初始化
void vLED_init(void)
{
    //设置GPIO为输出状态
    gpio_enable(Led_Data.Blue_Led,DIR_OUT);
    gpio_enable(Led_Data.Green_Led,DIR_OUT);
    gpio_enable(Led_Data.Red_Led,DIR_OUT);
    //关闭LED
    gpio_write(Led_Data.Blue_Led,RESET);
    gpio_write(Led_Data.Green_Led,RESET);
    gpio_write(Led_Data.Red_Led,RESET);
}

//点亮/熄灭灯
void vLED_set(uint8_t led_color,uint8_t swch)
{
    if(SET == swch)
    {
        gpio_write(led_color,SET);
    }
    else
    {
        gpio_write(led_color,RESET);
    }
}

按键

硬件连接

对应管脚序号 用途
56 S1
57 S2
40 S3
41 S4

程序编写

key.h
#ifndef _KEY_H
#define _KEY_H
#include "all.h"

//按键
#define S1 56
#define S2 57
#define S3 40
#define S4 41
//读取电平
#define KEY1 gpio_read(S1)
#define KEY2 gpio_read(S2)
#define KEY3 gpio_read(S3)
#define KEY4 gpio_read(S4)

typedef struct
{
    bool Key1_Down_Flag;
    bool Key2_Down_Flag;
    bool Key3_Down_Flag;
    bool Key4_Down_Flag;
    void (*vKEY_init)(void);
    uint8_t (*ucKEY_sub)(void);
    void (*vKEY_detection)(void);
    void (*vKEY_function)(void);
}KEY_TypeDef;

extern KEY_TypeDef Key_Data;

void vKEY_init(void);
uint8_t ucKEY_sub(void);
void vKEY_detection(void);
void vKEY_function(void);
#endif
key.c
/*
 * key.c
 *
 * created: 2023-03-23
 *  author: 
 *  module: 按键
 */
 
#include "key.h"

static uint8_t Key_Up,Key_Down,Key_Value;


//初始化结构体
KEY_TypeDef Key_Data =
{
    .Key1_Down_Flag = 0,
    .Key2_Down_Flag = 0,
    .Key3_Down_Flag = 0,
    .Key4_Down_Flag = 0,
    .vKEY_init = &vKEY_init,
    .ucKEY_sub = &ucKEY_sub,
    .vKEY_detection = &vKEY_detection,
    .vKEY_function = &vKEY_function
};

//按键初始化
void vKEY_init(void)
{
    gpio_enable(S1,DIR_IN);
    gpio_enable(S2,DIR_IN);
    gpio_enable(S3,DIR_IN);
    gpio_enable(S4,DIR_IN);
}

//按键检测子程序
uint8_t ucKEY_sub(void)
{
    if((!KEY1) || (!KEY2) || (!KEY3) || (!KEY4))
    {
        if(!KEY1)
        {
            return 1;
        }
        if(!KEY2)
        {
            return 2;
        }
        if(!KEY3)
        {
            return 3;
        }
        if(!KEY4)
        {
            return 4;
        }
    }
    return 0;
}

//按键检测
void vKEY_detection(void)
{
    static uint8_t Key_Old;
    
    Key_Value = Key_Data.ucKEY_sub();
    Key_Up = ~Key_Value & (Key_Old^Key_Value);
    Key_Down = Key_Value & (Key_Old^Key_Value);
    Key_Old = Key_Value;
    
    switch(Key_Up)
    {
        case 1:
            {
                Key_Data.Key1_Down_Flag = 1;
                break;
            }
        case 2:
            {
                Key_Data.Key2_Down_Flag = 1;
                break;
            }
        case 3:
            {
                Key_Data.Key3_Down_Flag = 1;
                break;
            }
        case 4:
            {
                Key_Data.Key4_Down_Flag = 1;
                break;
            }
            default:
                break;
    }
}
//按键功能执行
void vKEY_function(void)
{
    if(Key_Data.Key1_Down_Flag)
    {
        Key_Data.Key1_Down_Flag = 0;
        Led_Data.vLED_set(Led_Data.Blue_Led,SET);
    }
    if(Key_Data.Key2_Down_Flag)
    {
        Key_Data.Key2_Down_Flag = 0;
        Led_Data.vLED_set(Led_Data.Green_Led,SET);
    }
    if(Key_Data.Key3_Down_Flag)
    {
        Key_Data.Key3_Down_Flag = 0;
        Led_Data.vLED_set(Led_Data.Red_Led,SET);
    }
    if(Key_Data.Key4_Down_Flag)
    {
        Key_Data.Key4_Down_Flag = 0;
    }
}


外部中断按键

LS1B0200 处理器的中断可以分为 软中断(软中断 0 和软中断 1)、 外设中断 (INT0,INT1, INT2 和 INT3 四个中断控制总线)、 Mips 性能中断Mips 计数中断 四类。INT0,INT1,INT2 和 INT3 四个中断控制总线连接到 CPU,其中 INT0 和 INT1 负责内部 64 个中断源,INT2 和 INT3 负责外部 61 个 GPIO 中断源。 没有中断优先级

  • 中断处理函数名称可以自定义,但是格式必须是 void (*isr)(int, void *)【可在irq.c里参考默认中断服务函数格式】
key.c
#include "bsp.h"    //包含打印函数所在的头文件

unsigned char KEY_EXTI_Flag = 0;    //外部中断标志位

//外部中断服务函数
void KEY_IRQhandler(int IRQn, void *arg)
{
    KEY_EXTI_Flag = 1;
}

void KEY_EXTI_function(void)
{
    static unsigned char change_flag = 0;
    static unsigned int num;

    if(KEY_EXTI_Flag)
    {
        KEY_EXTI_Flag = 0;
        gpio_write(LED1,change_flag);
        num++;
        change_flag ^=1;
        printk("num=%d\n",num);
    }
}

//按键外部中断初始化
//BUTTOM1 上升沿触发,其他的下降沿触发
void KEY_EXTI_init(unsigned char ionum)
{
    ls1x_disable_gpio_interrupt(ionum); //失能引脚外部中断
    ls1x_install_gpio_isr(ionum,INT_TRIG_EDGE_UP,KEY_IRQhandler,NULL);  //中断配置上升沿触发
    ls1x_enable_gpio_interrupt(ionum);  //使能引脚外部中断
}

蜂鸣器

硬件连接

对应管脚序号 用途
46 BEEP

程序编写

buzzer.h
#ifndef _BUZZER_H
#define _BUZZER_H
#include "all.h"

//管脚定义
#define BUZZER 46

typedef struct
{
    void (*vBUZZER_init)(void);
    void (*vBUZZER_control)(uint8_t);
}BUZZER_TypeDef;

extern BUZZER_TypeDef Buzzer_Data;

void vBUZZER_init(void);
void vBUZZER_control(uint8_t swch);
#endif
buzzer.c
/*
 * buzzer.c
 *
 * created: 2023-03-24
 *  author:
 *  module: 蜂鸣器
 */
 
#include "buzzer.h"

//结构体初始化
BUZZER_TypeDef Buzzer_Data =
{
    .vBUZZER_init = &vBUZZER_init,
    .vBUZZER_control = &vBUZZER_control
};


//蜂鸣器初始化
void vBUZZER_init(void)
{
    gpio_enable(BUZZER,DIR_OUT);    //输出
    gpio_write(BUZZER,RESET);   //关闭
}

//开启\关闭蜂鸣器
void vBUZZER_control(uint8_t swch)
{
    if(SET == swch)
    {
        gpio_write(BUZZER,SET);
    }
    else
    {
        gpio_write(BUZZER,RESET);
    }
}

数码管

硬件连接

对应管脚序号 用途
39 SI
48 RCK
49 SCK
45 COM1
44 COM2
43 COM3
42 COM4

程序编写

smg.h
#ifndef _SMG_H
#define _SMG_H
#include "all.h"

//管脚定义
#define SMG_SI 39
#define SMG_RCK 48
#define SMG_SCK 49
#define SMG_COM1 45
#define SMG_COM2 44
#define SMG_COM3 43
#define SMG_COM4 42

#define  HC595_SI(val)  gpio_write(SMG_SI,val) //控制595芯片串行输入(Serial Input)管脚的电平
#define  HC595_RCK(val) gpio_write(SMG_RCK,val) //控制595芯片锁存(Latch)管脚的电平
#define  HC595_SCK(val) gpio_write(SMG_SCK,val) //控制595芯片串行时钟(Serial Clock)管脚的电平

typedef struct
{
    void (*vSMG_init)(void);
    void (*vHC595_send_data)(uint8_t);
    void (*vSMG_choose)(uint8_t);
    void (*vSMG_countdown)(uint16_t);
    
}SMG_TypeDef;

extern SMG_TypeDef Smg_Data;
extern unsigned char Display[16];
extern unsigned char Display_1[];


void vSMG_init(void);
void vHC595_send_data(uint8_t dat);
void vSMG_choose(uint8_t index);
void vSMG_countdown(uint16_t init_value);
#endif
smg.c
/*
 * smg.c
 *
 * created: 2023-03-24
 *  author:
 *  module: 共阴数码管
 */
 
#include "smg.h"

//结构体初始化
SMG_TypeDef Smg_Data =
{
    .vSMG_init = &vSMG_init,
    .vHC595_send_data = &vHC595_send_data,
    .vSMG_choose = &vSMG_choose,
    .vSMG_countdown = &vSMG_countdown
};

//段码--0~F
unsigned char Display[16] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; // 不带小数点
unsigned char Display_1[] = {0xbf,0x86,0xdb,0xcf,0xef,0xed,0xfd,0x87,0xff,0xef,0xff,0x00};	// 带小数点

//数码管初始化
void vSMG_init(void)
{
    //595
    gpio_enable(SMG_SI,DIR_OUT);
    gpio_enable(SMG_RCK,DIR_OUT);
    gpio_enable(SMG_SCK,DIR_OUT);
    //COM端
    gpio_enable(SMG_COM1,DIR_OUT);
    gpio_enable(SMG_COM2,DIR_OUT);
    gpio_enable(SMG_COM3,DIR_OUT);
    gpio_enable(SMG_COM4,DIR_OUT);

    Smg_Data.vHC595_send_data(0x00);    //全灭
}

//HC595发送数据
//参数:段码数据
void vHC595_send_data(uint8_t dat)
{
    uint8_t dat_buf = 0, i;
    for(i=0; i<8; i++)
    {
        dat_buf = dat & 0x80;
        if (dat_buf)      //输出1bit数据
        {
            HC595_SI(SET);    //串行输入拉高
        }
        else
        {
            HC595_SI(RESET);    //串行输入拉低
        }
        HC595_SCK(RESET);   //时钟拉低
        delay_us(1);
        HC595_SCK(SET); //时钟拉高
        delay_us(1);
        dat <<= 1;
    }
    HC595_RCK(RESET);   //锁存拉低
    delay_us(3);
    HC595_RCK(SET);   //锁存拉高
}

//数码管选择(从左到右)
//参数:对应的数码管(0~3)
void vSMG_choose(uint8_t index)
{
    switch(index)
    {
        case 0: //1
            gpio_write(SMG_COM1,SET);
            gpio_write(SMG_COM2,RESET);
            gpio_write(SMG_COM3,RESET);
            gpio_write(SMG_COM4,RESET);
            break;
        case 1: //2
            gpio_write(SMG_COM1,RESET);
            gpio_write(SMG_COM2,SET);
            gpio_write(SMG_COM3,RESET);
            gpio_write(SMG_COM4,RESET);
            break;
        case 2: //3
            gpio_write(SMG_COM1,RESET);
            gpio_write(SMG_COM2,RESET);
            gpio_write(SMG_COM3,SET);
            gpio_write(SMG_COM4,RESET);
            break;
        case 3: //4
            gpio_write(SMG_COM1,RESET);
            gpio_write(SMG_COM2,RESET);
            gpio_write(SMG_COM3,RESET);
            gpio_write(SMG_COM4,SET);
            break;
        default:    //全部不选
            gpio_write(SMG_COM1,RESET);
            gpio_write(SMG_COM2,RESET);
            gpio_write(SMG_COM3,RESET);
            gpio_write(SMG_COM4,RESET);
            break;
    }
}

//倒计时
void vSMG_countdown(uint16_t init_value)
{
    //判断参数是否超出最大范围
    if(init_value > 9999)
    {
        init_value = 0;
    }
        
    uint16_t count = init_value;
    uint8_t temp = 0;   //段码数据
    uint32_t ms_count = get_clock_ticks()+1000;;  //获取当前时间+1000
    
    while(count > 0)
    {
        // 每隔1000个倒计时周期刷新显示
        if(get_clock_ticks() > ms_count)
        {
            ms_count=get_clock_ticks()+1000;
            count--;
        }
        //数码管1显示
        temp = Display[count / 1000];
#if 0
        if(0x3F == temp)
        {
            Smg_Data.vHC595_send_data(0x00);
        }
#endif
        Smg_Data.vSMG_choose(0);
        delay_ms(1);
        //数码管2显示
        temp = Display[count / 100 % 10];
        Smg_Data.vHC595_send_data(temp);
        Smg_Data.vSMG_choose(1);
        delay_ms(1);
        //数码管3显示
        temp = Display[count / 10 % 10];
        Smg_Data.vHC595_send_data(temp);
        Smg_Data.vSMG_choose(2);
        delay_ms(1);
        //数码管4显示
        temp = Display[count % 10];
        Smg_Data.vHC595_send_data(temp);
        Smg_Data.vSMG_choose(3);
        delay_ms(1);
    }
//倒计时完成显示0000
        //数码管1显示
        temp = Display[0];
        Smg_Data.vHC595_send_data(temp);
        Smg_Data.vSMG_choose(0);
        delay_ms(1);
        //数码管2显示
        temp = Display[0];
        Smg_Data.vHC595_send_data(temp);
        Smg_Data.vSMG_choose(1);
        delay_ms(1);
        //数码管3显示
        temp = Display[0];
        Smg_Data.vHC595_send_data(temp);
        Smg_Data.vSMG_choose(2);
        delay_ms(1);
        //数码管4显示
        temp = Display[0];
        Smg_Data.vHC595_send_data(temp);
        Smg_Data.vSMG_choose(3);
        delay_ms(1);
}


LCD屏幕

需要在 bsp.h 里打开下面外设:

#define BSP_USE_FB

然后定义一个全局数组(必须跟下面一模一样)

char LCD_display_mode[] = LCD_800x480;  //定义

包含头文件 #include "ls1x_fb.h"

引入 lkdGui_source ,添加在src那(在resource里),然后初始化,之前的LCD初始化不要屏蔽或者删留着否则看不到效果

defaultFontInit();/* 字库初始化 */
GuiUpdateDisplayAll();/* 更新屏幕-清屏 */
  • Gui库函数

颜色只有黑白

函数 参数 描述
void GuiRowText(lkdCoord x, lkdCoord y,uint16_t wide, FontFlag flag,uint8_t *str) x,y 起始坐标
wide 单行文本宽度
flag 字体对齐标志
str 文本字符串
写单行文本
注意如果显示后面有乱码可以在字符串后面空几个格

坐标是:

  • 自带

显示图片

把生成的数组复制,最好新建一个 .h 文件保存,然后需要去 ls1x_fb_utils.h 里找到 LS1x_draw_rgb565_pixel把宏定义打开

需要保证 图片的像素 和函数的像素一致

//显示图片
//参数1:x
//参数2:y
//参数3:像素
//参数4:像素
//参数5:显示的数组
void display_pic(unsigned int xpos,unsigned int ypos,unsigned int x1,unsigned int y1,unsigned char *ptrs)
{
    {
        int x, y;
        unsigned char *ptr = ptrs;

        for (y=0; y<y1; y++)
        {
            for (x=0; x<x1; x++)
            {
                unsigned int color;

                color = (*ptr << 8) | *(ptr+1);

                LS1x_draw_rgb565_pixel(x+xpos, y+ypos, color);

                ptr += 2;
            }
        }

        flush_dcache();
    }

}

小创

硬件连接

需要头文件 #include "ns16550.h"

更新词条需要把下面的开关拨到 更新词条 那档(2个为一组),更新完要识别则要拨到 语音识别 那档

小创使用的是 串口4 ,需要在 bsp.h 打开宏定义 #define BSP_USE_UART4

注意写的格式,有可能读不出来就是格式有问题:

以下是内部函数对串口的初始化设置(参数可自行修改,在ns16550.c大概1170行):

/* UART 4 */
#ifdef BSP_USE_UART4
static NS16550_t ls1c_UART4 =
{
    .BusClock  = 0,   //总线频率,初始化时填充              
    .BaudRate  = 115200,	// 默认速率
    .CtrlPort  = LS1C_UART4_BASE,	// 串口寄存器基址
    .DataPort  = LS1C_UART4_BASE,	
    .bFlowCtrl = false,             // 启用硬件支持
    .ModemCtrl = 0,
    .bIntrrupt = true,              // 使用中断方式
    .IntrNum   = LS1C_UART4_IRQ,	// 系统中断号
    .IntrCtrl  = LS1C_INTC1_BASE,	// 中断寄存器
    .IntrMask  = INTC1_UART4_BIT,	// 中断屏蔽位
    .dev_name  = "uart4",	// 设备名称
};
void *devUART4 = &ls1c_UART4;
#endif

注意

~~中断里判断接收的数组时需要延时或者打印控制台(不推荐在中断延时)然后才能判断(反正就是不要这么快执行到下面代码就行),不然判断不了(很sb的这龙芯,debug半天才找到是这个原因)~~2023.3.25(此方案虽然可以接收回传并且执行对应功能但是会造成其他功能全部卡死原因是中断使用延时导致定时器中断卡死)

  • 方案2

通过打印调试发现每次接收有效数据后面都有一个0xFF,所以直接采用状态机进行接收

xiaochuang.h
#ifndef _XIAOCHUANG_H
#define _XIAOCHUANG_H
#include "all.h"

//定义管脚
#define USART4_GPIO 58
#define UART4_RX_MAX_LEN 30 //接收最大长度

typedef struct
{
    bool UART4_Rx_Over_Flag;    //接收有效数据完成标志位
    uint8_t Rx_Data[8]; //有效数据缓存
    char UART4_RX_BUFF[UART4_RX_MAX_LEN];   //接收数据存储数组
    uint8_t XiaoChuang_return_state;    //小创回传状态(用于分辨不同回传0~255)
    void (*vUART4_init)(void);
    void (*vXIAOCHUANG_order_parse)(void);
    void (*vXIAOCHUANG_state_function)(void);
    void (*vXIAOCHUANG_play_specify_content)(int);
} XIAOCHUANG_TypeDef;

extern XIAOCHUANG_TypeDef XiaoChuang_Data;

void vUART4_init(void);
void vUART4_IRQhandler(int IRQn,void* param);
void vXIAOCHUANG_order_parse(void);
void vXIAOCHUANG_state_function(void);
void vXIAOCHUANG_play_specify_content(int id);
#endif
xiaochuang.c
/*
 * xiaochuang.c
 *
 * created: 2023-03-24
 *  author:
 *  module: 小创语音
 */
#include "xiaochuang.h"


//初始化结构体数据
XIAOCHUANG_TypeDef XiaoChuang_Data =
{
    .UART4_Rx_Over_Flag = 0,
    .Rx_Data = {0},
    .UART4_RX_BUFF = {0},
    .XiaoChuang_return_state = 0,
    .vUART4_init = &vUART4_init,
    .vXIAOCHUANG_order_parse = &vXIAOCHUANG_order_parse,
    .vXIAOCHUANG_state_function = &vXIAOCHUANG_state_function,
    .vXIAOCHUANG_play_specify_content = &vXIAOCHUANG_play_specify_content
};

//串口4初始化
void vUART4_init(void)
{
    unsigned int BaudRate = 115200;

    ls1x_uart_init(devUART4,(void *)BaudRate); //初始化串口
    ls1x_uart_open(devUART4,NULL); //打开串口
    ls1x_disable_gpio_interrupt(USART4_GPIO);   //失能GPIO中断
    //端口号,上升沿触发,中断函数名,NULL
    ls1x_install_gpio_isr(USART4_GPIO,INT_TRIG_EDGE_UP,vUART4_IRQhandler,NULL); //中断初始化
    ls1x_enable_gpio_interrupt(USART4_GPIO);    //使能GPIO中断
}

/*
功能:串口4中断函数
小创播报后回传命令格式:55 02 01 00(帧头 数据类型 状态标志 数据位 ----帧头固定的后面3位可自定义)
uart6_flag执行顺序:0x00-->0x01-->0x02-->0x03-->0x00...以此循环
*/
void vUART4_IRQhandler(int IRQn,void* param)
{
    static uint8_t timing_flag = 0;  //时序

    ls1x_uart_read(devUART4,XiaoChuang_Data.UART4_RX_BUFF,UART4_RX_MAX_LEN,NULL);  //接收数据,返回值是读取的字节数

    if(0x55 == XiaoChuang_Data.UART4_RX_BUFF[0])
    {
        timing_flag = 1;

    }
    switch(timing_flag)
    {
        case 1: //帧头
            {
                XiaoChuang_Data.Rx_Data[0] = XiaoChuang_Data.UART4_RX_BUFF[0];
                timing_flag = 2;
                break;
            }
        case 2: //0xFF
            {
                XiaoChuang_Data.Rx_Data[1] = XiaoChuang_Data.UART4_RX_BUFF[0];
                timing_flag = 3;
                break;
            }
        case 3: //数据类型
            {
                XiaoChuang_Data.Rx_Data[2] = XiaoChuang_Data.UART4_RX_BUFF[0];
                timing_flag = 4;
                break;
            }
        case 4: //0xFF
            {
                timing_flag = 5;
                XiaoChuang_Data.Rx_Data[3] = XiaoChuang_Data.UART4_RX_BUFF[0];
                break;
            }
        case 5: //状态标志
            {
                timing_flag = 0;    //新的一轮
                XiaoChuang_Data.UART4_Rx_Over_Flag = 1; //接收完成
                XiaoChuang_Data.Rx_Data[4] = XiaoChuang_Data.UART4_RX_BUFF[0];
                memset(XiaoChuang_Data.UART4_RX_BUFF,0,sizeof(XiaoChuang_Data.UART4_RX_BUFF));  //清0
#if 0
                printk("%#x--%#x--%#x--%#x--%#x\r\n",Rx_Data[0],Rx_Data[1],Rx_Data[2],Rx_Data[3],Rx_Data[4]);
#endif
                break;
            }
        default:
            break;
    }
}

/*
当前数据:
任务一启动::好的,任务一启动:55020100
任务二启动::好的,任务二启动:55020200
任务三启动::好的,任务三启动:55020300
任务四启动::好的,任务四启动:55020400
任务五启动::好的,任务五启动:55020500
任务六启动::好的,任务六启动:55020600
任务七启动::好的,任务七启动:55020700
任务八启动::好的,任务八启动:55020800
任务九启动::好的,任务九启动:55020900
任务十启动::好的,任务十启动:55020A00
*/

/*
功能:小创回传解析
主要是判断asrWordlist.txt里的命令回传
通过赋不同值标志位XiaoChuang_return_state来进行分辨不同回传命令
回传格式:5502XX00
*/
void vXIAOCHUANG_order_parse(void)
{
    if (XiaoChuang_Data.UART4_Rx_Over_Flag)
    {
        XiaoChuang_Data.UART4_Rx_Over_Flag = 0;
        
        if(0x02 == XiaoChuang_Data.Rx_Data[2]) //数据类型
        {
            switch(XiaoChuang_Data.Rx_Data[4])    //状态标志
            {
                case 0x01:
                    {
                        XiaoChuang_Data.XiaoChuang_return_state = 1;
                        break;
                    }
                case 0x02:
                    {
                        XiaoChuang_Data.XiaoChuang_return_state = 2;
                        break;
                    }
                case 0x03:
                    {
                        XiaoChuang_Data.XiaoChuang_return_state = 3;
                        break;
                    }
                case 0x04:
                    {
                        XiaoChuang_Data.XiaoChuang_return_state = 4;
                        break;
                    }
                case 0x05:
                    {
                        XiaoChuang_Data.XiaoChuang_return_state = 5;
                        break;
                    }
                case 0x06:
                    {
                        XiaoChuang_Data.XiaoChuang_return_state = 6;
                        break;
                    }
                case 0x07:
                    {
                        XiaoChuang_Data.XiaoChuang_return_state = 7;
                        break;
                    }
                case 0x08:
                    {
                        XiaoChuang_Data.XiaoChuang_return_state = 8;
                        break;
                    }
                case 0x09:
                    {
                        XiaoChuang_Data.XiaoChuang_return_state = 9;
                        break;
                    }
                case 0x0A:
                    {
                        XiaoChuang_Data.XiaoChuang_return_state = 10;
                        break;
                    }
                default:
                    break;
            }
        }
    }
    memset(XiaoChuang_Data.Rx_Data,0,sizeof(XiaoChuang_Data.Rx_Data));  //清0
}

/*
功能:根据小创回传命令状态XiaoChuang_return_state执行对应功能
*/
void vXIAOCHUANG_state_function(void)
{
    switch(XiaoChuang_Data.XiaoChuang_return_state)
    {
        case 1:
            {
                Led_Data.vLED_control(Led_Data.Blue_Led,SET);
                break;
            }
        case 2:
            {
                Led_Data.vLED_control(Led_Data.Green_Led,SET);
                break;
            }
        case 3:
            {
                Led_Data.vLED_control(Led_Data.Red_Led,SET);
                break;
            }
        case 4:
            {
                break;
            }
        case 5:
            {
                break;
            }
        case 6:
            {
                break;
            }
        case 7:
            {
                break;
            }
        case 8:
            {
                break;
            }
        case 9:
            {
                break;
            }
        case 10:
            {
                break;
            }
        default:
            break;
    }
    XiaoChuang_Data.XiaoChuang_return_state = 0;    //清除状态
}

/*
当前数据
01::庆祝中国共产党成立100周年:55030100
02::喜迎二十大、永远跟党走、奋进新征程:55030200
*/
/*
播报指定词条
参数:词条编号
*/
void vXIAOCHUANG_play_specify_content(int id)
{
    int ID_number = id;
    ls1x_uart_write(devUART4,&ID_number,1,NULL);
}

然后在定时器里进行判断

if(50 == Time_50ms) //50ms小创回传检测
{
    Time_50ms = 0;
    XiaoChuang_Data.vXIAOCHUANG_order_parse();
}

定时器

需要包含头文件 #include "ls1x_rtc.h",然后在 bsp.h 打开宏定义 #define BSP_USE_RTC

程序编写

tim.h
#ifndef _TIM_H
#define _TIM_H
#include "all.h"

typedef struct
{
    void (*vTIM_rtc0_init)(void);
}TIM_TypeDef;

extern TIM_TypeDef Tim_Data;

void vTIM_rtc0_init(void);
void rtctimer_callback(int device, unsigned match, int *stop);
#endif
tim.c
/*
 * tim.c
 *
 * created: 2023-03-24
 *  author:
 *  module: 定时器
 */

#include "tim.h"

extern uint16_t number;

//结构体初始化
TIM_TypeDef Tim_Data =
{
    .vTIM_rtc0_init = &vTIM_rtc0_init
};

//RTC开启定时器模式(定时1ms)
void vTIM_rtc0_init(void)
{
    ls1x_rtc_init(NULL,NULL);   //初始化

    //开启RTC定时中断功能
    rtc_cfg_t rtc;
    rtc.cb = rtctimer_callback;	//定时器中断回调函数
    rtc.isr = NULL;	//中断服务函数
    rtc.interval_ms = 1;	//定时器时间间隔(ms)
    rtc.trig_datetime = NULL;	//用于toymatch的日期
    //开启定时器
    ls1x_rtc_timer_start(DEVICE_RTCMATCH0,&rtc);
}

//定时器回调函数
void rtctimer_callback(int device, unsigned match, int *stop)
{
    static uint16_t Time_50ms = 0;
    static uint8_t Time_20ms = 0;

    if(DEVICE_RTCMATCH0 == device)
    {
        Time_20ms++;
        Time_50ms++;
        if(20 == Time_20ms) //20ms按键检测
        {
            Time_20ms = 0;
            Key_Data.vKEY_detection();
        }
        if(50 == Time_50ms)	//50msLCD刷新
        {
            Time_50ms = 0;
            Lcd_Data.Lcd_Display_Flag = 1;
            number++;
        }
    }
}

温度传感器

硬件连接

分析:此电路用到 LM35DZ 传感器,还有 LMV611 差分放大器

LM35DZ是一种温度传感器,其输出信号为温度值直接输出,但输出电压较小,一般在 0.01V/K(每增加1摄氏度的温度变化)左右。为了能够将其输出信号放大并转换为标准的电压信号,需要联合使用运放等电路进行处理。

LMV611是一款高精度、低功耗、铁电电容耦合的差分放大器。其输入端可以直接连接LM35DZ的输出端,作为放大器的非反馈端,另外一个反馈端接地。这样,当LM35DZ输出的温度变化时,LMV611内部就会产生相应的放大电压信号,并通过其输出端进行输出。

在连接LMV611的过程中,需要将 LM35DZ的输出端连接到LMV611的非反馈端(+端),而不是反馈端(-端),这是因为 LMV611是差分放大器,它将输入信号在反相输入端和非反相输入端之间进行差分放大,同时去除掉共模电压干扰。因此,为了更好地抵消环境噪声对输出的影响,并保证系统的精度,需要将LM35DZ的输出信号接到差分放大器的非反馈端。

如果输出端接到反馈端 无论LM35DZ的温度变化如何,输出的电压信号始终为0

//需要在 bsp.h 打开宏定义 #define ADS1015_DRV 
//ADS1015 挂接在 I2C0 上,I2C 地址和通信速率定义如下
#define ADS1015_ADDRESS         0x48	    /* Ground 1001 000 */

#define ADS1015_BAUDRATE        1000000

//包含头文件
#include "ls1x_i2c_bus.h"
#include "i2c/ads1015.h"

读取转换电压思想

adc的值是从0到4095的,adc接到GND,读出来必然是0,接到VCC必然是4095;需要事先定义好量程和分辨率。 量程其实就是基准电压,以5V电压为基准,那么测量的范围就是0V~5V; 分辨率就是测量的精度,假如12位, 12位二进制最大值为4095 ;这时候就可以知道 0V=0,5V=4095 了,把5V分为4095份就可以了;再配合相应的滤波算法,自然可以很好的写出转化的电压

电压值=参考电压*量化精度*ADC采集到的数值/量化等级

程序编写

lm35.h
#ifndef _LM35_H
#define _LM35_H
#include "all.h"

typedef struct
{
    float Lm35_temp;    //读取的温度值
    void (*vLM35_init)(void);
    float (*fLM35_get_temperature)(void);
    
}LM35_TypeDef;


extern LM35_TypeDef Lm35_Data;


void vLM35_init(void);
float fLM35_get_temperature(void);
#endif
lm35.c
/*
 * lm35.c
 *
 * created: 2023-03-25
 *  author:
 *  module: 温度传感器
 */

#include "lm35.h"

LM35_TypeDef Lm35_Data =
{
    .Lm35_temp = 0.0,
    .vLM35_init = &vLM35_init,
    .fLM35_get_temperature = &fLM35_get_temperature
};

//初始化
void vLM35_init(void)
{
    ls1x_i2c_initialize(busI2C0);   //初始化I2C0
    ls1x_ads1015_ioctl(busI2C0,IOCTL_ADS1015_DISP_CONFIG_REG,NULL); //初始化ADC
}

/*
功能:获取温度
返回值:温度
*/
float fLM35_get_temperature(void)
{
    uint16_t adc2 = 0;
    float temp = 0;   //温度临时存储

    adc2 = get_ads1015_adc(busI2C0, ADS1015_REG_CONFIG_MUX_SINGLE_2); //获取ADC
    temp = 4.096*2*adc2/4096;//采集电压的转换公式
    temp = temp*100;
    Lm35_Data.Lm35_temp = temp;
    
    return temp;
}

然后把这个获取温度的函数放在定时器500ms获取一次即可

注意

如果直接把获取函数放定时器肯定会卡死的,因为 get_ads1015_adc 函数里面的实现有使用 delay_ms导致定时器卡死,所以方法是去掉延时即可

加热电阻

硬件连接

分析:这个Q1是 NMOS,S极接地,N极接负载,G极是开关控制,高电平导通(因为要G极电压高于S极才导通)

程序编写

resistor.h
#ifndef _RESISTOR_H
#define _RESISTOR_H
#include "all.h"

//定义管脚
#define RESISTOR_PIN 38

typedef struct
{
    void (*vRESISTOR_init)(void);
    void (*vRESISTOR_control)(uint8_t);
}RESISTOR_TypeDef;

extern RESISTOR_TypeDef Resistor_Data;

void vRESISTOR_init(void);
void vRESISTOR_control(uint8_t swch);
#endif
resistor.c
/*
 * resistor.c
 *
 * created: 2023-03-26
 *  author:
 *  module: 加热电阻
 */
 
#include "resistor.h"

//结构体初始化
RESISTOR_TypeDef Resistor_Data =
{
    .vRESISTOR_init = &vRESISTOR_init,
    .vRESISTOR_control = &vRESISTOR_control
};

/*
功能:初始化
*/
void vRESISTOR_init(void)
{
    gpio_enable(RESISTOR_PIN,DIR_OUT);  //输出模式
    Resistor_Data.vRESISTOR_control(RESET); //默认关闭状态
}

/*
功能:控制加热电阻开关
参数:SET--开 RESET--关
*/
void vRESISTOR_control(uint8_t swch)
{
    if(SET == swch)
    {
        gpio_write(RESISTOR_PIN,SET); //开
    }
    else
    {
        gpio_write(RESISTOR_PIN,RESET); //关
    }
}

风扇

硬件连接

需要包含头文件 #include "i2c/gp7101.h"

程序编写

fan.h
#ifndef _FAN_H
#define _FAN_H
#include "all.h"

//定义管脚
#define FAN_PIN 36

typedef struct
{
    void (*vFAN_init)(void);
    void (*vFAN_control)(uint8_t);
    void (*vFAN_set_speed)(uint8_t);
}FAN_TypeDef;

extern FAN_TypeDef Fan_Data;

void vFAN_init(void);
void vFAN_control(uint8_t swch);
void vFAN_set_speed(uint8_t speed);
#endif


fan.c
/*
 * fan.c
 *
 * created: 2023-03-26
 *  author:
 *  module: 风扇 硬件连接:插板子左侧FAN
 */
#include "fan.h"

//初始化结构体
FAN_TypeDef Fan_Data =
{
    .vFAN_init = &vFAN_init,
    .vFAN_control = &vFAN_control,
    .vFAN_set_speed = &vFAN_set_speed
};


/*
功能:初始化
*/
void vFAN_init(void)
{
    gpio_enable(FAN_PIN,DIR_OUT);//输出
    Fan_Data.vFAN_control(RESET);//默认关闭
    Fan_Data.vFAN_set_speed(0); //速度0
}

/*
功能:控制风扇
参数:SET--开 RESET--关
*/
void vFAN_control(uint8_t swch)
{
    if(SET == swch)
    {
        gpio_write(FAN_PIN,SET);
    }
    else
    {
        gpio_write(FAN_PIN,RESET);
    }
}

/*
功能:风扇转速控制
参数:速度(0~100)
*/
void vFAN_set_speed(uint8_t speed)
{
    set_lcd_brightness(speed);
}

光度传感器

硬件连接

IIC地址线,接GND时器件地址为 0100011 ,接VCC时器件地址为 1011100,上面原理图是接GND的

ADDR='L' 时,该芯片的I2C地址为 0x23,当 ADDR='H' 时,该芯片的I2C地址为 0x5C 。根据不同的地址选择,可以在同一个电路上添加多个BH1750,以便监测多个位置的光强度。

常用命令(手册):

  • 时序

但是使用模拟的方法初始化I2C的IO后温度传感器就失效了冲突,所以还是不要用模拟的

bh1750.h
#ifndef _BH1750_H
#define _BH1750_H
#include "all.h"


#define BH1750_ADDR 0x23    //默认地址 0100 011
#define BH1750_ON   0x01    //等待测量命令
#define BH1750_CON  0x10    //以1 lx 分辨率开始测量测量时间通常为 120 毫秒
#define BH1750_ONE  0x20    //以 1 lx 分辨率开始测量测量时间通常为 120 毫秒测量后自动设置为掉电模式
#define BH1750_RSET 0x07    //复位数据寄存器值复位命令

typedef struct
{
    float Bh1750_value; //最终光度值(单位lx)
    uint8_t BH1750_Read_Buff[2];    //存储读取的待合成的光照值2字节
    void (*BH1750_init)(void);
    void (*vBH1750_Cmd_Write)(uint8_t);
    void (*vBH1750_start)(void);
    void (*vBH1750_read)(void);
    float (*vBH1750_convert_data)(void);
    void (*vBH1750_whole_get_data)(void);
}BH1750_TypeDef;

extern BH1750_TypeDef Bh1750_Data;

void BH1750_init(void);
void vBH1750_Cmd_Write(uint8_t cmd);
void vBH1750_start(void);
void vBH1750_read(void);
float vBH1750_convert_data(void);
void vBH1750_whole_get_data(void);
#endif

bh1750.c
/*
 * bh1750.c
 *
 * created: 2023-03-26
 *  author:
 *  module: 光度传感器 ---I2C通信
 */

#include "bh1750.h"

//初始化结构体
BH1750_TypeDef Bh1750_Data =
{
    .Bh1750_value = 0.0,
    .BH1750_Read_Buff = {0},
    .BH1750_init = &BH1750_init,
    .vBH1750_Cmd_Write = &vBH1750_Cmd_Write,
    .vBH1750_start = &vBH1750_start,
    .vBH1750_read = &vBH1750_read,
    .vBH1750_convert_data = &vBH1750_convert_data,
    .vBH1750_whole_get_data = &vBH1750_whole_get_data
};

/*
功能:初始化
*/
void BH1750_init(void)
{
    Bh1750_Data.vBH1750_start();
}

/*
功能:发送设备地址
*/
void vBH1750_Cmd_Write(uint8_t cmd)
{
    uint8_t data[2]= {0};
    data[0]=cmd;

    ls1x_i2c_send_start(busI2C0,BH1750_ADDR);//起始信号
    ls1x_i2c_send_addr(busI2C0,BH1750_ADDR,0);	//发送设备地址+写信号
    ls1x_i2c_write_bytes(busI2C0, data, 1);	//内部寄存器地址
    ls1x_i2c_send_stop(busI2C0,BH1750_ADDR);	//发送停止信号
    delay_ms(5);
}

/*
功能:开启一次高分辨率模式
*/
void vBH1750_start(void)
{
    Bh1750_Data.vBH1750_Cmd_Write(BH1750_ON);   //打开
    Bh1750_Data.vBH1750_Cmd_Write(BH1750_RSET); //清空
    Bh1750_Data.vBH1750_Cmd_Write(BH1750_CON); //1lx分辨率,至少120ms(如果是ONE则会进入掉电模式每次读取都要先执行vBH1750_start)
}

/*
功能:读光照数据
*/
void vBH1750_read(void)
{
    ls1x_i2c_send_start(busI2C0, BH1750_ADDR);//开始信号
    ls1x_i2c_send_addr(busI2C0,BH1750_ADDR,1);//发送设备地址+读信号
    ls1x_i2c_read_bytes(busI2C0,Bh1750_Data.BH1750_Read_Buff, 2);//读取两个字节数据
    ls1x_i2c_send_stop(busI2C0,BH1750_ADDR);//发送停止信号
}

/*
功能:合成光照数据
返回值:最终的光照数据
*/
float vBH1750_convert_data(void)
{
    uint16_t temp = 0;  //16位

    temp = Bh1750_Data.BH1750_Read_Buff[0];
    temp = (temp<<8)+Bh1750_Data.BH1750_Read_Buff[1];		//合成数据,即光照数据
    Bh1750_Data.Bh1750_value = (float)temp/1.2;

    return Bh1750_Data.Bh1750_value;
}

/*
功能:完成一次完整的采集
*/
void vBH1750_whole_get_data(void)
{
    Bh1750_Data.vBH1750_read(); //连续读取
    Bh1750_Data.vBH1750_convert_data(); //合成
}

UART串口

  • 龙芯1B集成了 12 个UART核
  • 需要用到哪个串口就在 bsp.h 里打开对应的
//使用时需要包含头文件
#include "ns16550.h"

//串口初始化
//参数1:UART设备
//参数2:波特率(为0或NULL时默认是115200)
//返回值:成功返回0 失败返回-1
ls1x_uart_init(uart, arg)
    
//打开串口
//参数1:UART设备    
//参数2:把串口配置为指定参数模式. 该参数可为 0 或 NULL
//返回值:成功返回0 失败返回-1    
ls1x_uart_open(uart, arg) 
    
//关闭串口
//参数1:UART设备    
//参数2:把串口配置为指定参数模式. 该参数可为 0 或 NULL
//返回值:成功返回0 失败返回-1   
ls1x_uart_close(uart, arg)   
    
//读数据
//参数1:UART设备
//参数2:用于存放读取数据的缓冲区数组
//参数3:待读取的字节数, 长度不能超过 buf 的容量
//参数4:中断/阻塞模式下的功能    
//返回值:读取的字节数    
ls1x_uart_read(uart, buf, size, arg)  
 
//写数据
//参数1:UART设备
//参数2:用于存放待发送数据的缓冲区数组
//参数3:待发送的字节数, 长度不能超过 buf 的容量
//参数4:总是 0 或 NULL   
//返回值:发送的字节数    
ls1x_uart_write(uart, buf, size, arg) 
    
//发送控制命令
//参数1:UART设备
//参数2:IOCTL_NS16550_SET_MODE
//参数3:把串口配置为指定参数模式    
ls1x_uart_ioctl(uart, cmd, arg)    

硬件连接

管脚名称 对应管脚序号 用途
UART5_TX 61 串口5发送
UART5_RX 60 串口5接收

程序编写

my_uart.h
#ifndef _MY_UART_H
#define _MY_UART_H
#include "allhead.h"

#define RX_MAX_LEN 250  //接收最大长度


void UART_init(void);
void uart_test(void);
#endif // _MY_UART_H
my_uart.c
#include "my_uart.h"

unsigned char RX_LEN;   //接收的数据长度
unsigned char RX_BUFF[RX_MAX_LEN];  //接收数据数组

//串口初始化
void UART_init(void)
{
    //设置波特率
    unsigned int BaudRate = 9600;
    //初始化串口
    ls1x_uart_init(devUART5,(void*)BaudRate);
    //打开串口
    ls1x_uart_open(devUART5,NULL);
}

//串口发送接收测试
void uart_test(void)
{
    //读取数据
    RX_LEN = ls1x_uart_read(devUART5,RX_BUFF,RX_MAX_LEN,NULL);
    
    if(RX_LEN)  //接收到数据
    {
        //发送回去
        ls1x_uart_write(devUART5,RX_BUFF,8,NULL);
        
        //比较接收数组的前7个字符是否相等
        if(0 == strncmp((char*)RX_BUFF,"LED1_ON",7))
        {
            printk("LED1_ON\n");
        }
        //比较接收数组的前8个字符是否相等
        else if(0 == strncmp((char*)RX_BUFF,"LED1_OFF",8))
        {
            printk("LED1_OFF\n");
        }
    }
}

RTC

龙芯1B实时时钟( RTC)单元可以在主板上电后进行配置,当主板断电后,该单元仍然运作,可以仅靠板上的电池供电就正常运行。 RTC单元运行时功耗 为 300微瓦。RTC由 32.768 KHz时钟 驱动,内部经可配置的分频器分频进行计数,年月日,时分秒等信息被更新。同时该时钟也用于产生各种定时和计数中断。

  • 使用时需要在 bsp.h 打开RTC宏定义才能用
//包含头文件【在lx1x-drv--include--里】
#include "ls1x_rtc.h"

//初始化RTC设备
//参数1:总是 NULL
//参数2:如果该参数不是 NULL, 其值用于初始化RTC系统时间(一般是NULL)
//返回值:成功返回0 失败无返回
int LS1x_RTC_init(void *dev, void *arg)
    
//设置RTC时间  
int ls1x_rtc_set_datetime(struct tm *dt)    
    
//读取RTC时间
int ls1x_rtc_get_datetime(struct tm *dt) 
rtc.h
#ifndef _RTC_H
#define _RTC_H
#include "allhead.h"

void RTC_init(void);
void Get_Rtc(void);
#endif // _RTC_H
rtc.c
#include "allhead.h"

//结构体
struct tm tmp,now =
{
    .tm_hour = 8,
    .tm_min = 47,
    .tm_sec = 30,
    .tm_year = 2023,
    .tm_mon = 2,
    .tm_mday = 28,
};

//初始化RTC
void RTC_init(void)
{
    ls1x_rtc_init(NULL,NULL);
    ls1x_rtc_set_datetime(&now);    //设置RTC时间
}

//获取RTC时间
void Get_Rtc(void)
{
    unsigned char temp[20] = "";
    
    ls1x_rtc_get_datetime(&tmp);
    sprintf((char*)temp,"%02d-%02d-%02d",tmp.tm_hour,tmp.tm_min,tmp.tm_sec);
}

LCD


//初始化并打开framebuffer驱动
int fb_open(void);

//设置屏幕输出使用的背景色
void fb_set_bgcolor(unsigned coloridx,unsigned value);

//设置屏幕输出使用的前景色
void fb_set_fgcolor(unsigned coloridx,unsigned value);

//在指定位置删除文本
void fb_textout(int x,int y,char *str);

//清屏函数
void fb_cons_clear(void);

//在指定坐标处用指定颜色显示字符串
void fb_put_string(int x,int y,char *str,unsigned coloridx);

//输出显示一个汉字
void fb_draw_gb2312_char(int x,int y,unsigned char *str);

//在指定坐标处画一个像素点
void fb_drawpixel(int x,int y,unsigned coloridx);

//在指定坐标处画一个点,支持两种大小
void fb_drawpoint(int x,int y,int thickness,unsigned coloridx);

//根据两个指定的坐标画直线
void fb_drawline(int x1,int y1,int x2,int y2,unsigned coloridx);

//根据指定的坐标画矩形框
void fb_drawrect(int x1,int y1,int x2,int y2,unsigned coloridx);

//根据指定的坐标填充矩形
void fb_fillrect(int x1,int y1,int x2,int y2,unsigned coloridx);

//在指定坐标处显示图片(图片需取模)
void display_pic(unsigned int cpos,unsigned int ypos,unsigned int x1,unsigned int y1,unsigned char *ptrs);
  • 取模软件:Image2LCD

  • 使用时需要在 bsp.h 打开FB宏定义才能用
lcd.c
#include "lcd.h"

#if 0

//LCD初始化
void LCD_init(void)
{
    //设置LCD显示模式
    char LCD_display_mode[] = LCD_480x800;
    //初始化并打开framebuffer驱动
    fb_open();
    //设置字符输出使用的背景色
    fb_set_bgcolor(cidxWHITE,clWHITE);
    //显示背景色
    fb_cons_clear();
    //设置字符输出使用的前景色
    fb_set_fgcolor(cidxBRTRED,clbRED);
    //在坐标(200,400)显示字符串
    fb_textout(200,400,buf);
}

//显示字符串
void LCD_Dis(void)
{
    unsigned char buf[] = "Hello World";
    //在坐标(150,400)显示字符串
    fb_textout(150,400,buf);
    //在坐标(150,200)处指定颜色打印字符串
    fb_put_string(150,200,buf,cidxGREEN);
    //在坐标(150,500)输出一个汉字
    fb_draw_gb2312_char(150,500,"龙");
}

//画图
void LCD_Draw(void)
{
    //在LCD[x,y]处指定颜色画像素
    fb_drawpixel(280,32,cidxBLUE);
    //在LCD[x,y]处指定颜色,宽度画点--支持两种宽度
    fb_drawpoint(300,32,1,cidxBLUE);
    fb_drawpoint(300,32,2,cidxBLUE);
    //在LCD[x1,y1]~[x2,y2]处指定颜色画线
    fb_drawline(36,64,300,64,cidxYELLOW);   //水平线
    fb_drawline(0,0,478,798,cidxYELLOW);    //对角线
    //在LCD[x1,y1]~[x2,y2]处指定颜色画矩形框
    fb_drawrect(0,0,478,798,cidxBRTCYAN);
    //在LCD[x1,y1]~[x2,y2]处指定颜色填充矩形框
    fb_fillrect(100,100,250,250,cidxBRTCYAN);
}

//显示图片
void LCD_Image(void)
{
    display_pic(45,50,400,400,gImage_2);
}

#endif

PWM

  • LS1B芯片集成了四路脉冲宽度调节/计数控制器(PWM),每路PWM工作和控制方式完全相同。每路控制器有四个寄存器,分别是: 主计数器(CNTR)高脉冲定时参考寄存器(HRC)低脉冲定时参考寄存器(LRC)控制寄存器(CTRL)
  • 使用时需要在 bsp.h 打开PWM宏定义才能用
//初始化IO,设置IO方向
//参数1:devPWM0/devPWM1/devPWM2/devPWM3
//参数2:mode==PWM_SINGLE_PULSE/PWM_CONTINUE_PULSE(单脉冲/持续脉冲)
int ls1x_pwm_pulse_start(void *pwm,pwm_cfg_t *cfg)
    
//停止PWM设备产生脉冲
//参数:devPWM0/devPWM1/devPWM2/devPWM3  
//返回值:成功返回0    
int ls1x_pwm_pulse_stop(void *pwm)    
//需要定义结构体

xxx.hi_ns(设置PWM高电平时间)
xxx.lo_ns(设置PWM低电平时间)
xxx.mode(设置模式)
xxx.isr(设置中断服务函数)
xxx.cb(设置回调函数)
  • 由于LED只有LED3,4有PWM功能故拿它做实验
pwm.h
#ifndef _PWM_H
#define _PWM_H
#include "allhead.h"

void PWM_init(void);
void pwm_test(void);
#endif // _PWM_H
pwm.c
#include "pwm.h"

unsigned int period = 5000; //周期为5000ns
pwm_cfg_t cfg;

//PWM初始化
void PWM_init(void)
{
    //中断服务函数无
    cfg.isr = NULL;
    //回调函数无
    cfg.cb = NULL;
    //连续脉冲模式
    cfg.mode = PWM_CONTINUE_PULSE;
}

//PWM测试
void pwm_test(void)
{
    static unsigned char dir = 1;
    static unsigned char count = 1;
    
    if(dir)
    {
        count++;
    }
    else
    {
        count--;
    }
    printk("count=%d\n",count);
    cfg.hi_ns = period - count*100; //PWM高电平时间
    cfg.lo_ns = count*100;  //PWM低电平时间
    ls1x_pwm_pulse_start(devPWM2,&cfg); //启PWM
    delay_ms(20);
    ls1x_pwm_pulse_stop(devPWM2);   //停止PWM
    if(49 == dir)
    {
        dir = 1;
    }
    if(1 == dir)
    {
        dir = 49;
    }
}

超声波

测距芯片CS100A

CS100A是一款工业级超声波测距芯片,CS100A内部集成超声波发射电路,超声波接收电路,数字处理电路等,单芯片即可完成超声波测距,测距结果通过脉宽的方式进行输出。CS100A配合使用 40KHZ 的开放式超声波探头,只需要一个 15MR 的下拉电阻(建议取值范围5.1MR~22MR)和8M的晶振,即可实现高性能测距功能。

TRIG:TRIG引脚是触发引脚的缩写,它是用于发出超声波信号的引脚

ECHO:ECHO引脚是回波引脚的缩写,它是用于接收超声波信号的引脚

计算公式:

distance=CNT/33.034/40distance = CNT/33.0*34/40

CNT:表示发射到接收到反射超声波波脉冲的时间,单位为ms

33.0:表示1ms的时间

34:表示超声波的传播速度,单位为M/ms

40:表示超声波的发射和接收的距离之差(也可以是其他值但是这个值结果最准确),单位为cm

硬件连接

管脚名称 对应管脚序号 用途
UART1_RTS/P1_TXD2 14 TRIG
UART1_CTS/P1_TXD3 13 ECHO
  • 注意程序里宏定义不要跟系统重复命名,ECHO 已经有宏定义了所以需要换成别的名字

程序编写

cs100a.h
#ifndef _CS100A_H
#define _CS100A_H
#include "allhead.h"

//引脚定义
#define CS_TRIG 14
#define CS_ECHO 13


void CS100A_init(void);
float CS100A_Get_Dist(void);
#endif // _CS100A_H
cs100a.c
//超声波实验
#include "cs100a.h"

unsigned char CS100A_Over_Flag = 0;  //超声波接收触发标志位
unsigned int cnt = 0;   //超声波计数

//超声波中断服务函数
void ECHO_IRQhandler(int IRQn, void *arg)
{
    CS100A_Over_Flag = 1;
    cnt++;
}

//超声波初始化
void CS100A_init(void)
{
    //初始化输入输出IO
    gpio_enable(CS_TRIG,DIR_OUT);
    gpio_enable(CS_ECHO,DIR_IN);
    //配置超声波接收IO中断 配置触发方式为高电平触发
    ls1x_install_gpio_isr(CS_ECHO,INT_TRIG_LEVEL_HIGH,ECHO_IRQhandler,NULL);
    //开启gpio中断
    ls1x_enable_gpio_interrupt(CS_ECHO);
    //发送引脚默认设置为低电平
    gpio_write(CS_TRIG,0);
}

//超声波距离计算(单位:cm)
float CS100A_Get_Dist(void)
{
    float distance;
    
    //输出10us以上低电平触发测距
    gpio_write(CS_TRIG,1);
    delay_us(20);
    gpio_write(CS_TRIG,0);
    
    if(CS100A_Over_Flag)
    {
        delay_ms(100);
        distance = cnt/33.0*34/40;
        CS100A_Over_Flag = 0;
        cnt = 0;
        return distance;
    }
    return 0;
}

IIC

  • 舵机驱动是由 GP7101 芯片提供PWM信号,IIC通信,将IIC协议输入的数据线性转换成占空比为0%~100%的PWM信号,头文件 gp7101.h

  • 需要在 bsp.h 打开宏定义 #define GP7101_DRV

//GP7101挂接在I2CO上,I2C地址和通信速率定义如下(在gp7101.c有定义)
#define GP7101_ADDRESS          0x58	//7位地址

#define GP7101_BAUDRATE         1000000	//100K

//写数据
int GP7101_write(void *dev,void *buf,int size,void *arg);

//设置PWM
//参数:brightpercent:1~100
int set_lcd_brightness(void *bus,int brightpercent);

//初始化
ls1x_IIC_initialize(busIIC1);

//起始信号
ls1x_IIC_send_start(busIIC1,GP7101_ADDRESS);
//发送地址
ls1x_IIC_send_addr(busIIC1,GP7101_ADDRESS,false);
//写入数据
ls1x_IIC_write_bytes(busIIC1,data,3);
//停止信号
ls1x_IIC_send_stop(busIIC1,GP7101_ADDRESS);

定时器

  • 龙芯]1B200内部是没有定时器的,只能通过 PWM模拟定时器RTC计时中断 两种方法实现计数

PWM模拟定时器

//在 bsp.h 中打开 PWM2 的宏定义,并添加头文件 ls1x_pwm.h
//回调函数格式可在头文件看到
//定义pwm_cfg_t结构体配置参数
pwm_cfg_t cfg;
cfg.cb = pwmtimer_callback;
cfg,isr = NULL;
cfg.mode = PWM_CONTINUE_TIMER;	//脉冲持续产生
cfg.hi_ns = 5000000;	//5ms
cfg.lo_ns = 0;	//定时器模式不用到lo_ns
ls1x_pwm_init(devPWM2,NULL);
ls1x_pwm_open(devPWM2,(void*)&pwm2_cfg);
ls1x_pwm_timer_start(devPWM2,(void*)&cfg);

//回调函数
//pwm定时器定时的时间有些值是无法使用的具体需要自己测试
void pwmtimer_callback(void *pwm,int *stopit)
{
    static unsigned int count;
    count++;
    if(100 == count)
    {
        count = 0;
        ...
    }
}

RTC计时中断

//RTC开启定时器模式
void Timer1ms(void)
{
    ls1x_rtc_init(NULL,NULL);
    
    //开启RTC定时中断功能
    rtc_cfg_t rtc;
    rtc.cb = rtctimer_callback;	//定时器中断回调函数
    rtc.isr = NULL;	//中断服务函数
    rtc.interval_ms = 1000;	//定时器时间间隔(ms)
    rtc.trig_datetime = NULL;	//用于toymatch的日期
    //开启定时器
    ls1x_rtc_timer_start(DEVICE_RTCMATCHO,&rtc);
}

void rtctimer_callback(int device, unsigned match, int *stop)
{
    static unsigned int second;
    second++;
    
    if(10 == second)	//10s
    {
        second = 0;
        ...
    }
}

ADC

my.h
void ADC_init(void)
{
    //初始化MCP4725(DAC才需要)
    ls1x_mcp4725_ioctl(busI2C0,IOCTL_MCP4725_DISP_CONFIG_REG,NULL);
    //初始化ADS1015
    ls1x_ads1015_ioctl(busI2C0,IOCTL_ADS1015_DISP_CONFIG_REG,NULL);
}

//获取电压
void Get_V(void)
{
    unsigned short adc1,adc2;   //存储读取的值
    float adc_v1,adc_v2;
    
    //读取通道0
    adc1 = get_ads1015_adc(busI2C0,ADS1015_REG_CONFIG_MUX_SINGLE_0);
    //读取通道1
    adc2 = get_ads1015_adc(busI2C0,ADS1015_REG_CONFIG_MUX_SINGLE_1);
    adc_v1 = 4.096*2*adc1/4096;
    adc_v2 = 4.096*2*adc2/4096;
}

DAC

//需要在 bsp.h 打开宏定义 #define MCP4725_DRV
//MCP4725 挂接在 I2C0 上,I2C 地址和通信速率定义如下
#define MCP4725_ADDRESS			0x62	    /* mcp4725 address 1100 010(7bits) */

#define MCP4725_BAUDRATE        1000000

//包含头文件
#include "ls1x_i2c_bus.h"
#include "i2c/mcp4725.h"	

//初始化MCP4725
ls1x_mcp4725_ioctl(busI2C0,IOCTL_MCP4725_DISP_CONFIG_REG,NULL);

set_mcp4725_dac(busI2C0,dac);
out_v = 3.3*dac/4096;	//输出电压公式

int set_mcp4725_dac(void *bus,unsigned short dacVal)
{
    //向MCP4725写 2字节数值进行DAC转换
    return MCP4725_write(bus,(void*)&dacVal,2,NULL);
}

adc = get_ads1015_adc(busI2C0,ADS1015_REG_CONFIG_MUX_SINGLE_3);
in_v = 4.096*2*adc/4096;

待解决

  • ls1x_uart_write(devUART5,RX_BUFF,8,NULL);这个函数里面的参数是不是8位无停止位,待有板子时测试一下
  • rtc代码错误不行【在bsp.h打开RTC即可】
  • 打开宏定义BSP_USE_FB报错