工作需要使用FPGA驱动的CAN总线,一番搜索发现正点原子开发板有例程。了解之后知道是FPGA内部软核IP,基于qsys实现 ,就看完了相关视频。
nios更进一步可以看FPGA niosII 视频笔记--小梅_gzc0319的博客-CSDN博客
1 Qsys简介:Qsys是Quartus中的系统集成工具;SOPC Builder-Qsys-Platform Designer;作用--集成IP、IP自动互联、自定义IP;
2 Nios II简介:Altera设计的精简指令集软核(性能低、灵活)处理器;软核--利用FPGA本身的硬件;硬核--如ZYNQ;
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);
先下载硬件,后下载软件;实验现象
1 PIO简介:parallel IO;为avalon从端口和通用IO端口提供存储器映射;寄存器描述;
2 试验任务:4个键控制4个LED,上电自启动
3 程序设计:基于helloworld工程;打开qsys,添加2个PIO核并设置;添加EPCS核实现自启动,修改nios复位地址为epcs;保存、实例化、编译、分配引脚(直接修改qsf文件、引脚均改为通用IO);硬件部分至此完成
1 PIO简介:
2 试验任务:
3 程序设计:重新生成bsp工程;包含system.h(PIO_KEY_BASE地址与qsys一致)、pio相关文件;按键取反后赋值LED;
出现问题,用helloworld工程验证软核,新建工程
#include
#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
1 PIO IRQ简介:高电平触发、边沿触发;输入型、IRQ功能、(边沿捕获寄存器)、设置IRQ类型、使能中断;
2 试验任务:按键中断控制流水灯
3 程序设计:打开qsys工程;选中(synchronously capture)、generate IRQ;添加中断线;先跑通helloworld;主函数初始化中断后进入流水灯、中断函数全亮
注意:nios运行时占用JTAG口,quartus无法烧写sof,需要先停止nios运行。
#include
#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<
知识点:qsys中Export与quartus的nios-IP核引脚一一对应,如Export中pio_key对应Verilog端口pio_key_export。
1 UART IP核简介:avalon mm从端口;6个寄存器、收发数据、状态(收发ready)和控制、波特率除数(+0.5实现四舍五入好聪明)
2 试验任务:与上位机通信
3 程序设计:加入UART核、连线;实例化、管脚分配;C代码--include、中断函数、中断初始化、 主函数while1
Synchronizer stages 与信号同步有关,保持默认设置即可。
#include
#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 ,烧写正常,固化成功
1 SDRAM控制器核:无需读取寄存器
2 试验任务:读写SDRAM;SDRAM既用做程序运行也用作普通RAM;
3 程序设计:PLL设置SDRAM控制器和芯片相位差;qsys时钟、增加SDRAM核(位宽、banks、row、column、根据SDRAM芯片进行时序配置、时序配置文件qprs);实例化、分配管脚;硬件结束
地址偏移,避开程序空间;
有时无法下载elf 时,可以尝试下列设置
#include //标准输入输出头文件
#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;
}
1 定时器核简介:时钟源、计时器、看门狗;状态寄存器--超时、运行;控制寄存器--使能超时中断、启停、连续计时;32/64位计时周期寄存器;snap寄存器;配置--简单周期中断、全功能、看门狗;
三个经典配置如下:
2 试验任务:蜂鸣器周期鸣叫
3 程序设计:配置TIMER核;C代码编写--include altera_avalon_timer_regs.h、主程序初始化定时器 死循环蜂鸣器、中断程序 清状态寄存器、初始化函数 写周期寄存器 设置控制寄存器 注册中断;
#include //标准的输入输出头文件
#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;
}
}
}
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
1 EPCS IP核简介:
2 试验任务:
3 程序设计:简单访问--打开、读写、关闭; 精确访问--获取信息、擦写、写 ;elf运行前需要擦除EPCS中固化的jic文件
感觉讲API真啰嗦,直接上实例最好
一般1个flash只有1个region
下图中block_offset data_offset,偏移地址均相对flash起始地址
#include
#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; ioffset);
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
1 Avalon-MM简介:存储映射接口;基于地址的读写接口、用于主从设备连接;常用5种信号;读写时序;参考文档《Avalon Interface Specifications》
2 试验任务:自定义IP核实现数码管显示
3 程序设计:符合avalon要求的接口+编译通过的源代码; qsys-new component 添加v文件,设置顶层模块,分析文件,signal设置很重要;生成并修改tcl文件路径;
增加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不会报错,但用户工程会报错,即主函数头文件正常,但初始化函数中报错,如下图
MCU屏自带GRAM,适合单片机驱动
RGB屏需要外置SDRAM,刷新速度快,适合FPGA驱动播放视频
1 TFT-LCD驱动原理:薄膜晶体管TFT;
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;
1 试验任务: LCD屏幕实时显示摄像头采集的视频;nios无法实时处理视频数据,仅负责屏幕的初始化(约2000行代码)
2 程序设计:顶层双端口引脚只能连接到一个模块,若要连接多个模块,需要拆分成输入+输出+方向控制三组引脚;
若硬件电路上无上拉电阻,可通过quartus进行设置 Weak Pull-Up Resistor
1 电容触摸屏原理:触摸屏与显示屏本质上完全无关;正点原子小屏幕为电阻式,SPI通信。大屏幕为电容式,I2C通信。
GT9147把配置自动保存在flash中,只需配置1次
2 试验任务:
3 程序设计:
2 试验任务:nios驱动触摸+显示
3 程序设计:
增加后4个引脚
几千行C代码
效果演示
共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
#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;
}
1 任务管理:任务--堆栈、控制块(通过控制块管理任务)、函数;任务堆栈的创建 OS_STK、任务创建函数自动初始化堆栈;任务的状态--休眠(无任务控制块)、就绪、运行、等待、中断服务;任务调度--255个优先级(1个优先级只允许有1个任务)、OS_LOWEST_PRIO决定最低优先级的数值;系统任务--空闲任务(自动创建)、统计任务(可选任务);常用函数(参见os_task.c)--建立/删除任务、请求删除任务、改变任务优先级;
2 时间管理:所有任务必须(避免高优先级任务始终占用)调用任务延时函数;取消任务延时;获取任务时间
1 任务管理:
2 时间管理:
3 程序设计:每0.5s打印时间,每1s打印CPU利用率;
任务较多时,注意 OS_LOWEST_PRIO OS_MAX_TASKS
#include
#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;
}
1 信号量:事件;信号量用于保护共享资源,现在基本用来同步任务;类型--二进制、计数型;API--建立、删除、等待、释放、取消等待、强制设置;
2 互斥信号量:二进制信号量易导致优先级反转(高优先级任务等待信号量,中优先级任务无需信号量,反而无需等待,执行完毕,事实上中优先级任务优先级最高),互斥信号量可解决该问题;API类似普通信号量;
3 程序设计:
1 信号量:
2 互斥信号量:
3 程序设计:2个任务访问共享空间;先创建信号量后创建任务;
#include
#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;
}
1 消息邮箱:任务间通信--全局变量(独占访问,如配合信号量)、发布消息;消息邮箱--传送消息缓冲区指针的数据结构;API--创建、等待、发送、删除、查询、取消等待;
2 消息队列:消息队列=邮箱数组;API--创建、等待、发送、删除、清空、取消等待、查询;
3 程序设计:4个任务--邮箱收发、消息队列收发;
#include
#include
#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;
}
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
#include
#include
#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
P29_Qsys_FPGA开发板GUI综合实验
几乎占用FPGA全部资源;4个人写了3个月;
系统框图;C代码;