概览

本次使用Altera公司的NIOS II软核。

使用Quatus工具生成BSP并利用BSP打包工具生成UCOSII嵌入环境。

手动书写LCD驱动与显示函数,对UCOS II加入简单图像显示接口。

./
├── create-this-app
├── driver #板子的具体驱动(非操作系统)
│ ├── init.h #初始化
│ ├── irs.h #中断处理
│ ├── lcd.h #LCD驱动
│ ├── sys.h #系统驱动
│ └── tools.h #工具
├── lib #显示库
│ ├── ansii_lib.h
│ ├── cn_lib.h
│ ├── color.h
│ └── values.h
├── Makefile
├── obj
│ └── default
│ ├── sys_kernel.d
│ └── sys_kernel.o
├── readme.txt
├── sys_kernel.c #系统的主函数
├── sys_user_interface.elf
├── sys_user_interface.map
├── sys_user_interface.objdump
└── tasks #任务文件夹
├── task1.h
└── task2.h

NIOS II软核生成

由于这次没有加载其余IP核,这次的软核非常简单,没有预留过多IP核PIO接口:

软核工程中包含以下内容:

  • NIOS II processer
  • 软核时钟信号
  • 外部存储器接口
  • SPI总线接口
  • 一个控制LCD屏幕亮度的接口
  • 一个软核版本控制器

若是有其余IP核需要加入,则需要单独的PIO进行交互,或者选择其余总线协议与IP核交互。

总线的速度非常慢,在软核中推荐使用可编程布线来进行交互。

具体软核IP核交互可以参考我的博客中的DES核与卷积核,这里不做过多描述。

FPGA工程概览

其中由于板载频率问题,加入了PLL

然后右边接了一个处理LCD的亮度的PWM模块也非常简单,不做过多描述。

UCOS II环境配置

由于这里使用的是Altera公司提供的NIOS II软核,其有完整的BSP与系统移植。

这里只需要根据选择软核生成对于此板子的BSP。

选择一个UCOS的系统工程即可。(还提供了其他的RTOS)

LCD 驱动书写

显示器驱动

  • LCD显示屏初始化

    void LCD_init() {
    //************* Reset LCD Driver ****************//
    IOWR_ALTERA_AVALON_PIO_DATA(LCD_RESET_BASE, 1);
    delay_ms(200);
    IOWR_ALTERA_AVALON_PIO_DATA(LCD_RESET_BASE, 0);
    delay_ms(200);
    IOWR_ALTERA_AVALON_PIO_DATA(LCD_RESET_BASE, 1);
    delay_ms(10);
    //************* Start Initial Sequence **********//
    //剩余参数配置见源代码,这里不予展示
    }

    LCD初始化可以直接利用芯片厂商提供的代码,或者参考芯片资料中的参数配置自行完成。

  • LCD显示驱动

    查阅ILI9481芯片手册,可以将发送一次指令和内容打包成Indexcmd两个函数。

    这两个函数内容如下:

    void LCD_ILI9481_INDEX(unsigned int data) {
    IOWR_ALTERA_AVALON_PIO_DATA(LCD_RS_BASE, 1);
    IOWR_ALTERA_AVALON_PIO_DATA(LCD_WR_BASE, 0);
    IOWR_ALTERA_AVALON_PIO_DATA(LCD_DATA_BASE, data);
    IOWR_ALTERA_AVALON_PIO_DATA(LCD_WR_BASE, 1);
    }
    void LCD_ILI9481_CMD(unsigned int data) {
    IOWR_ALTERA_AVALON_PIO_DATA(LCD_RS_BASE, 0);
    IOWR_ALTERA_AVALON_PIO_DATA(LCD_WR_BASE, 0);
    IOWR_ALTERA_AVALON_PIO_DATA(LCD_DATA_BASE, data);
    IOWR_ALTERA_AVALON_PIO_DATA(LCD_WR_BASE, 1);
    }

    发送一个指令的流程为先利用cmd函数发送指令,再用INDEX函数发送指令内容。

    再将LCD显示一个像素的图像打包为set_addrsend_data两个步骤,先通过set_addr发送像素地址,再用send_data发送像素颜色信息。

    send_addr的时序可以通过查阅芯片手册来得知,其代码如下:

    void set_addr(unsigned int x, unsigned int y){
    LCD_ILI9481_CMD(0x002b);
    LCD_ILI9481_INDEX(x >> 8);
    LCD_ILI9481_INDEX(x & 0x00ff);
    LCD_ILI9481_INDEX(0x0001);
    LCD_ILI9481_INDEX(0x00df); LCD_ILI9481_CMD(0x002a);
    LCD_ILI9481_INDEX(y >> 8);
    LCD_ILI9481_INDEX(y & 0x00ff);
    LCD_ILI9481_INDEX(0x0001);
    LCD_ILI9481_INDEX(0x003f); LCD_ILI9481_CMD(0x002c);
    }

    同样,可以写出send_data如下:

    void send_data(unsigned int data) {
    IOWR_ALTERA_AVALON_PIO_DATA(LCD_RS_BASE, 1);
    IOWR_ALTERA_AVALON_PIO_DATA(LCD_WR_BASE, 0);
    IOWR_ALTERA_AVALON_PIO_DATA(LCD_DATA_BASE, data);
    IOWR_ALTERA_AVALON_PIO_DATA(LCD_WR_BASE, 1);
    }
  • LCD显示工具

    ascii码显示工具(中文显示工具原理相同),利用lcd_buffer传参数,字母数据保存在word_libc

      void display_ascii(unsigned int x, unsigned int y, unsigned int w_color,
    unsigned int b_color) {
    unsigned int i, j, k = 0;
    unsigned char str;
    unsigned int OffSet, z; while (1) {
    if (lcd_buffer[k] == 0) {
    set_addr(0, 0);
    return;
    }
    z = lcd_buffer[k];
    //每个字符在wordlib中用11
    OffSet = z * 11;
    //显示一个字符 该字符的像素大小为
    for (i = 0; i < 11; i++) {
    //读取字符表示中的一个字符的一行
    str = word_lib[OffSet + i];
    for (j = 0; j < 8; j++) {
    //设置显示像素点的相对左边
    set_addr(x + j, y - i);
    //如果是要显示的话,就显示前景颜色
    if (str & 0x80) {
    send_data(w_color);
    } else {
    //如果是不显示的话,显示背景颜色
    send_data(b_color);
    }
    str <<= 1;
    }
    }
    x += 8;
    k++;
    }
    }

    通过如下方式利用该工具

      sprintf((char * )lcd_buffer, " Test ");
    display_ascii(12, 16, 0x0000, MENU_FULL_COLOR);

    图片显示则利用如上所述的set_addrsend_data完成,欢迎界面显示函数如下

    (图像利用工具转换后放在welcome数组之中)

中断处理

  • 时钟中断在此版本中暂时用于处理背光,中断的申请与定义可见各NIOS教程,处理函数如下

    void timer(void* context) {
    IOWR_ALTERA_AVALON_TIMER_STATUS(TIMER_BASE, 0);
    IOWR_ALTERA_AVALON_TIMER_CONTROL(TIMER_BASE, 0x0b); //控制LCD的背光灯
    if (LED_PWM_DATA <= 130) {
    LED_PWM_DATA += 1;
    IOWR_ALTERA_AVALON_PIO_DATA(PWM_LED_BASE, LED_PWM_DATA);
    }
    IOWR_ALTERA_AVALON_TIMER_CONTROL(TIMER_BASE, 0x07);
    }
  • 按键中断则是SPI总线来读取按键值

    void KeyListener(void* context) {
    KEY_IS_DOWN = !KEY_IS_DOWN;
    unsigned char i = 0;
    for (i = 0; i < 8; i++) {
    send_KEY(((0xfeff << i) >> 8) & 0xff);
    if (IORD_ALTERA_AVALON_PIO_DATA(KEY_PORT_BASE) != 0x03) {
    if (IORD_ALTERA_AVALON_PIO_DATA(KEY_PORT_BASE) == 1) {
    switch (i) {
    case 0:
    KEY_DATA = 8;
    break;
    //...
    //见源代码 省去
    }
    } else if (IORD_ALTERA_AVALON_PIO_DATA(KEY_PORT_BASE) == 2) {
    switch (i) {
    case 0:
    KEY_DATA = 0;
    break;
    //...
    //见源代码 省去
    }
    }
    break;
    }
    }
    send_KEY(0x00);
    if(KEY_IS_DOWN == 0){
    sprintf(lcd_buffer,"keytest");
    display_ascii(417,95,0x0000,0xEF78);
    sprintf(lcd_buffer,"%2d",KEY_DATA);
    display_ascii(417,80,0x0000,0xEF78);
    }
    IOWR_ALTERA_AVALON_PIO_EDGE_CAP(KEY_PORT_BASE, 0x0000);
    }

    SPI总线的驱动send_key见下

    void send_KEY(unsigned char data) {
    unsigned char u;
    IOWR_ALTERA_AVALON_PIO_DATA(SPI_LE_K_BASE, 0);
    //串行发送j的数据,通过移位发送j的数据
    for (u = 0; u < 8; u++) {
    if (data & 0x80) {
    IOWR_ALTERA_AVALON_PIO_DATA(SPI_DATA_BASE, 1);
    } else {
    IOWR_ALTERA_AVALON_PIO_DATA(SPI_DATA_BASE, 0);
    } IOWR_ALTERA_AVALON_PIO_DATA(SPI_CLK_BASE, 1); IOWR_ALTERA_AVALON_PIO_DATA(SPI_CLK_BASE, 0); data <<= 1;
    //左移发送信号
    }
    IOWR_ALTERA_AVALON_PIO_DATA(SPI_LE_K_BASE, 1);
    }

工具类

工具类主要提供了delay如下

void delay_ms(unsigned int i) {
unsigned int j, k;
for (j = 0; j < i; j++)
for (k = 0; k < 1000; k++);
}
void delay_us(unsigned int i) {
unsigned int j;
for (j = 0; j < i; j++);
}

系统初始化

cvoid SYS_init() {
LCD_init();
Init_background();
print_screen("============================================");
print_screen("= =");
print_screen("= Welcome to UCOS II based on NIOS II =");
print_screen("= =");
print_screen("= NIOS II Version : Liu Nian =");
print_screen("= =");
print_screen("============================================");
print_screen("Initial UCOS II");
sprintf((char *)lcd_buffer,"Initial interrupt ...");
display_ascii(1, LINE_Y, 0xffff, 0x0000);
//初始化中断服务
alt_irq_init (ALT_IRQ_BASE);
sprintf((char *)lcd_buffer,"DONE");
display_ascii(DONE_X, LINE_Y, DONE_COLOR, 0x0000);
LINE_Y = LINE_Y - 10; sprintf((char *)lcd_buffer,"Initial Key Listener ...");
display_ascii(1, LINE_Y, 0xffff, 0x0000);
KEY_init(); //初始化按键中断
sprintf((char *)lcd_buffer,"DONE");
display_ascii(DONE_X, LINE_Y, DONE_COLOR, 0x0000);
LINE_Y = LINE_Y - 10;
}

初始化用于测试显示驱动,与初始化中断处理。

int main (void){
SYS_init();
OSInit(); /* Initialize uC/OS-II */
OSTaskCreate(TaskStart, (void *)0, &TaskStartStk[TASK_STK_SIZE - 1], 0);
/*Create semaphore*/
mutex = OSSemCreate(1);
full = OSSemCreate(0);
empty = OSSemCreate(10);
OSStart(); /* Start multitasking */
return 0;
}

初始化系统也是直接调用sysinit(),然后交给Taskstart进行处理。

贪吃蛇移植

有了显示驱动,有了系统框架,移植贪吃蛇也就很快了。

#include <stdio.h>
#include "includes.h"
#include "driver/lcd.h"
#include "driver/init.h"
#include "driver/sys.h"
#include "tasks/task1.h"
#include "tasks/task2.h" /*
*********************************************************************************************************
* CONSTANTS
*********************************************************************************************************
*/ #define TASK_STK_SIZE 512 /* Size of each task's stacks (# of WORDs) */
#define TASK_0_ID 0
#define TASK_1_ID 1
#define TASK_PRIO 1
#define TASK_0_PRIO 3
#define TASK_1_PRIO 2 #define LEFT 0
#define FRONT 1
#define BACK 2
#define RIGHT 3 #define snake_maxlen 20
#define true 1
#define false 0 /*
*********************************************************************************************************
* VARIABLES
*********************************************************************************************************
*/ OS_STK TaskStartStk[TASK_STK_SIZE]; /* Task Start task stack */
OS_STK Task0Stk[TASK_STK_SIZE]; /* Task #0 task stack */
OS_STK Task1Stk[TASK_STK_SIZE]; /* Task #1 task stack */ /*
*********************************************************************************************************
* FUNCTION PROTOTYPES
*********************************************************************************************************
*/ void Task0(void *data); /* Function prototypes of tasks */
void Task1 (void *pdata);
void TaskStart(void *data); /* Function prototypes of Startup task */
static void TaskStartCreateTasks(void);
static void TaskStartDispInit(void);
static void TaskStartDisp(void); struct snake_node{
int node_x;
int node_y;
} snake[snake_maxlen]; /*$PAGE*/
/*
*********************************************************************************************************
* MAIN
*********************************************************************************************************
*/ /* Semaphores */
OS_EVENT *mutex;
OS_EVENT *full;
OS_EVENT *empty; int main (void){
SYS_init();
OSInit(); /* Initialize uC/OS-II */
OSTaskCreate(TaskStart, (void *)0, &TaskStartStk[TASK_STK_SIZE - 1], 0);
/*Create semaphore*/
mutex = OSSemCreate(1);
full = OSSemCreate(0);
empty = OSSemCreate(10);
OSStart(); /* Start multitasking */
return 0;
} /*
*********************************************************************************************************
* STARTUP TASK
*********************************************************************************************************
*/
OS_EVENT *mail1;
OS_EVENT *mail2; void TaskStart (void *pdata){
int need_new = 1;
TaskStartDispInit();
OSStatInit(); /* Initialize uC/OS-II's statistics */
TaskStartCreateTasks(); /* Create all the application tasks */
mail1 = OSMboxCreate(0);
mail2 = OSMboxCreate(0);
OSMboxPost(mail2,&need_new);
TaskStartDisp();
while(1) {
OSCtxSwCtr = 0; /* Clear context switch counter */
OSTimeDlyHMSM(0, 0, 1, 0); /* Wait one second */
}
} /*$PAGE*/
/*
*********************************************************************************************************
* INITIALIZE THE DISPLAY
*********************************************************************************************************
*/ static void TaskStartDispInit (void){
Init_background();
} static void TaskStartDisp (void){
} static void TaskStartCreateTasks (void){
OSTaskCreateExt(Task0, /*蛇*/
(void *)0,
&Task0Stk[TASK_STK_SIZE - 1],
TASK_0_PRIO,
TASK_0_ID,
&Task0Stk[0],
TASK_STK_SIZE,
(void *)0, /*无扩展*/
OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);//堆栈检查,堆栈清空
OSTaskCreateExt(Task1, /*豆子*/
(void *)0,
&Task1Stk[TASK_STK_SIZE - 1],
TASK_1_PRIO,
TASK_1_ID,
&Task1Stk[0],
TASK_STK_SIZE,
(void *)0, /*无扩展*/
OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);//堆栈检查,堆栈清空
} /*
*********************************************************************************************************
* My TASKS
*********************************************************************************************************
*/ int direction;
int count = 0; int find_way(int target_x,int target_y,int cur_x,int cur_y){
printf("FIND %D %D\n\n",target_x,target_y);
if(cur_x < target_x) return RIGHT;
else if(cur_x > target_x) return LEFT;
else if(cur_y < target_y) return FRONT;
else if(cur_y > target_y) return BACK;
return BACK; } int need_new = true; int target_x = 0;
int target_y = 0; void Task0 (void *pdata){
INT8U *err;
char s[40];
int length = 4;
int i = 0;
int need_grow = false; snake[0].node_x = 10;
snake[0].node_y = 10;
snake[1].node_x = 9;
snake[1].node_y = 10;
snake[2].node_x = 9;
snake[2].node_y = 9;
snake[3].node_x = 9;
snake[3].node_y = 8; length = 4;
for(i = 0; i< length ; i++){
print_xy(snake[i].node_x, snake[i].node_y);
}
while(1){
if(need_grow){
length ++;
need_grow = false;
for(i = length - 1; i > 0;i--){
snake[i].node_x = snake[i-1].node_x;
snake[i].node_y = snake[i-1].node_y;
}
switch (direction) {
case LEFT:
snake[0].node_x = snake[0].node_x - 1;
break;
case RIGHT:
snake[0].node_x = snake[0].node_x + 1;
break;
case FRONT:
snake[0].node_y = snake[0].node_y + 1;
break;
case BACK:
snake[0].node_y = snake[0].node_y - 1;
break;
default:
snake[0].node_x = snake[0].node_x - 1;
break;
} }
else{
if(target_x == snake[0].node_x && target_y == snake[0].node_y){
need_grow = true;
need_new = true;
}
direction = find_way(target_x,target_y,snake[0].node_x,snake[1].node_y); print_xy_t(snake[length - 1].node_x, snake[length - 1].node_y);
for(i = length - 1; i > 0;i--){
snake[i].node_x = snake[i-1].node_x;
snake[i].node_y = snake[i-1].node_y;
} switch (direction) {
case LEFT:
snake[0].node_x = snake[0].node_x - 1;
break;
case RIGHT:
snake[0].node_x = snake[0].node_x + 1;
break;
case FRONT:
snake[0].node_y = snake[0].node_y + 1;
break;
case BACK:
snake[0].node_y = snake[0].node_y - 1;
break;
default:
snake[0].node_x = snake[0].node_x - 1;
break;
}
} //显示
sprintf((char*)lcd_buffer,"*");
print_xy(snake[0].node_x, snake[0].node_y);
sprintf(s,"tail.x = %d tail.y = %d direction:%d",snake[length - 1].node_x, snake[length - 1].node_y,direction);
printf("%s",s);
sprintf(s,"head.x = %d head.y = %d",snake[0].node_x, snake[0].node_y);
printf("%s\n",s);
OSTimeDly(10);
}
} void Task1 (void *pdata){
INT8U *err;
char s[40]; while(1){
if(need_new==true){
target_x = rand()% 20 + 3;
target_y = rand()% 15 + 2;
printf("target_x = %d target_y = %d",target_x, target_y);
print_xy(target_x, target_y);
need_new = false;
}
OSTimeDly(10);
}
}

扩展:VGA模块的加入

VGA显存的设计可以看我的另一篇博客

同样是移植贪吃蛇,只需要更改printxy即可

void print_xy(int x, int y) {
int addr;
IOWR_ALTERA_AVALON_PIO_DATA(WREN_BASE, 0);
addr = y * 64 + x;
IOWR_ALTERA_AVALON_PIO_DATA(WRITEADDR_BASE, addr);
IOWR_ALTERA_AVALON_PIO_DATA(WRITEDATA_BASE, 0x7491);
IOWR_ALTERA_AVALON_PIO_DATA(WREN_BASE, 1);
}

UCOSii项目在NIOSii上的移植的更多相关文章

  1. KEIL MDK环境下uCOS-II在LPC17xx上的移植实例

    1. 知识准备 要想对ucos-ii的移植有较深的理解,需要两方面知识: (1)目标芯片,这里是lpc17xx系列芯片,它们都是基于ARMv7 Cortex-M3内核,所以这一类芯片的ucos-ii移 ...

  2. UCOSII在STM32F1上的移植

    UCOSII在STM32F1上的移植 首先准备好一份STM32F1的工程.UCOSII源码. 在准备好的工程中新建一个"UCOSII"文件夹(或者取其它名字也行): UCOSII文 ...

  3. 怎样把UCos-ii_在STM32上的移植

    下载代码 stm32 标准外设库是 stm32 全系列芯片的外设驱动,有了它能够大大加速我们 开发 stm32. 首先从 st 公司的站点下载最新的 stm32 标准外设库,写本文时最新的版本号是 V ...

  4. GitHub Android 最火开源项目Top20 GitHub 上的开源项目不胜枚举,越来越多的开源项目正在迁移到GitHub平台上。基于不要重复造轮子的原则,了解当下比较流行的Android与iOS开源项目很是必要。利用这些项目,有时能够让你达到事半功倍的效果。

    1. ActionBarSherlock(推荐) ActionBarSherlock应该算得上是GitHub上最火的Android开源项目了,它是一个独立的库,通过一个API和主题,开发者就可以很方便 ...

  5. opus在arm的嵌入式平台上的移植和开发

    最近产品中要用到opus,圣上一声令下,把opus移植到我们平台上,什么?opus?opus是什么?在一脸 茫然中,我这特种兵码农就赤手空拳上战场了. 废话少说,赶紧在网站:https://opus- ...

  6. 【无私分享:ASP.NET CORE 项目实战(第十章)】发布项目到 Linux 上运行 Core 项目

    目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 ASP.Net Core 给我们带来的最大的亮点就是跨平台,我在我电脑(win7)上用虚拟机建了个 CentOS7 ,来演示下 ...

  7. Qt4.8.5在ARM9上的移植

    Qt4.8.5在ARM9开发板上的移植 以前移植过qtopia-embedded-2.2.0,俗称Qt/E,在早期的Qt框架中是使用X11桌面服务器系统,无法应用于嵌入式平台,为此产生了qtopia, ...

  8. Win7上的ASP.NET MVC3项目在Win10上运行的一个坑

    先解释一下问题:我原来的电脑环境是Win7+VS2015,因为新换了个电脑环境变成Win10+VS2015了,所以就把原先的项目复制到新的机器上,那么问题来了,原先的一个项目在VS2015上打开竟然直 ...

  9. 发布项目到 Linux 上运行 Core 项目

    发布项目到 Linux 上运行 Core 项目 目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 ASP.Net Core 给我们带来的最大的亮点就是跨平台,我在我电脑(win ...

随机推荐

  1. Python Socket 简单聊天室1

    这是第一版,最简单的,仅仅实现了通信,你收我发,我收你发而已.下篇将介绍,基于异步多线程的聊天室: 客户端: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ...

  2. C++STL(vector,map,set,list)成员函数整理

    / *最近ACM比赛,用到的时候忘记成员函数了,贼尴尬,给以后比赛做下准备 */ LIST: 构造函数 list<int> c0; //空链表 list<int> c1(3); ...

  3. VS2013+MFC串口控件的简单上位机

    因为做东西,正好用到这里.所以就上传了文件分享一下. 利用VS带的MFC库,用起来还是比较方便的.空间的程序构架都是自动生成的,具体的程序自己加进去就行. 里面有整个的工程 还带有一个生成的EXE文件 ...

  4. Android service 服务的应用之电话监听器以及短信监听器

    首先建立一个项目工程文件,如下图所示:

  5. Mybatis(三)

    1 Mybaits--动态SQL 动态SQL是Mybatis强大特性之一.极大的简化我们拼装SQL的操作. 动态SQL元素和使用JSTL或其他类似基于XML的文本处理器相似. Mybatis采用功能强 ...

  6. Python集合(set)类型的操作

    python的set和其他语言类似, 是一个无序不重复元素集, 基本功能包括关系测试和消除重复元素. 集合对象还支持union(联合), intersection(交), difference(差)和 ...

  7. entos 7虚拟机安装手册

    entos 7虚拟机安装手册. 环境:VMware虚拟机软件 centos7 最小化安装包 步骤: 第一步:新建虚拟机 第二步:选择要安装的虚拟机的操作系统镜像.如我这里使用的就是CentOS7最小化 ...

  8. SSH服务(一)

    一.初始SSH SSH是标准的网络协议,可用于大多数UNIX操作系统,能够实现字符界面的远程登录管理,它默认使用22号端口,采用密文的形式在网络中传输数据,相对于通过明文传输的Telnet,具有更高的 ...

  9. CSS常见英语单词属性一览

    这些是css中常会用到的一些英文单词,大家可以多看看,多使用就会容易记得了. color : #999999; /*文字颜色*/ font-family : 宋体,sans-serif; /*文字字体 ...

  10. vector中删除第k个元素的巧妙方法

    假设我们定义了一个vector如下: vector<int> v;v.push_back(1);...v.push_back(255); 如果要删除第k个元素的话,应该这样写: (1)如果 ...