概览

本次使用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. HDU-1495 非常可乐 (嵌套结构体-广搜 对比 一般广搜)

    题意 大家一定觉的运动以后喝可乐是一件很惬意的事情,但是seeyou却不这么认为.因为每次当seeyou买了可乐以后,阿牛就要求和seeyou一起分享这一瓶可乐,而且一定要喝的和seeyou一样多.但 ...

  2. 30. leetcode 121. Best Time to Buy and Sell Stock

    121. Best Time to Buy and Sell Stock Say you have an array for which the ith element is the price of ...

  3. swift3.0 UITableView侧滑支持多选项

    func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableVie ...

  4. HTML 基本标签01

    ↑ !DOCTYPE 文档类型声明:让浏览器,按html5的标准对代码进行解释与执行 文档类型声明必不可少,而且,必须放在文档最上方 如果不写文档类型声明,浏览器会默认按照兼容模式运行,可能会出现不明 ...

  5. oracle 内连接,外连接

    --内连接  inner join  ...  on  --左外连接 left join ... on  --右外连接  right join ... on 列: select * from stud ...

  6. Python requests 安装与开发

    Requests 是用Python语言编写HTTP客户端库,跟urllib.urllib2类似,基于 urllib,但比 urllib 更加方便,可以节约我们大量的工作,完全满足 HTTP 测试需求, ...

  7. 使用jdbc调用存储,函数

    public class JdbcTest {        private Connection conn = null;    private PreparedStatement pst = nu ...

  8. python 如何在一个for循环中遍历两个列表

    是我在看<笨方法学python>过程中发现有一行代码看不懂--" for sentence in snippet, phrase:",所以研究了半天,感觉挺有收获的.所 ...

  9. MFC基础窗口创建,CWinApp、CFrameWnd

    1.CWinApp(包括了这个类的导出类):代表了我们的程序.封装了消息循环等. 2.CFrameWnd:代表了程序的框架窗口.封装了窗口的注册.创建.显示.刷新.等等窗口操作. 3.Win32中.一 ...

  10. Linux环境下安装禅道

      1.下载禅道包 http://dl.cnezsoft.com/zentao/7.3/ZenTaoPMS.7.3.stable.zbox_64.tar.gz http://dl.cnezsoft.c ...