之前已经把nios2的硬件设计给搞定了,剩下就进行软件设计就行了。
在刚刚建立的工程,选择nios2 SBT。。现在开发nios2软件已经用NIOS2 SBT了,不在用以前的IDE了。
弹出的框中选择软件的路径,这里就选择建立的工程。
就弹出软件界面了。。。和一般的IDE软件看着没啥区别。
第一步肯定是新建工程了。
在sopc information中选择刚刚硬件设计时生成的.sopcinfo文件,这个文件包括了硬件的一些信息。软件会识别到CPU 是nios2.
在project name中填入工程的名字,这里填lcd。
下面有一些模板可以选择,这里直接选择空工程。点击next。
这里是选择新建一个BSP还是使用一个以前的BSP。。这里的BSP是板极支持包,也就是对顶层硬件的封装库,这样,我们对底层的操作,就调用BSP提供的库函数就可以了。如果用过stm32的话,就比较理解这个东西了,就相当于一个硬件操作的库。
这样就新建了一个工程。
左边就多了两个东西,一个是我们自己要用的,另外一个是BSP,硬件底层的 库函数都是在这个里面。
首先在lcd下新建文件夹,include,用来保存我们编写的.h文件。
然后在新建main.c文件。用来编写main函数。
然后就开始我们的写程序之旅了。。
首先新建lcd.h和lcd.c文件。。。用来写lcd的相关程序。
首先是lcd.h。
#ifndef __LCD_H_ #define __LCD_H_
#include"system.h" #include<unistd.h> #include<io.h>
#define lcd_write_cmd(cmd) IOWR(LCD_BASE,0,cmd) #define lcd_read_cmd() IORD(LCD_BASE,1) #define lcd_write_data(dat) IOWR(LCD_BASE,2,dat) #define lcd_read_data() IORD(LCD_BASE,3)
void lcd_init(); void lcd_show_char(unsigned char add, char ch ); void lcd_show_str(unsigned char add, char *str);
#endif /* LCD_H_ */ |
开头和结尾都是固定的。预防重定义。
下面是把一些头文件给包括进来。
l system.h: 这是一个比较重要的头文件,这个文件里面定义了我们之前用qsys构建的硬件信息。这些信息编程时候是需要的,比如硬件的基地址,因为只有知道了基地址,才知道去控制硬件相关的哪些寄存器。
l unistd.h: 这个是标准库,里面有一个微妙延时函数。
l Io.h: io控制的库。 里面定义了io控制的函数。
之后是定义一些操作。
在以前驱动lcd的时候,我们需要自己给控制lcd的管脚以达到显示的功能,但是现在altera的lcd ip给我们做好了,我们不需要去控制lcd的管脚。只需要调用给的函数就可以了。
比如IOWR(LCD_BASE,0,cmd) 就是向lcd发送命令cmd。是不是简单很多了,这就是ip的好处,简单。但是这个函数表达不够清楚,并不能从这函数看出这个函数什么功能,所以这里用了一个
lcd_write_cmd(cmd) IOWR(LCD_BASE,0,cmd)
这样,后面写程序就直接用lcd_write_cmd(cmd)就可以了。
最后是定义了三个函数,一个初始化,一个写一个字符,一个写一串字符。
lcd.c里面的程序就比较简单了,就调用.h文件中那两个define。写命令,写数据。
#include"include/LCD.h" void lcd_init() { lcd_write_cmd(0x38); usleep(2000); lcd_write_cmd(0x0c); usleep(2000); lcd_write_cmd(0x01); usleep(2000); lcd_write_cmd(0x06); usleep(2000); } void lcd_show_char(unsigned char add, char ch ) { lcd_write_cmd(add); lcd_write_data(ch); } void lcd_show_str(unsigned char add, char *str) { lcd_write_cmd(add); while(*str != '\0') { lcd_write_data(*str); str++; usleep(2000); } } |
这样,就完成了lcd的代码了,需要使用lcd的时候,调用函数就可以了。
下面是main函数。
首先说明下我main函数实现什么功能。
板子上有16个led灯,我分成两组。当按键没有按下的时候,第一组执行流水灯,从右到左,然后从左到右,当按键按下的时候,第二组执行流水灯。
至于lcd显示就是在开始的时候显示一些信息,按键按下的时候在显示另外的信息。
#include <stdio.h> #include <unistd.h> #include "altera_avalon_pio_regs.h" #include"system.h" #include"sys/alt_irq.h" #include"include/LCD.h"
unsigned char flag = 0; volatile int key_ex_init;
void ISR_key(void * isr_context) { flag = 0; }
void init_key(void) { //KEY->INTERRUPT_MASK = 1; void *key_ex_init_ptr = (void *) & key_ex_init; IOWR_ALTERA_AVALON_PIO_IRQ_MASK(KEY_BASE,0X1); alt_ic_isr_register(KEY_IRQ_INTERRUPT_CONTROLLER_ID, KEY_IRQ, ISR_key, key_ex_init_ptr, 0x0); } int main() { volatile char i; printf("Hello weiqi7777!\n"); init_key(); lcd_init(); lcd_show_str(0x80," hello world"); lcd_show_str(0xC0," lujun love 7777"); flag = 1; while(1) { if(flag == 1) { for(i=0; i<8; i++) { IOWR_ALTERA_AVALON_PIO_DATA(LED_BASE,(1<<i)); usleep(100000); } for(i=0; i<8; i++) { IOWR_ALTERA_AVALON_PIO_DATA(LED_BASE,(1<<(8-i))); usleep(100000); } } else { lcd_write_cmd(0x01); lcd_show_str(0x80," interrupt !!"); for(i=8; i<16; i++) { IOWR_ALTERA_AVALON_PIO_DATA(LED_BASE,(1<<(i))); usleep(100000); } for(i=0; i<8; i++) { IOWR_ALTERA_AVALON_PIO_DATA(LED_BASE,(1<<(16-i))); usleep(100000); } } } return 0; } |
首先是头文件的说明
#include <stdio.h> c里面的标准头文件,用到里面的printf函数
#include <unistd.h> 标准头文件,用到里面的延时函数
#include "altera_avalon_pio_regs.h" PIO ip核的头文件,里面定义了操作PIO ip的一些函数。
#include"system.h"
#include"sys/alt_irq.h" 中断头文件,定义中断的相关函数
#include"include/LCD.h" 之前写的lcd头文件。
然后是按键中断的处理
volatile int key_ex_init;
void ISR_key(void * isr_context) { flag = 0; } void init_key(void) { void *key_ex_init_ptr = (void *) & key_ex_init; IOWR_ALTERA_AVALON_PIO_IRQ_MASK(KEY_BASE,0X1); alt_ic_isr_register(KEY_IRQ_INTERRUPT_CONTROLLER_ID, KEY_IRQ, ISR_key, key_ex_init_ptr, 0x0); } |
这里比较重要的中断注册函数alt_ic_isr_register()。在nios2,使用中断之前,需要对中断进行注册,也就是相当于打开中断,这样才能使用中断。而这个函数有5个参数。
l 第一个是中断控制ID,这个在system.h 中有定义。
l 第二个是中断号,这个在system.h 中也有定义。
l 第三个是中断函数,这个你想写什么就什么,只要和中断函数名字一样就可以了。
l 第四个和第五个目前我也不知道有什么用,只知道是这样用,那就先这么用。
中断注册后,就开启中断了,就可以再中断函数中写程序,执行中断函数了。这里要注意,注册中断函数之前,需要打开中断使能。
IOWR_ALTERA_AVALON_PIO_IRQ_MASK(KEY_BASE,0X1);
这个是提供的库函数,打开IO中断。
剩下就是main函数了,main函数也比较简单,这里就不说了。
要说明的是
IOWR_ALTERA_AVALON_PIO_DATA(LED_BASE,(1<<i));
这个也是一个库函数,就是控制io管脚的输出。第一个参数是IO外设基地址,第二个是输出数据,可以看出这个是一次性控制所有IO的输出。
程序搞定后,就编译了。
Build project 或者快捷键 crtl+b。
编译完成后,就可以下载程序了。
可以看到nios2 控制台打印出了信息,说明程序正确执行了。
程序是在ram中运行的,当掉电后程序就丢失了,所以上电后,要重新下载程序以及硬件的sof。
程序运行的时候,就会感觉到这个软件不好用的地方了。。有时候程序会下不进去,提示XXX不匹配,或者是下进去没有反应,这个时候,就重新建nios2软件工程,重新来一次,注意选择的sopc文件一定要选择对。
实在不行的话,就回到quartus重新生成qsys,然后重新编译,在重新下载生成的sof文件。