首页 > 代码库 > openwrt-安装-驱动-应用-lcd2004a实验

openwrt-安装-驱动-应用-lcd2004a实验

1. 板子f403tech的RT5350的板子和

(1)openWRT系统的定义和特点
        OpenWrt是一个高度模块化、高度自动化的嵌入式Linux系统,拥有强大的网络组件,常常被
用于工控设备、电话、小型机器人、智能家居、路由器以及VOIP设备中。
        OpenWrt支持各种处理器架构,无论是对ARM,X86,PowerPC或者MIPS都有很好的支持。
        其多达3000多种软件包,囊括从工具链(toolchain),到内核(linux kernel),到软件包
(packages),再到根文件系统(rootfs)整个体系,使得用户只需简单的一个make命令即可方便快
速地定制一个具有特定功能的嵌入式系统来制作固件。 其模块化设计也可以方便的移植各类功能
到OpenWrt下,加快开发速度。

2 实验mjpeg-streamer、uvc视频监控

3 openwrt的下载安装

<pre name="code" class="cpp">sudo apt-get install subversion
sudo apt-get install git-core
sudo apt-get install gcc g++ binutils patch bzip2 flex bison make autoconf gettext texinfo unzip sharutils zlib1g-dev libncurses5-dev gawk
mkdir openwrt
cd openwrt/
svn co svn://svn.openwrt.org/openwrt/trunk
cd trunk/
./scripts/feeds update -a
./scripts/feeds install -a
(4) 配置编译openWRT系统
a、选择CPU型号
TargetSystem —> RalinkRT288x/RT3xxx
b、选择CPU子型号
Subtarget —> RT3x5x/RT5350basedboards
c、选择具体路由器型号
Targetprofile—>HAME-MPR-A2
(5) 编译
make V=99
至此,完成内核和文件系统镜像编译


4 驱动开发流程
进入/home/openwrt/trunk/package/kernel驱动目录,仿照参考其他的驱动。

新建example文件夹,进入example文件夹。

创建Makefile:
#
# Copyright (C) 2008-2012 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
# modify by 2014-10-23

include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk

PKG_NAME:=example
PKG_RELEASE:=1

include $(INCLUDE_DIR)/package.mk

define KernelPackage/example
  SUBMENU:=Other modules
#  DEPENDS:=@!LINUX_3_3
  TITLE:=Simple example driver
  FILES:=$(PKG_BUILD_DIR)/example.ko
#  AUTOLOAD:=$(call AutoLoad,30,gpio-button-hotplug,1)
  KCONFIG:=
endef

define KernelPackage/example/description
 This is a example for the following in-kernel drivers:
 1) example one
 2) example two
endef

MAKE_OPTS:= \
        ARCH="$(LINUX_KARCH)" \
        CROSS_COMPILE="$(TARGET_CROSS)" \
        SUBDIRS="$(PKG_BUILD_DIR)"

define Build/Prepare
        mkdir -p $(PKG_BUILD_DIR)
        $(CP) ./src/* $(PKG_BUILD_DIR)/
endef

define Build/Compile
        $(MAKE) -C "$(LINUX_DIR)" \
                $(MAKE_OPTS) \
                modules
endef

$(eval $(call KernelPackage,example))

新建srce文件夹,进入src文件夹。创建Makefile:obj-m += example.o创建example.c源文件#include <linux/module.h>#include <linux/version.h>#include <linux/kmod.h>static int __init example_init(void){        printk("hello example openwrt\n");        return 0;}static void __exit example_exit(void){        printk("hello example openwrt exit\n");}module_init(example_init);module_exit(example_exit);MODULE_AUTHOR("zhaochuang8888@126.com");MODULE_DESCRIPTION("example driver");MODULE_LICENSE("GPL");MODULE_ALIAS("platform:" DRV_NAME);c.编译驱动的命令make package/kernel/example/compile V=99<pre name="code" class="cpp"><pre name="code" class="cpp">在/home/openwrt/trunk/bin/ramips/packages/base目录生成:kmod-example_3.14.18-1_ramips_24kec.ipk 
5 应用开发流程
进入/home/openwrt/trunk/package应用目录。参考其他的应用文件。
创建helloworld文件夹,并进入。
创建Makefile:
##############################################
# OpenWrt Makefile for helloworld program
#
#
# Most of the variables used here are defined in
# the include directives below. We just need to
# specify a basic description of the package,
# where to build our program, where to find
# the source files, and where to install the
# compiled program on the router.
#
# Be very careful of spacing in this file.
# Indents should be tabs, not spaces, and
# there should be no trailing whitespace in
# lines that are not commented.
#
##############################################

include $(TOPDIR)/rules.mk

# Name and release number of this package
PKG_NAME:=helloworld
PKG_RELEASE:=1


# This specifies the directory where we‘re going to build the program.
# The root build directory, $(BUILD_DIR), is by default the build_mipsel
# directory in your OpenWrt SDK directory
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)


include $(INCLUDE_DIR)/package.mk

 

# Specify package information for this program.
# The variables defined here should be self explanatory.
# If you are running Kamikaze, delete the DESCRIPTION
# variable below and uncomment the Kamikaze define
# directive for the description below
define Package/helloworld
    SECTION:=utils
    CATEGORY:=Utilities
    TITLE:=Helloworld -- prints a snarky message
endef


# Uncomment portion below for Kamikaze and delete DESCRIPTION variable above
define Package/helloworld/description
        If you can‘t figure out what this program does, you‘re probably
        brain-dead and need immediate medical attention.
endef

 

# Specify what needs to be done to prepare for building the package.
# In our case, we need to copy the source files to the build directory.
# This is NOT the default.  The default uses the PKG_SOURCE_URL and the
# PKG_SOURCE which is not defined here to download the source from the web.
# In order to just build a simple program that we have just written, it is
# much easier to do it this way.
define Build/Prepare
    mkdir -p $(PKG_BUILD_DIR)
    $(CP) ./src/* $(PKG_BUILD_DIR)/
endef


# We do not need to define Build/Configure or Build/Compile directives
# The defaults are appropriate for compiling a simple program such as this one


# Specify where and how to install the program. Since we only have one file,
# the helloworld executable, install it by copying it to the /bin directory on
# the router. The $(1) variable represents the root directory on the router running
# OpenWrt. The $(INSTALL_DIR) variable contains a command to prepare the install
# directory if it does not already exist.  Likewise $(INSTALL_BIN) contains the
# command to copy the binary file from its current location (in our case the build
# directory) to the install directory.
define Package/helloworld/install
    $(INSTALL_DIR) $(1)/bin
    $(INSTALL_BIN) $(PKG_BUILD_DIR)/helloworld $(1)/bin/
endef


# This line executes the necessary commands to compile our program.
# The above define directives specify all the information needed, but this
# line calls BuildPackage which in turn actually uses this information to
# build a package.
$(eval $(call BuildPackage,helloworld))
创建src文件夹,并进入。
创建Makefile:
# build helloworld executable when user executes "make"

helloworld: helloworld.o
        $(CC) $(LDFLAGS) helloworld.o -o helloworld

helloworld.o: helloworld.c
        $(CC) $(CFLAGS) -c helloworld.c

# remove object files and executable when user executes "make clean"
clean:
        rm *.o helloworld
创建helloworld.c:
#include<stdio.h>
int main(void)
{
        printf("helloworld\n");
        return 0;
}

make package/helloworld/motor/compile V=99
在/home/openwrt/trunk/bin/ramips/packages/base目录生成:helloworld_1_ramips_24kec.ipk


5 LCD2004实验

lcd_204a_drv.c 驱动源程序:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/device.h>

#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>

static struct class *lcddrv_class;
static struct device *lcddrv_class_dev;

volatile unsigned long *GPIOMODE;
volatile unsigned long *I2C_CONFIG;
volatile unsigned long *I2C_CLKDIV;
volatile unsigned long *I2C_DEVADDR;
volatile unsigned long *I2C_ADDR;
volatile unsigned long *I2C_DATAOUT;
volatile unsigned long *I2C_STATUS;
volatile unsigned long *I2C_STARTXFR;
volatile unsigned long *I2C_BYTECNT;

static int lcd_204a_open(struct inode *inode, struct file *file)
{
    return 0;
}

static ssize_t lcd_204a_write(struct file *file, const char __user *buf, size_t size, loff_t *ppos)
{
    unsigned char val;

    copy_from_user(&val, buf, 1);

    *I2C_CONFIG = (0x6<<2)|(1<<1);                // 设备地址是7bit、不用发地址
    *I2C_DEVADDR = 0x20;                        // 设备地址为0x20

    *I2C_DATAOUT = val;

    *I2C_STARTXFR = 0x0;

    while(*I2C_STATUS & 0x1);
    
    return 1;
}

static struct file_operations sencod_drv_fops = {
    .owner        = THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open        = lcd_204a_open,
    .write        = lcd_204a_write,
};

int major;
static int lcd_204a_init(void)
{
    major = register_chrdev(0, "lcd_204a", &sencod_drv_fops);

    lcddrv_class = class_create(THIS_MODULE, "lcd");
    lcddrv_class_dev = device_create(lcddrv_class, NULL, MKDEV(major, 0), NULL, "lcd"); /* /dev/204a */
    
    /* 映射相应的寄存器的地址 */
    GPIOMODE = (volatile unsigned long *)ioremap(0x10000060, 4);
    I2C_CONFIG = (volatile unsigned long *)ioremap(0x10000900, 4);
    I2C_CLKDIV = (volatile unsigned long *)ioremap(0x10000904, 4);
    I2C_DEVADDR = (volatile unsigned long *)ioremap(0x10000908, 4);
    I2C_ADDR = (volatile unsigned long *)ioremap(0x1000090C, 4);
    I2C_DATAOUT = (volatile unsigned long *)ioremap(0x10000910, 4);
    I2C_STATUS = (volatile unsigned long *)ioremap(0x10000918, 4);
    I2C_STARTXFR = (volatile unsigned long *)ioremap(0x1000091C, 4);
    I2C_BYTECNT = (volatile unsigned long *)ioremap(0x10000920, 4);

    /* 开始设置这些寄存器 */
    *GPIOMODE &= ~(1<<0);                        // I2C_GPIO_MODE
    *I2C_CLKDIV = 0x200;                            // IIC总线时钟 = 40MHz / 512 = 79KHz
    
    return 0;
}

static void lcd_204a_exit(void)
{
    unregister_chrdev(major, "lcd_204a");
    device_unregister(lcddrv_class_dev);
    class_destroy(lcddrv_class);
    iounmap(GPIOMODE);
    iounmap(I2C_CONFIG);
    iounmap(I2C_CLKDIV);
    iounmap(I2C_DEVADDR);
    iounmap(I2C_ADDR);
    iounmap(I2C_DATAOUT);
    iounmap(I2C_STATUS);
    iounmap(I2C_STARTXFR);
    iounmap(I2C_BYTECNT);
}

module_init(lcd_204a_init);
module_exit(lcd_204a_exit);

MODULE_LICENSE("GPL");

lcd2004的应用程序:


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
//根据LCD2004A和PCF8574T的引脚确定
//RS:指令/数据 寄存器选择  RS = 0 : 指令寄存器 RS = 1 : 数据寄存器   根据原理图得:RS 与 PCF8574T的PO链接
//RW:读/写 选择   R/W = 0 : 写     R/W = 1 : 读  根据原理图得:RW 与 PCF8574T的P1链接
//CS:允许信号    表示从高电平到低电平,
#define DATA(rs, rw, cs, data) (data<<4)|(cs<<2)|(rw<<1)|rs

int fd;

static void lcd_write_command(unsigned char command)
{
    unsigned char data;

    /* 1.发送命令的高4位 */
    data = http://www.mamicode.com/DATA(0, 0, 1, (command>>4));
    printf("h data = http://www.mamicode.com/%d/n", data);
    write(fd, &data, 1);
    usleep(20);
    data = http://www.mamicode.com/DATA(0, 0, 0, (command>>4));
    printf("h data = http://www.mamicode.com/%d/n", data);
    write(fd, &data, 1);

    /* 2.发送命令的低4位 */
    data = http://www.mamicode.com/DATA(0, 0, 1, (command&0x0f));
    printf("l data = http://www.mamicode.com/%d/n", data);
    write(fd, &data, 1);
    usleep(20);
    data = http://www.mamicode.com/DATA(0, 0, 0, (command&0x0f));
    printf("l data = http://www.mamicode.com/%d/n", data);
    write(fd, &data, 1);
}

static void lcd_write_data(unsigned char wdata)
{
    unsigned char data;

    /* 1.发送命令的高4位 */
    data = http://www.mamicode.com/DATA(1, 0, 1, (wdata>>4));
    printf("h data = http://www.mamicode.com/%d/n", data);
    write(fd, &data, 1);
    usleep(20);
    data = http://www.mamicode.com/DATA(1, 0, 0, (wdata>>4));
    printf("h data = http://www.mamicode.com/%d/n", data);
    write(fd, &data, 1);

    /* 2.发送命令的低4位 */
    data = http://www.mamicode.com/DATA(1, 0, 1, (wdata&0x0f));
    printf("l data = http://www.mamicode.com/%d/n", data);
    write(fd, &data, 1);
    usleep(20);
    data = http://www.mamicode.com/DATA(1, 0, 0, (wdata&0x0f));
    printf("l data = http://www.mamicode.com/%d/n", data);
    write(fd, &data, 1);
}

static void lcd_set_xy(unsigned char x, unsigned char y)
{
    unsigned char address;

    if (y == 0)
        address = 0x80 + x;
    else
        address = 0xc0 + x;
    
    lcd_write_command(address);
}

/*
    函数名:
        lcd_write_char()
    功能:
        列x=0~15,行y=0,1
    用法:
*/
static void lcd_write_char(unsigned char X,unsigned char Y,unsigned char Recdata)
{
    lcd_set_xy(X, Y);
    lcd_write_data(Recdata);
}

static int lcd_PutStr(unsigned char *DData,int pos)
{
    unsigned char i;
    
    if(pos==-1)
    {
        lcd_write_command(0x01); //清屏
        usleep(2);
        pos=0;
    }
    
    while((*DData)!=‘\0‘)
    {
        switch(*DData)
        {
            case ‘\n‘: //如果是\n,则换行   
            {
                if(pos<21)
                {
                    for(i=pos;i<20;i++)
                    lcd_write_char(i%20, i/20, ‘ ‘);
                    pos=20;
                }
                else
                {
                    for(i=pos;i<40;i++)
                        lcd_write_char(i%20, i/20, ‘ ‘);
                    pos=40;
                }
                break;
            }

            case ‘\b‘: //如果是\b,则退格   
            {
                if(pos>0)
                    pos--;
                lcd_write_char(pos%20, pos/20, ‘ ‘);
                break;
            }

            default:
            {
                if((*DData)<0x20)
                {
                    *DData=http://www.mamicode.com/‘ ‘;
                }

                lcd_write_char(pos%20, pos/20,*DData);
                pos++;
                break;
            }
        }  
        DData++;
    }

    return(pos);
}

//从右边数,保留几位小数
static int lcd_PutNum(unsigned long num,int XS,int pos)
{
    unsigned long tmp=0;
    unsigned char numbits=0;
    
    if(pos==-1)
    {
        lcd_write_command(0x01);
        usleep(2);
        pos=0;
    }
    
    if(num==0)
    {
        lcd_write_char(pos%20, pos/20, ‘0‘);
        pos++;
    }
    else
    {
        if(num<0)
        {
            lcd_write_char(pos%20, pos/20, ‘-‘);
            num*=(-1);
            pos++;
        }

        while(num)
        {
            tmp=tmp*10+(num%10);
            num=num/10;
            numbits++;
        }
        
        while(tmp)
        {
            lcd_write_char(pos%20, pos/20, (tmp%10)+48);
            tmp=tmp/10;
            pos++;
            numbits--;
            if(numbits==XS)
                pos=lcd_PutStr(".",pos); //显示小数点
        }
        
        while(numbits--)
        {
            lcd_write_char(pos%20, pos/20, ‘0‘);
            pos++;
        }
    }
    
    return(pos);
}

void lcd_put_char(unsigned char ch, unsigned int pos , int flag)
{
    if(flag == 0)
    {
        lcd_write_command(0x80+pos);
    }
    if(flag == 1)
    {
        lcd_write_command(0xc0+pos);
    }
    lcd_write_data(ch);
}

void lcd_put_str(const char *str, int flag)
{
    int cnt=0;
    while(*str !=‘\0‘)
    {
        lcd_put_char(*str, cnt, flag);
        cnt++;
        str++;
    }
}
/* seconddrvtest
  */
int main(int argc, char **argv)
{
    int p;
    
    fd = open("/dev/lcd", O_RDWR);
    if (fd < 0)
    {
        printf("can‘t open!\n");
    }

    lcd_write_command(0x28);
    usleep(1000);
    lcd_write_command(0x28);
    usleep(1000);
    lcd_write_command(0x28);
    usleep(1000);

    lcd_write_command(0x0e);            // 显示开
    usleep(1000);
    lcd_write_command(0x01);            // 清屏
    usleep(1000);
    
//    lcd_write_char(0, 0, "a");

    int i = 0;
    for(i=0; i<26; i++)
    {
        lcd_write_command(0x80+i);
        lcd_write_data(‘a‘+i);
    }
    for(i=0; i<30; i++)
    {
        lcd_write_command(0xc0+i);
        lcd_write_data(‘2‘+i);
    }

    
     sleep(10);    
    lcd_write_command(0x01);                        // 清屏
        usleep(1000);
    lcd_put_str("Welcome RT5350", 0);
    lcd_put_str("        --By Dreeam", 1);
    //p = lcd_PutStr("RT5350 By F403tech!\n",0);    // 显示一段文字
    //usleep(1000);
    //p = lcd_PutStr("f403tech.taobao.com\n",p);    // 显示一段文字
    //lcd_PutNum(1234,2,p);                // 显示12.34这个数

    return 0;
}


a.软件包管理常用命令
opkg install xxx.ipk                        // 安装指定的软件包
opkg remove xxx.ipk                            // 卸载已经安装过的指定的软件包
opkg list                                                // 获取软件列表

生成的驱动位于 /lib/modules/3.14.18/目录下。
<pre name="code" class="cpp">生成的应用位于 /bin目录下。


opkg help











openwrt-安装-驱动-应用-lcd2004a实验