FPGA niosII 视频笔记

5 篇文章 1 订阅
3 篇文章 0 订阅

 工作需要使用FPGA驱动的CAN总线,一番搜索发现正点原子开发板有例程。了解之后知道是FPGA内部软核IP,基于qsys实现 ,就看完了相关视频。

nios更进一步可以看FPGA niosII 视频笔记--小梅_gzc0319的博客-CSDN博客

 

P1_Qsys_hello_world(第一讲)

1 Qsys简介:Qsys是Quartus中的系统集成工具;SOPC Builder-Qsys-Platform Designer;作用--集成IP、IP自动互联、自定义IP


2 Nios II简介:Altera设计的精简指令集软核(性能低、灵活)处理器;软核--利用FPGA本身的硬件;硬核--如ZYNQ;


3 Hello World:

P2_Qsys_hello_world(第二讲)

1 Qsys简介:
2 Nios II简介:
3 Hello World:创建普通工程-打开Qsys->添加Nios II Processor、On-Chip Memory、jtag_uart、System ID Peripheral、加上系统时钟共5个IP核;连接IP核--NIOS II从RAM取指令;设置Nios参数;分配地址;Generate、HDL example;

注意quartus15没有jtag debug module reset信号,有类似的debug reset request

将qip文件加入工程、实例化、分配引脚、编译;至此,硬件部分工作完成。


Nios II Software Build Tools for Eclipls:设置工作空间、设置工程 Nios II Application and BSP(board support package) from Template 选中sopcinfo文件、分别编译bsp和应用工程(bsp设置 使能精简驱动、小C库、禁止C++、禁止clean_exit);
先下载硬件,后下载软件;实验现象

P3_Qsys_PIO IP核(第一讲)

1 PIO简介:parallel IO;为avalon从端口和通用IO端口提供存储器映射;寄存器描述;


2 试验任务:4个键控制4个LED,上电自启动
3 程序设计:基于helloworld工程;打开qsys,添加2个PIO核并设置;添加EPCS核实现自启动,修改nios复位地址为epcs;保存、实例化、编译、分配引脚(直接修改qsf文件、引脚均改为通用IO);硬件部分至此完成

  

P4_Qsys_PIO IP核(第二讲)

1 PIO简介:
2 试验任务:
3 程序设计:重新生成bsp工程;包含system.h(PIO_KEY_BASE地址与qsys一致)、pio相关文件;按键取反后赋值LED;
出现问题,用helloworld工程验证软核,新建工程

#include <stdio.h>
#include "system.h" 					//系统头文件
#include "alt_types.h" 					//数据类型头文件
#include "altera_avalon_pio_regs.h"		//pio 寄存器头文件

 int main(void)
 {
  alt_u32 key,led; //key和led缓存变量
  while(1)
  {
	  //读取按键的值,并赋值给key。
	  key = IORD_ALTERA_AVALON_PIO_DATA(PIO_KEY_BASE);
	  //Key按下时为低电平,没有按下时为高电平。Led在高电平时亮,低电平灭。我们要将按键的值按位取反后,再赋值给led。
	  led = key;
	  //用led的值控制Led亮灭。
	  IOWR_ALTERA_AVALON_PIO_DATA(PIO_LED_BASE, led);
  }

  return(0);
}

烧录工程,先sof、后elf

P5_Qsys_PIO中断

1 PIO IRQ简介:高电平触发、边沿触发;输入型、IRQ功能、(边沿捕获寄存器)、设置IRQ类型、使能中断; 


2 试验任务:按键中断控制流水灯
3 程序设计:打开qsys工程;选中(synchronously capture)、generate IRQ;添加中断线;先跑通helloworld;主函数初始化中断后进入流水灯、中断函数全亮

 注意:nios运行时占用JTAG口,quartus无法烧写sof,需要先停止nios运行。

#include <stdio.h>
#include "system.h"
#include "altera_avalon_pio_regs.h"
#include "sys/alt_irq.h"
#include "unistd.h"
#include "alt_types.h"

void key_irq()
{
	IOWR_ALTERA_AVALON_PIO_DATA(PIO_LED_BASE, 0xF);		//写1点亮LED
}

void key_irq_init()
{
	IOWR_ALTERA_AVALON_PIO_IRQ_MASK(PIO_KEY_BASE, 1);	//使能中断
	alt_ic_isr_register(PIO_KEY_IRQ_INTERRUPT_CONTROLLER_ID,PIO_KEY_IRQ,key_irq,0,0);
}

int main()
{
  alt_u8 led=0,i=0;

  key_irq_init();

  while(1)
  {
	  for(i=0;i<4;i++)
	  {
		  led = 1<<i;
		  IOWR_ALTERA_AVALON_PIO_DATA(PIO_LED_BASE, led);
		  usleep(200000);
	  }
  }
  return 0;
}

知识点:qsys中Export与quartus的nios-IP核引脚一一对应,如Export中pio_key对应Verilog端口pio_key_export

P6_Qsys_UART IP核

1 UART IP核简介:avalon mm从端口;6个寄存器、收发数据、状态(收发ready)和控制、波特率除数(+0.5实现四舍五入好聪明


2 试验任务:与上位机通信
3 程序设计:加入UART核、连线;实例化、管脚分配;C代码--include、中断函数、中断初始化、 主函数while1

Synchronizer stages 与信号同步有关,保持默认设置即可。

#include <stdio.h>
#include "unistd.h"
#include "system.h"
#include "alt_types.h"
#include "altera_avalon_uart_regs.h"
#include "sys\alt_irq.h"
#include "stddef.h"
#include "priv/alt_legacy_irq.h"
static alt_u8 txdata = 0;
static alt_u8 rxdata = 0;

void IRQ_UART_Interrupts();             //中断初始化函数
void IRQ_init();                        //中断服务子程序

int main()
{
    printf("Hello from Nios II!\n");
    IRQ_init();
    return 0;
}

void IRQ_init()
{
    //清除状态寄存器
    IOWR_ALTERA_AVALON_UART_STATUS(UART_BASE,0);
    //使能接受准备好中断,给控制寄存器相应位写1
    IOWR_ALTERA_AVALON_UART_CONTROL(UART_BASE,0X80);
	// 注册ISR
	alt_ic_isr_register(
    UART_IRQ_INTERRUPT_CONTROLLER_ID,  // 中断控制器标号,从system.h 复制
    UART_IRQ                        ,  // 硬件中断号,从system.h 复制
	IRQ_UART_Interrupts             ,  // 中断服务子函数
	0x0                             ,  // 指向与设备驱动实例相关的数据结构体
	0x0);                              // flags,保留未用
}

//UART中断服务函数
void IRQ_UART_Interrupts()
{
	//将rxdata寄存器中存储的值读入变量rxdata中
    rxdata = IORD_ALTERA_AVALON_UART_RXDATA(UART_BASE);
    //进行串口自收发,将变量rxdata中的值赋值给变量txdata
    txdata = rxdata;
    //查询发送准备好信号,如果没有准备好,则等待。
    while(  !( IORD_ALTERA_AVALON_UART_STATUS(UART_BASE)&ALTERA_AVALON_UART_STATUS_TRDY_MSK )  );
    //发送准备好,发送txdata
    IOWR_ALTERA_AVALON_UART_TXDATA(UART_BASE,txdata);
}

其他:

qsys增加epcs核,原工程无法在线运行sof(貌似重启eclipse就好了)。新建工程,复制代码,sof验证通过;烧写出现错误,如下图

修改qsys,将epcs中断优先级改为最高 0 ,烧写正常,固化成功

P7_Qsys_SDRAM IP核

1 SDRAM控制器核:无需读取寄存器


2 试验任务:读写SDRAM;SDRAM既用做程序运行也用作普通RAM
3 程序设计:PLL设置SDRAM控制器和芯片相位差;qsys时钟、增加SDRAM核(位宽、banks、row、column、根据SDRAM芯片进行时序配置、时序配置文件qprs);实例化、分配管脚;硬件结束
地址偏移,避开程序空间;

有时无法下载elf 时,可以尝试下列设置

#include <stdio.h>     //标准输入输出头文件
#include "system.h"    //系统头文件
#include "alt_types.h" //数据类型头文件
#include "string.h"

//SDRAM地址
alt_u8 * ram = (alt_u8 *)(SDRAM_BASE+0x10000);	//SDRAM控制器地址单位为字节

int main(void){
    int i;
    memset(ram,0,100);
    //向ram中写数据,当ram写完以后,ram的地址已经变为(SDRAM_BASE+0x10000+200)
    for(i=0;i<100;i++){
        *(ram++) = i;
    }
    //逆向读取ram中的数据
    for(i=0;i<100;i++){
        printf("%d      ",*(--ram));
    }
    return 0;
}

P8_Qsys_Timer IP核

1 定时器核简介:时钟源、计时器、看门狗;状态寄存器--超时、运行;控制寄存器--使能超时中断、启停、连续计时;32/64位计时周期寄存器;snap寄存器;配置--简单周期中断、全功能、看门狗;

三个经典配置如下:


2 试验任务:蜂鸣器周期鸣叫
3 程序设计:配置TIMER核;C代码编写--include altera_avalon_timer_regs.h、主程序初始化定时器 死循环蜂鸣器、中断程序 清状态寄存器、初始化函数 写周期寄存器 设置控制寄存器 注册中断;

#include <stdio.h> 						//标准的输入输出头文件
#include "system.h" 					//系统头文件
#include "altera_avalon_timer_regs.h" 	//Timer 寄存器头文件
#include "altera_avalon_pio_regs.h" 	//PIO 寄存器头文件
#include "sys/alt_irq.h" 				//中断头文件

alt_u8 time_out = 0; //定时器计时结束标志

//定时器中断程序
void Timer_ISR_Interrupt(){
	time_out = 1;								   //定时器计时结束标志置1
	IOWR_ALTERA_AVALON_TIMER_STATUS(TIMER_BASE,0); //清除状态寄存器
}

//定时器中断初始化函数
void Timer_initial(void){

	//设置定时器周期为1s,系统时钟周期为10ns,(0x5F5_E0FF + 1) * 10ns = 1s
	IOWR_ALTERA_AVALON_TIMER_PERIODH(TIMER_BASE, 0x05F5);
	IOWR_ALTERA_AVALON_TIMER_PERIODL(TIMER_BASE, 0xE0FF);

	//设置CONTROL 寄存器
	IOWR_ALTERA_AVALON_TIMER_CONTROL(
			TIMER_BASE, ALTERA_AVALON_TIMER_CONTROL_ITO_MSK     //使能计时器中断
					  | ALTERA_AVALON_TIMER_CONTROL_CONT_MSK    //计时器连续运行
		              | ALTERA_AVALON_TIMER_CONTROL_START_MSK); //启动计时器

	//注册中断服务函数
	alt_ic_isr_register(
			TIMER_IRQ_INTERRUPT_CONTROLLER_ID, //中断控制器ID
			TIMER_IRQ,                         //硬件中断号
			Timer_ISR_Interrupt,               //中断服务程序
			0,                       		   //用于传递参数的结构体指针
			0);                                //保留位
}

int main(void){
	alt_u8 beep = 0; 		//蜂鸣器鸣叫状态

    Timer_initial();   		//初始化定时器中断

    while(1){
    	if(time_out == 1){  //定时器计时结束时,改变蜂鸣器状态
            beep = ~beep;
            IOWR_ALTERA_AVALON_PIO_DATA(PIO_BEEP_BASE, beep);
            time_out = 0;
        }
    }
}

P9_Qsys_EPCS IP核(第一讲)

 EPCS IP核简介:spi flash;允许nios访问epcs器件;epcs核本身包含BootLoader rom;寄存器通过硬件抽象层操作


2 试验任务:读写EPCS flash
3 程序设计:EPCS核实例化(注意端口,epcs的reset并未连接到jtag_debug_module_reset);HAL API两种读写flash的方法--简单访问、精确访问;flash从0写1必须擦除整个block;

epcs bootloader拷贝完成生成中断,通知nios处理器,程序开始从ram执行;

0随便写,写1则擦除整个block

block即sector

P10_Qsys_EPCS IP核(第二讲)

1 EPCS IP核简介:
2 试验任务:
3 程序设计:简单访问--打开、读写、关闭; 精确访问--获取信息、擦写、写 ;elf运行前需要擦除EPCS中固化的jic文件
感觉讲API真啰嗦,直接上实例最好

一般1个flash只有1个region

下图中block_offset data_offset,偏移地址均相对flash起始地址

#include <stdio.h>
#include "system.h"
#include "sys/alt_flash.h"

#define BUF_SIZE 100

int main(void)
{
    flash_region* regions_info;		//器件信息
    alt_flash_fd* fd;				//flash句柄
    int number_of_regions;
    int flash_rw_offset;
    int i,ret_code;
    char data_wr0[50];
    char data_wr[BUF_SIZE];
    char data_rd[BUF_SIZE];

    //将data_wr0数组初始化为0
    memset(data_wr0,0,50);

    //初始化data_wr数组
    printf("写入flash的数据: ");
    for (i=0; i<BUF_SIZE; i++){
        data_wr[i] = i;
        printf("%d,",data_wr[i]);
    }
    printf("\n");

    //打开 EPCS_FLASH 器件,获取 EPCS_FLASH 器件句柄
    fd = alt_flash_open_dev(EPCS_FLASH_NAME);

    //返回值为0表示打开器件失败
    if(!fd){
        printf("Can't open flash device\n");
    }
    //成功打开 EPCS_FLASH 器件
    else {
    	//获取 EPCS_FLASH 器件信息
        ret_code = alt_get_flash_info(fd, &regions_info, &number_of_regions);

		//返回值ret_code为0,表示获取成功FLASH信息
		if(!ret_code){

			//打印FLASH信息
			printf("\nFlash Region 信息:\n");
			printf("region_number = %d\n"	   ,number_of_regions);
			printf("region_offset = %d\n"      ,regions_info->offset);
			printf("region_size   = %d Bytes\n",regions_info->region_size);
			printf("block_number  = %d\n"	   ,regions_info->number_of_blocks);
			printf("block_size    = %d Bytes\n",regions_info->block_size);

			//指定FLASH读写的偏移地址为 "第九个Block的起始地址"
			flash_rw_offset = regions_info->offset + (regions_info->block_size)*8;

			//擦除第九个Block的内容
			alt_erase_flash_block(fd,flash_rw_offset,regions_info->block_size);

			//使用  “精确访问” 把data_wr数组的数据写入第九个Block
			alt_write_flash_block(fd,flash_rw_offset,flash_rw_offset,data_wr,BUF_SIZE);
			//读取第九个Block的数据
			alt_read_flash(fd,flash_rw_offset,data_rd,BUF_SIZE);
			printf("\n使用“精确访问”写入100个数据,从flash读取到的数据:\n");
			for(i=0;i<BUF_SIZE;i++){
				printf("%d,",data_rd[i]);
			}

			//使用  “精确访问” 将后50个数据重写为0
			alt_write_flash_block(fd,flash_rw_offset,flash_rw_offset+50,data_wr0,50);
			//读取第九个Block的数据
			alt_read_flash(fd,flash_rw_offset,data_rd,BUF_SIZE);
			printf("\n\n使用“精确访问”将后50个数据重写为0,从flash读取到的数据:\n");
			for(i=0;i<BUF_SIZE;i++){
				printf("%d,",data_rd[i]);
			}

			//使用  “简单访问” 重写前50个数据为0
			alt_write_flash(fd,flash_rw_offset,data_wr0,50);
			//读取第九个Block的数据
			alt_read_flash(fd,flash_rw_offset,data_rd,BUF_SIZE);
			printf("\n\n使用“简单访问”重写前50个数据为0,从flash读取到的数据:\n");
			for(i=0;i<BUF_SIZE;i++){
				   printf("%d,",data_rd[i]);
			}

		}
		//返回值 ret_code 非0,表示获取FLASH信息失败
		else{
			printf("Can't get EPCS flash device info"); //没有获得 EPCS_FLASH 信息
		}

		alt_flash_close_dev(fd); //关闭 EPCS_FLASH 器件
	}

    return 0;
}

P11_Qsys_自定义IP核-数码管(第一讲)

1 Avalon-MM简介:存储映射接口;基于地址的读写接口、用于主从设备连接;常用5种信号;读写时序;参考文档《Avalon Interface Specifications

2 试验任务:自定义IP核实现数码管显示
3 程序设计:符合avalon要求的接口+编译通过的源代码; qsys-new component   添加v文件,设置顶层模块,分析文件,signal设置很重要;生成并修改tcl文件路径

P12_Qsys_自定义IP核-数码管(第二讲)

增加sw.tcl和h头文件,提高可读性。有了sw.tcl才能找到h文件;ip核最好放到hardware目录下;在qsys工程中添加该IP核、后续方法同系统IP一样.

关于tcl和h文件的关系,视频没有说清楚,开发指南里提到

如下图,第35行加入双斜扛

在eclipse中Generate BSP时报错,类似语法错误

实测,若第35行加入#注释,对于新建的工程,#include "segled_controller_regs.h"会报错。但对于已经有该头文件的工程,重新生成bsp也不会报错,始终有该头文件。应该算是eclipse的1个BUG;

若初始化的false改为true, bsp不会报错,但用户工程会报错,即主函数头文件正常,但初始化函数中报错,如下图

P13_Qsys_MCU LCD图片显示实验(第一讲)

MCU屏自带GRAM,适合单片机驱动

RGB屏需要外置SDRAM,刷新速度快,适合FPGA驱动播放视频

1 TFT-LCD驱动原理:薄膜晶体管TFT;

P14_Qsys_MCU LCD图片显示实验(第二讲)

2 试验任务:nios驱动TFT,显示单张图片,叠加4行字符

3 程序设计:qsys增加TFT接口,数据接口Bidir;img2lcd生成h文件

软件工程导入现有文件--新建helloworld工程、拷贝main文件、拷贝drive文件夹、eclipse中refresh工程、build;

中文乱码设置--window-->preference-->general-->workspace-->text file encoding UTF-8

P15_Qsys_OV5640摄像头MCU LCD显示实验

1 试验任务: LCD屏幕实时显示摄像头采集的视频;nios无法实时处理视频数据,仅负责屏幕的初始化(约2000行代码) 

2 程序设计:顶层双端口引脚只能连接到一个模块,若要连接多个模块,需要拆分成输入+输出+方向控制三组引脚;

若硬件电路上无上拉电阻,可通过quartus进行设置 Weak Pull-Up Resistor

P16_Qsys_MCU LCD画板实验-触摸驱动(第一讲)

1 电容触摸屏原理:触摸屏与显示屏本质上完全无关;正点原子小屏幕为电阻式,SPI通信。大屏幕为电容式,I2C通信。

 

GT9147把配置自动保存在flash中,只需配置1次 

 

2 试验任务:

3 程序设计:

P17_Qsys_MCU LCD画板实验-触摸驱动(第二讲)

2 试验任务:nios驱动触摸+显示

 

3 程序设计:

增加后4个引脚

几千行C代码 

效果演示

 

 

P18_Qsys_Nios II写彩条LCD显示实验

P19_Qsys_uCGUI显示线点实验

​​​​​​P20_Qsys_uCGUI显示汉字图片实验

P21_Qsys_创建第一个uCOSII系统

共7讲ucos相关内容,全都是小例程,可以理解具体的几个知识点。

1 前后台系统和RTOS系统:硬实时、软实时;可剥夺型内核; 


2 uCOS简介:可读性强、源码开源、C语言可移植、资料丰富;altera移植到nios;


3 创建uCOS系统:代码存在flash,运行在sdram;nios选择fast;添加定时器核;软件工程模板选择Hello MicroC/OS-II;运行的第一行代码为alt_main.c;运行效果,两个任务交替显示信息。

无法跳转的任务创建函数时:右键-->Declarations-->Workspace,双击Search中相应行。

调整代码字体: Window-->Preferences-->General-->Appearance-->Colors and Fonts-->Basic-->双击Text Font

#include <stdio.h>
#include "includes.h"
#include "system.h"
#include "altera_avalon_pio_regs.h"

/* Definition of Task Stacks */
#define   TASK_STACKSIZE       2048
OS_STK    task1_stk[TASK_STACKSIZE];
OS_STK    task2_stk[TASK_STACKSIZE];
OS_STK    task_gzc_stk[TASK_STACKSIZE];

/* Definition of Task Priorities */

#define TASK1_PRIORITY      1
#define TASK2_PRIORITY      2
#define TASK_GZC_PRIORITY   3

/* Prints "Hello World" and sleeps for three seconds */
void task1(void* pdata)
{
  while (1)
  { 
    printf("Hello from task1\n");
    OSTimeDlyHMSM(0, 0, 3, 0);
  }
}

/* Prints "Hello World" and sleeps for three seconds */
void task2(void* pdata)
{
  while (1)
  { 
    printf("Hello from task2\n");
    OSTimeDlyHMSM(0, 0, 3, 0);
  }
}

/* Prints "Hello World" and sleeps for three seconds */
void task_gzc(void* pdata)
{
	int cnt=0;

  while (1)
  {
	  cnt++;
	  if(cnt%2==0)
		  IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE,0xf);
	  else
		  IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE,0x0);

    printf("Hello from task gzc %d  \n",cnt);
    OSTimeDlyHMSM(0, 0, 3, 0);
  }
}

/* The main function creates two task and starts multi-tasking */
int main(void)
{
  
  OSTaskCreateExt(task1,
                  NULL,
                  (void *)&task1_stk[TASK_STACKSIZE-1],
                  TASK1_PRIORITY,
                  TASK1_PRIORITY,
                  task1_stk,
                  TASK_STACKSIZE,
                  NULL,
                  0);
              
               
  OSTaskCreateExt(task2,
                  NULL,
                  (void *)&task2_stk[TASK_STACKSIZE-1],
                  TASK2_PRIORITY,
                  TASK2_PRIORITY,
                  task2_stk,
                  TASK_STACKSIZE,
                  NULL,
                  0);

  OSTaskCreateExt(task_gzc,
                  NULL,
                  (void *)&task_gzc_stk[TASK_STACKSIZE-1],
                  TASK_GZC_PRIORITY,
                  TASK_GZC_PRIORITY,
                  task_gzc_stk,
                  TASK_STACKSIZE,
                  NULL,
                  0);

  OSStart();
  return 0;
}

P22_Qsys_uCOSII任务管理与时间管理(第一讲)

1 任务管理:任务--堆栈控制块(通过控制块管理任务)、函数;任务堆栈的创建 OS_STK、任务创建函数自动初始化堆栈;任务的状态--休眠(无任务控制块)、就绪、运行、等待、中断服务;任务调度--255个优先级(1个优先级只允许有1个任务)、OS_LOWEST_PRIO决定最低优先级的数值;系统任务--空闲任务(自动创建)、统计任务(可选任务);常用函数(参见os_task.c)--建立/删除任务、请求删除任务、改变任务优先级;


2 时间管理:所有任务必须(避免高优先级任务始终占用)调用任务延时函数;取消任务延时;获取任务时间


3 程序设计

P23_Qsys_uCOSII任务管理与时间管理(第二讲)

1 任务管理:
2 时间管理:
3 程序设计:每0.5s打印时间,每1s打印CPU利用率

任务较多时,注意 OS_LOWEST_PRIO    OS_MAX_TASKS

#include <stdio.h>
#include "includes.h"

/* Definition of Task Stacks */
#define   TASK_STACKSIZE       2048
OS_STK    start_stk[TASK_STACKSIZE];
OS_STK    time_gzc_stk[TASK_STACKSIZE];
OS_STK    CPUinfo_gzc_stk[TASK_STACKSIZE];

/* Definition of Task Priorities */
#define START_PRIORITY      0
#define TIME_GZC_PRIORITY      1
#define CPU_GZC_PRIORITY      2



/* Prints "Hello World" and sleeps for three seconds */
void task_time(void* pdata)
{
  while (1)
  {
    printf("the system time is %d \n",OSTimeGet());
    OSTimeDlyHMSM(0, 0, 0, 500);
  }
}
/* Prints "Hello World" and sleeps for three seconds */
void task_cpu(void* pdata)
{
  while (1)
  {
    printf("the cpu usage is %d \n",OSCPUUsage);
    OSTimeDlyHMSM(0, 0, 1, 0);
  }
}

/* 开始任务 */
void task_start(void* pdata)
{
	OSTaskCreateExt(task_time,
	                  NULL,
	                  (void *)&time_gzc_stk[TASK_STACKSIZE-1],
	                  TIME_GZC_PRIORITY,
	                  TIME_GZC_PRIORITY,
	                  time_gzc_stk,
	                  TASK_STACKSIZE,
	                  NULL,
	                  0);


	  OSTaskCreateExt(task_cpu,
	                  NULL,
	                  (void *)&CPUinfo_gzc_stk[TASK_STACKSIZE-1],
	                  CPU_GZC_PRIORITY,
	                  CPU_GZC_PRIORITY,
	                  CPUinfo_gzc_stk,
	                  TASK_STACKSIZE,
	                  NULL,
	                  0);

	  OSTaskDel(START_PRIORITY);
}
/* The main function creates two task and starts multi-tasking */
int main(void)
{

	OSTaskCreateExt(task_start,
					  NULL,
					  (void *)&start_stk[TASK_STACKSIZE-1],
					  START_PRIORITY,
					  START_PRIORITY,
					  start_stk,
					  TASK_STACKSIZE,
					  NULL,
					  0);


  OSStart();
  return 0;
}

P24_Qsys_uCOSII信号量(第一讲)

1 信号量:事件;信号量用于保护共享资源,现在基本用来同步任务;类型--二进制、计数型;API--建立、删除、等待、释放、取消等待、强制设置;


2 互斥信号量:二进制信号量易导致优先级反转(高优先级任务等待信号量,中优先级任务无需信号量,反而无需等待,执行完毕,事实上中优先级任务优先级最高),互斥信号量可解决该问题;API类似普通信号量;

 

3 程序设计:

P25_Qsys_uCOSII信号量(第二讲)

1 信号量:
2 互斥信号量:
3 程序设计:2个任务访问共享空间先创建信号量后创建任务;

#include <stdio.h>
#include "includes.h"

//定义堆栈大小
#define   TASK_STACKSIZE       2048

//定义任务堆栈
OS_STK    start_stk[TASK_STACKSIZE];
OS_STK    task1_stk[TASK_STACKSIZE];
OS_STK    task2_stk[TASK_STACKSIZE];

//定义任务优先级
#define START_PRIORITY      4
#define TASK1_PRIORITY      5
#define TASK2_PRIORITY      6

//定义信号量 ,用作共享资源访问的信号量
OS_EVENT *shared_sem;

//共享内存单元
char shared_memory[40];

//任务1 打印字符
void task1(void* pdata)
{
	INT8U return_code = OS_NO_ERR; //系统调用的返回状态
	INT8U task1_str[] = "First Task Running";
	while (1)
	{

		printf("task1:\n");
		OSSemPend(shared_sem, 0, &return_code);
		memcpy(shared_memory,task1_str,sizeof(task1_str));
		usleep(200000);
		printf("%s\r\n",shared_memory);
		OSSemPost(shared_sem);
		OSTimeDlyHMSM(0, 0, 2, 0);
	}
}

//任务2 打印字符
void task2(void* pdata)
{
	INT8U return_code = OS_NO_ERR; //系统调用的返回状态
	INT8U task2_str[] = "Second Task Running";
	while (1)
	{
		printf("task2:\n");
		OSSemPend(shared_sem, 0, &return_code);
		memcpy(shared_memory,task2_str,sizeof(task2_str));
		usleep(200000);
		printf("%s\r\n",shared_memory);
		OSSemPost(shared_sem);
		OSTimeDlyHMSM(0, 0, 2, 0);
	}
}

//开始任务
void start_task(void* pdata)
{
	initOSsemaphore();
	initCreateTasks();
	//任务自我删除
	OSTaskDel(OS_PRIO_SELF);
	while(1);
}

//初始化信号量
int initOSsemaphore(void)
{ 	//用作二值信号量时,信号量初始化为1
	shared_sem = OSSemCreate(1);
	return 0;
}

//初始化任务函数
void initCreateTasks()
{
	//创建任务1
	OSTaskCreateExt(task1,
				  NULL,
				  (void *)&task1_stk[TASK_STACKSIZE-1],
				  TASK1_PRIORITY,
				  TASK1_PRIORITY,
				  task1_stk,
				  TASK_STACKSIZE,
				  NULL,
				  0);

	//创建任务2
	OSTaskCreateExt(task2,
				  NULL,
				  (void *)&task2_stk[TASK_STACKSIZE-1],
				  TASK2_PRIORITY,
				  TASK2_PRIORITY,
				  task2_stk,
				  TASK_STACKSIZE,
				  NULL,
				  0);
}

//主函数
int main(void)
{
  //创建开始任务
	OSTaskCreateExt(start_task,
				  NULL,
				  (void *)&start_stk[TASK_STACKSIZE-1],
				  START_PRIORITY,
				  START_PRIORITY,
				  start_stk,
				  TASK_STACKSIZE,
				  NULL,
				  0);

	OSStart();
	return 0;
}

P26_Qsys uCOSII消息邮箱与消息队列

1 消息邮箱:任务间通信--全局变量(独占访问,如配合信号量)、发布消息;消息邮箱--传送消息缓冲区指针的数据结构;API--创建、等待、发送、删除、查询、取消等待;


2 消息队列:消息队列=邮箱数组;API--创建、等待、发送、删除、清空、取消等待、查询;

3 程序设计:4个任务--邮箱收发、消息队列收发;

#include <stdio.h>
#include <unistd.h>
#include "includes.h"

//定义任务堆栈大小
#define TASK_STACKSIZE 2048

//定义各任务堆栈
OS_STK start_task_stk[TASK_STACKSIZE];
OS_STK mail_sender_stk[TASK_STACKSIZE];
OS_STK mail_receiver_stk[TASK_STACKSIZE];
OS_STK msg_sender_stk[TASK_STACKSIZE];
OS_STK msg_receiver_stk[TASK_STACKSIZE];

//分配各任务优先级
#define START_TASK_PRIORITY	        5
#define MAIL_SENDER_PRIORITY		6
#define MAIL_RECEIVER_PRIORITY		7
#define MSG_SENDER_PRIORITY			8
#define MSG_RECEIVER_PRIORITY		9

//定义1个邮箱
OS_EVENT  *mailbox;

//定义消息队列 (msgqueue)
#define		QUEUE_SIZE  10               	//消息队列大小
OS_EVENT	*msgqueue;                      //消息队列事件指针
void     	*msgqueueTbl[QUEUE_SIZE];  		//队列缓冲区

//用于发送消息队列的数据
INT32U      data_arr[QUEUE_SIZE] = {0,1,2,3,4,5,6,7,8,9};

//局部函数声明
int initOSDataStructs(void);            //初始化邮箱和消息队列的数据结构
int initCreateTasks(void);              //初始化任务

//邮箱部分
//邮件发送任务: 每隔2s将当前时间发送给mailbox
void MailSend(void* pdata)
{
	INT32U time;
	while (1)
	{
		OSTimeDlyHMSM(0, 0, 2, 0);
		time = OSTimeGet();
		//把消息发送到mailbox
		OSMboxPost(mailbox,(void *)&time);
		printf("Task send the message: %lu in mailbox\n",time);
	}
}

//邮件接收任务
void MailReceiver(void* pdata)
{
	INT8U return_code = OS_NO_ERR;
	INT32U *mbox_contents;           	//指向邮件内容的指针
	while (1)
	{
		//从mailbox接收邮件,如果邮箱为空,则一直等待
		mbox_contents = (INT32U *)OSMboxPend(mailbox, 0, &return_code);
		printf("Task get the message: %lu in mailbox\n",(*mbox_contents));
		OSTimeDlyHMSM(0, 0, 1, 0);
	}
}

//消息队列部分
//发送消息任务: 将消息通过消息队列发送给所有等待消息的任务,当队列满时,延时2s
void MsgSender(void* pdata)
{
	INT8U  send_cnt = 0;  	//定义发送计数器,用于循环发送数组中的数据
	OS_Q_DATA queue_data; 	//存放消息队列的事件控制块中的信息
	while (1)
	{
		OSQQuery(msgqueue, &queue_data);    //查询消息队列的信息
		if(queue_data.OSNMsgs < QUEUE_SIZE) //查询消息队列是否已满
		{
			//消息队列未满时,将消息发送到消息队列
			OSQPost(msgqueue,(void *)&data_arr[send_cnt]);
			if(send_cnt == QUEUE_SIZE-1)
				send_cnt = 0;
			else
				send_cnt++;
		}
		else
		{
			//消息队列已满,延时2s
			OSTimeDlyHMSM(0, 0, 2, 0);
		}
	}
}

//消息接收任务:每200ms接收一次消息队列的消息
void MsgReceiver(void* pdata)
{
	INT8U return_code = OS_NO_ERR;
	INT32U *msg_rec;                	       //存储接收到的消息
	while (1)
	{  	//到msgqueue接收消息,如果消息队列为空,则一直等待
		msg_rec = (INT32U *)OSQPend(msgqueue, 0, &return_code);
		printf("Receive message: %lu \n",*msg_rec);
		OSTimeDlyHMSM(0, 0, 0, 20);   //延时200ms
	}
}

//初始化各子任务
void  start_task(void* pdata)
{
	//初始化消息队列和邮箱的数据结构
	initOSDataStructs();
	//创建子任务
	initCreateTasks();
	OSTaskDel(OS_PRIO_SELF);
	while (1);
}

int initOSDataStructs(void)
{ 	//初始化邮箱的数据结构
	mailbox = OSMboxCreate((void *)NULL);
	//初始化消息队列的数据结构
	msgqueue = OSQCreate(&msgqueueTbl[0], QUEUE_SIZE);
	return 0;
}

int initCreateTasks(void)
{

	//创建MailSender任务
	OSTaskCreateExt(MailSend,
                             NULL,
                             &mail_sender_stk[TASK_STACKSIZE-1],
                             MAIL_SENDER_PRIORITY,
                             MAIL_SENDER_PRIORITY,
                             mail_sender_stk,
                             TASK_STACKSIZE,
                             NULL,
                             0);

	//创建MAILReceiver任务
	OSTaskCreateExt(MailReceiver,
                             NULL,
                             &mail_receiver_stk[TASK_STACKSIZE-1],
                             MAIL_RECEIVER_PRIORITY,
                             MAIL_RECEIVER_PRIORITY,
                             mail_receiver_stk,
                             TASK_STACKSIZE,
                             NULL,
                             0);
	//创建MsgSender任务
	OSTaskCreateExt(MsgSender,
                             NULL,
                             &msg_sender_stk[TASK_STACKSIZE-1],
                             MSG_SENDER_PRIORITY,
                             MSG_SENDER_PRIORITY,
                             msg_sender_stk,
                             TASK_STACKSIZE,
                             NULL,
                             0);

	//创建MsgReceiver任务
	OSTaskCreateExt(MsgReceiver,
                             NULL,
                             &msg_receiver_stk[TASK_STACKSIZE-1],
                             MSG_RECEIVER_PRIORITY,
                             MSG_RECEIVER_PRIORITY,
                             msg_receiver_stk,
                             TASK_STACKSIZE,
                             NULL,
                             0);

	return 0;
}

//main函数
int main (void)
{
	//创建父任务
	OSTaskCreateExt(start_task,
                             NULL,
                             &start_task_stk[TASK_STACKSIZE-1],
                             START_TASK_PRIORITY,
                             START_TASK_PRIORITY,
                             start_task_stk,
                             TASK_STACKSIZE,
                             NULL,
                             0);
	OSStart();  //启动uCOSII系统
	return 0;
}

P27_Qsys_CAN通信实验

1 CAN简介:MCU+总线控制器+总线驱动器    或    集成总线控制器的MCU+总线驱动器;


2 试验任务:两块开发板CAN通信,数码管显示收到的数据
3 程序设计:总线驱动器--开发板芯片TJA1050;总线控制器--第三方IP核、opencores 实现SJA1000功能;了解SJA1000--阅读IP核代码、参考datasheet、百度百科;opencores注册、trunk-rtl-verilog即为源码、修改can_defines.v文件--使用WISHBONE接口,altera器件、将源码封装为IP核(自定义IP核);封装--wishbone转换为avalon;
C代码--寄存器、代码编写;效果演示

can_top u1_can_top(
    //Wishbone interface
    .wb_clk_i   (csi_clk              ),
    .wb_rst_i   (rsi_reset | can_reset),
    .wb_dat_i   (avs_writedata[7:0]   ),
    .wb_dat_o   (avs_readdata[7:0]    ),
    .wb_cyc_i   (avs_write | avs_read ),
    .wb_stb_i   (avs_chipselect       ),
    .wb_we_i    (avs_write & ~avs_read),
    .wb_adr_i   (avs_address[7:0]     ),
    .wb_ack_o   (avs_waitrequest_n    ),
    //CAN interface
    .clk_i      (can_clk              ),
    .rx_i       (can_rx               ),
    .tx_o       (can_tx               ),
    .bus_off_on (),
    .irq_on     (can_irq_n            ),
    .clkout_o   (can_clkout           )
);

注意can核的外部连线

#include "system.h"
#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
#include "sys/alt_irq.h"
#include "sys/alt_sys_init.h"
#include "altera_avalon_pio_regs.h"            //PIO寄存器文件
#include "can_controller.h"                    //CAN控制器驱动文件
#include "segled_controller_regs.h"                 //数码管驱动文件

#define CanBaseAddr CAN_CONTROLLER_BASE
//帧信息
#define FF          0                          //帧格式: 0:SFF   1:EFF
#define RTR         0                          //0数据帧   1远程帧
#define DLC         3                          //数据长度(0~8)
#define FRM_INFO    (FF<<7) + (RTR<<6) + DLC   //帧信息

const uint8_t id[2]={0x00,0x20};               //发送的ID

uint8_t acceptance[8]={0x00,0x00,0x00,0x00,    //验收代码
                       0xff,0xff,0xff,0xff};   //验收屏蔽
uint8_t rx_data[8];                            //接收的数据
uint8_t rx_flag = 0;                           //接收有效标志

void can_isr(void* context, alt_u32 id);
void delay_ms(uint32_t n);

int main(void)
{
    uint8_t tx_data[8],i;
    uint8_t key;

    printf("Hello from Nios II!\n");
    //需发送数据
    tx_data[0] = 18;
    tx_data[1] = 12;
    tx_data[2] = 25;
    IOWR_AVALON_SEGLED_EN(SEGLED_CONTROLLER_BASE,1);
    alt_ic_isr_register(
        CAN_CONTROLLER_IRQ_INTERRUPT_CONTROLLER_ID,
        CAN_CONTROLLER_IRQ,
        can_isr,
        0,
        0);                                 //注册CAN控制器中断服务
    can_brt0(0x00);                         //设置总线定时器0 sjw=1tscl,  tscl=2tclk
    can_brt1(0x14);                         //设置总线定时器1 位长时间为8tscl
    can_acp(acceptance);                    //设置验收滤波器
    can_rest();                             //复位CAN控制器
    while(1){
        key = IORD_ALTERA_AVALON_PIO_DATA(CAN_TX_EN_BASE);
        if(!key){
            tx_id_f(FF,id);                 //发送ID
            can_txf(tx_data,FRM_INFO);      //发送数据
            delay_ms(500);
        }
        else if(rx_flag){
            rx_data_f(rx_data);
            for(i=0;i<DLC;i++){
                IOWR_AVALON_SEGLED_DATA(SEGLED_CONTROLLER_BASE,rx_data[i]);
                delay_ms(1000);
            }
            rx_flag = 0;
        }
    }
   return 0;
}

/*****************************************************************
函数功能:延时函数
入口参数:n:延时的时间
返回参数:
说明       :单位ms
******************************************************************/
void delay_ms(uint32_t n)
{
    usleep(n*1000);
}

/*****************************************************************
函数功能:中断服务函数
入口参数:
返回参数:
说明       :CAN 控制器的中断服务函数
******************************************************************/
void can_isr(void* context, alt_u32 id)
{
    uint8_t status;
    status = IORD_8DIRECT(CanBaseAddr, SJA_IR);
    printf("status:%x\n",status);
    if(status & RI_BIT){            //判断是否是接收中断
        can_rxf();                  //接收数据
        rx_flag = 1;
    }
}

P29_Qsys_FPGA开发板GUI综合实验

几乎占用FPGA全部资源;4个人写了3个月;
系统框图;C代码;

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值