UCOSii项目在NIOSii上的移植
概览
本次使用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芯片手册,可以将发送一次指令和内容打包成Index与cmd两个函数。这两个函数内容如下:
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_addr和send_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_libcvoid 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_addr与send_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上的移植的更多相关文章
- KEIL MDK环境下uCOS-II在LPC17xx上的移植实例
1. 知识准备 要想对ucos-ii的移植有较深的理解,需要两方面知识: (1)目标芯片,这里是lpc17xx系列芯片,它们都是基于ARMv7 Cortex-M3内核,所以这一类芯片的ucos-ii移 ...
- UCOSII在STM32F1上的移植
UCOSII在STM32F1上的移植 首先准备好一份STM32F1的工程.UCOSII源码. 在准备好的工程中新建一个"UCOSII"文件夹(或者取其它名字也行): UCOSII文 ...
- 怎样把UCos-ii_在STM32上的移植
下载代码 stm32 标准外设库是 stm32 全系列芯片的外设驱动,有了它能够大大加速我们 开发 stm32. 首先从 st 公司的站点下载最新的 stm32 标准外设库,写本文时最新的版本号是 V ...
- GitHub Android 最火开源项目Top20 GitHub 上的开源项目不胜枚举,越来越多的开源项目正在迁移到GitHub平台上。基于不要重复造轮子的原则,了解当下比较流行的Android与iOS开源项目很是必要。利用这些项目,有时能够让你达到事半功倍的效果。
1. ActionBarSherlock(推荐) ActionBarSherlock应该算得上是GitHub上最火的Android开源项目了,它是一个独立的库,通过一个API和主题,开发者就可以很方便 ...
- opus在arm的嵌入式平台上的移植和开发
最近产品中要用到opus,圣上一声令下,把opus移植到我们平台上,什么?opus?opus是什么?在一脸 茫然中,我这特种兵码农就赤手空拳上战场了. 废话少说,赶紧在网站:https://opus- ...
- 【无私分享:ASP.NET CORE 项目实战(第十章)】发布项目到 Linux 上运行 Core 项目
目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 ASP.Net Core 给我们带来的最大的亮点就是跨平台,我在我电脑(win7)上用虚拟机建了个 CentOS7 ,来演示下 ...
- Qt4.8.5在ARM9上的移植
Qt4.8.5在ARM9开发板上的移植 以前移植过qtopia-embedded-2.2.0,俗称Qt/E,在早期的Qt框架中是使用X11桌面服务器系统,无法应用于嵌入式平台,为此产生了qtopia, ...
- Win7上的ASP.NET MVC3项目在Win10上运行的一个坑
先解释一下问题:我原来的电脑环境是Win7+VS2015,因为新换了个电脑环境变成Win10+VS2015了,所以就把原先的项目复制到新的机器上,那么问题来了,原先的一个项目在VS2015上打开竟然直 ...
- 发布项目到 Linux 上运行 Core 项目
发布项目到 Linux 上运行 Core 项目 目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 ASP.Net Core 给我们带来的最大的亮点就是跨平台,我在我电脑(win ...
随机推荐
- kickstart自动化安装--tftp+nfs+dhcp
使用kickstart实现Centos 自动化安装 Kickstart自动化安装简介: 规模化:同时装配多台 服务器 自动化 :安装系统,配置各种服务 远程实现:不需要光盘,U盘等安装介质 优势: ( ...
- swift UIButton边框添加阴影效果
btn.layer.shadowOpacity = 0.8 //阴影区域透明度 btn.layer.shadowColor = UIColor.black.cgColor // 阴影区域颜色 btn. ...
- 教你用python写:HDU刷题神器
声明:本文以学习为目的,请不要影响他人正常判题 HDU刷题神器,早已被前辈们做出来了,不过没有见过用python写的.大一的时候见识了学长写这个,当时还是一脸懵逼,只知道这玩意儿好屌-.时隔一年,决定 ...
- 将SpringMVC中的HttpMessageConverter替换为Gson
读者们看到这个标题也许会感到奇怪,SpringMVC中默认的HttpMessageConverter不是Jackson吗,但是我在使用的过程中发现Jackson并不好用,如果有一些复杂的嵌套类型,当然 ...
- 【Spring】浅谈ContextLoaderListener及其上下文与DispatcherServlet的区别
一般在使用SpingMVC开发的项目中,一般都会在web.xml文件中配置ContextLoaderListener监听器,如下: <listener> <listener-clas ...
- mysql创建定时任务,每月1号删除上月数据
1.创建存储过程: CREATE DEFINER=`gzy`@`%` PROCEDURE `delLastMonth`() BEGIN DECLARE lastmonth int; SET lastm ...
- python基础===使用switch方法,减少使用if语句
def jia(x,y): return x+y def jian(x,y): return x-y def cheng(x,y): return x*y def chu(x,y): return x ...
- js For循环练习。
一张纸的厚度是0.0001米,将纸对折,对折多少次厚度超过珠峰高度8848米 var i = 1; var height = 0.0001; while(true){ height *= 2; if( ...
- Java连接数据库的4中方式详解
Java连接数据库的方式有多种:根据所需要的不同数据库驱动分,分为四种: 1:1类驱动.这就是JDBC-ODBC桥的方式. 但这种方式不适合程序的重用与维护,不推荐使用.需要数据库的ODBC驱动. 2 ...
- 语音激活检测(VAD)--前向神经网络方法(Alex)
这是学习时的笔记,包含相关资料链接,有的当时没有细看,记录下来在需要的时候回顾. 有些较混乱的部分,后续会再更新. 欢迎感兴趣的小伙伴一起讨论,跪求大神指点~ VAD(ffnn神经网络)-Alex t ...