|
前段时间刚刚看完small RTOS,想移植到AVR上,但对WINGCC的中断管理不熟悉。然后自己写了这个简单的任务切换练习。对于我们这些刚刚接触嵌入式操作系统的菜鸟来说会有一点帮助。高手看了千万别xiao我,程序中有几个疑点希望指点一下。
采用时间片轮转法,总共定义了8个任务,每个任务对应一个LED灯,当前任务运行时,对应的LED灯闪烁,其他任务的LED灯熄灭。任务1还加了LCD1602显示功能,这个时我后来加上去的,可以删除。初始化时堆栈指针SP指向RAM的末端RAMEND,在定时器0的溢出中断函数中作任务切换,不保存当前任务的运行环境,所以在任务切换时SP都被重新赋值为RAMEND。
程序如下:
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
//LED灯控制端口定义
#define LED_PORT PORTD
#define LED_DDR DDRD
#define LED_PIN PIND
//====================1602液晶模块================================
//1602控制端口和控制信号定义
#define LCD_CTRL_DDR DDRB
#define LCD_CTRL_PORT PORTB
#define LCD_CTRL_PIN PINB
#define LCD_RS 0X01
#define LCD_RW 0X02
#define LCD_EN 0X04
//读取类型:命令或数据
#define read_comm 0
#define read_data 1
//1602数据端口
#define LCD_DATA_DDR DDRA
#define LCD_DATA_PORT PORTA
#define LCD_DATA_PIN PINA
//1602功能函数声明
void lcd_init(void); //LCD初始化
void lcd_write_comm(unsigned char comm); //写命令
void lcd_write_data(unsigned char data); //写数据
void lcd_write_str(unsigned char x,unsigned char y,unsigned char *p);//写字符串
unsigned char lcd_read_char(unsigned char read_type); //读取
void lcd_posion(unsigned char x,unsigned char y); //位置设定
void lcd_busy_chek(void);
void lcd_en_signal(void);
//1602初始化函数
void lcd_init(void)
{
LCD_CTRL_PORT=0x00;
LCD_CTRL_DDR= 0xff;
LCD_DATA_PORT=0X00;
LCD_DATA_DDR=0XFF;
lcd_write_comm(0x3f); //功能设置,8位数据,两行显示,5X10
lcd_write_comm(0x06); //输入方式设置
lcd_write_comm(0x0f); //显示开关设置
lcd_write_comm(0x01); //清屏
}
//写命令函数
void lcd_write_comm(unsigned char comm)
{
lcd_busy_chek();
LCD_CTRL_PORT&=~LCD_RW; //WRITE
LCD_CTRL_PORT&=~LCD_RS; //RS=0
LCD_DATA_PORT=comm;
lcd_en_signal();
LCD_CTRL_PORT^=LCD_RS; //CONVERSE RS
LCD_CTRL_PORT^=LCD_RW; //WRITE
}
//1602定位函数
void lcd_posion(unsigned char x,unsigned char y)
{
unsigned char address;
if(y == 0)
address = 0x80 + x;
else
address = 0xc0 + x;
lcd_write_comm(address);
}
//写数据函数
void lcd_write_data(unsigned char data)
{
lcd_busy_chek();
LCD_CTRL_PORT&=~LCD_RW; //WRITE
LCD_CTRL_PORT|=LCD_RS; //RS=0
LCD_DATA_PORT = data;
lcd_en_signal();
LCD_CTRL_PORT^=LCD_RS; //CONVERSE RS
LCD_CTRL_PORT^=LCD_RW; //WRITE
}
//写字符串函数
void lcd_write_str(unsigned char x,unsigned char y,unsigned char *p) //写数据
{
lcd_posion(x,y);
while(*p)
{
lcd_write_data(*p);
p++;
}
}
//读数据函数
unsigned char lcd_read_char(unsigned char read_type)
{
unsigned char temp;
lcd_busy_chek();
if(read_type==read_comm) //read comm
{
LCD_CTRL_PORT&=~LCD_RS;
}
else //read data
{
LCD_CTRL_PORT|=LCD_RS;
}
LCD_CTRL_PORT|=LCD_RW; //read
lcd_en_signal();
temp=LCD_DATA_PIN;
LCD_CTRL_PORT^=LCD_RW; //CONVERSE RW
LCD_CTRL_PORT^=LCD_RS; //CONVERSE RS
return temp;
}
//1602忙检测函数
void lcd_busy_chek(void)
{
LCD_DATA_DDR = 0X00;
LCD_DATA_PORT = 0XFF;
LCD_CTRL_PORT&=~LCD_RS;
LCD_CTRL_PORT|=LCD_RW; //read
//为什么这里换成函数lcd_en_signal()却不行???
LCD_CTRL_PORT&=~LCD_EN;
_delay_us(1);
LCD_CTRL_PORT|=LCD_EN;
_delay_us(1);
LCD_CTRL_PORT&=~LCD_EN;
while( LCD_DATA_PIN&0X80 )
{
LCD_CTRL_PORT&=~LCD_EN;
_delay_us(1);
LCD_CTRL_PORT|=LCD_EN;
_delay_us(1);
LCD_CTRL_PORT&=~LCD_EN;
}
LCD_CTRL_PORT^=LCD_RW; //CONVERSE RW
LCD_CTRL_PORT^=LCD_RS; //CONVERSE RS
LCD_DATA_PORT = 0X00;
LCD_DATA_DDR = 0XFF;
}
//1602使能信号
void lcd_en_signal(void)
{
LCD_CTRL_PORT&=~LCD_EN;
_delay_us(1);
LCD_CTRL_PORT|=LCD_EN;
_delay_us(1);
LCD_CTRL_PORT&=~LCD_EN;
}
//====================================1602功能模块结束============================================================
//全局变量定义
volatile unsigned char static tick=0;
volatile unsigned char static i=0;
volatile unsigned char static swich=0x00;
//任务1
void task1(void)
{
if(swich<0xf0)
{
lcd_write_comm(0x01); //清屏
lcd_write_str(0,0,"www.ouravr.com");
lcd_write_str(0,1,"nicholasldf");
swich=0xff;
}
else
{
lcd_write_comm(0x01); //清屏
lcd_write_str(0,0,"remember mydream");
lcd_write_str(0,1,"I will be winner");
swich=0x00;
}
while(1)
{
LED_PORT^=0X01;
_delay_ms(200);
_delay_ms(200);
_delay_ms(200);
}
}
//任务2
void task2(void)
{
LED_PORT=0;
while(1)
{
_delay_ms(200);
_delay_ms(200);
_delay_ms(200);
_delay_ms(200);
LED_PORT^=0X02;
}
}
//任务3
void task3(void)
{
LED_PORT=0;
while(1)
{
_delay_ms(200);
_delay_ms(200);
_delay_ms(200);
_delay_ms(200);
LED_PORT^=0X04;
}
}
//任务4
void task4(void)
{
LED_PORT=0;
while(1)
{
_delay_ms(200);
_delay_ms(200);
_delay_ms(200);
_delay_ms(200);
LED_PORT^=0X08;
}
}
//任务5
void task5(void)
{
LED_PORT=0;
while(1)
{
_delay_ms(200);
_delay_ms(200);
_delay_ms(200);
_delay_ms(200);
LED_PORT^=0X10;
}
}
//任务6
void task6(void)
{
LED_PORT=0;
while(1)
{
_delay_ms(200);
_delay_ms(200);
_delay_ms(200);
_delay_ms(200);
LED_PORT^=0X20;
}
}
//任务7
void task7(void)
{
LED_PORT=0;
while(1)
{
_delay_ms(200);
_delay_ms(200);
_delay_ms(200);
_delay_ms(200);
LED_PORT^=0X40;
}
}
//任务8
void task8(void)
{
LED_PORT=0;
while(1)
{
_delay_ms(200);
_delay_ms(200);
_delay_ms(200);
_delay_ms(200);
LED_PORT^=0X80;
}
}
//任务数组
void (*const TaskFuction[8])(void)={task1,task2,task3,task4,task5,task6,task7,task8};
//启动函数
void start(void)
{
//将SP设置指向存储器末端
SP=RAMEND;
//将第一个任务的入口入栽
*(unsigned char *)SP-- = ((unsigned int)(TaskFuction[0])) % 256;
*(unsigned char *)SP-- = ((unsigned int)(TaskFuction[0])) / 256;
//开中断
sei();
//用中断返回指令将入口地址弹出到PC指针,运行第一个任务
asm("RETI");
}
//定时器0溢出中断函数,作任务切换
SIGNAL(SIG_OVERFLOW0)
{
tick++;
//200次时钟中断作一次任务切换
if(tick==200)
{
tick=0;
//用静态变量i来实现任务加1
i=(i++)%8;
//SP重新定位到RAMEND,不保存任务现场
SP=RAMEND;
//将下一个任务的入口地址入栽
//而不能表示为:*SP-- = ((unsigned int)(TaskFuction)) % 256;
// *SP-- = ((unsigned int)(TaskFuction)) / 256;
//并且还不能表示为:*(unsigned int *)SP-- = ((unsigned int)(TaskFuction)) % 256;
// *(unsigned int *)SP-- = ((unsigned int)(TaskFuction)) / 256;
*(unsigned char *)SP-- = ((unsigned int)(TaskFuction)) % 256;
*(unsigned char *)SP-- = ((unsigned int)(TaskFuction)) / 256;
//用中断返回指令将入口地址弹出到PC指针,作任务切换
asm("RETI");
}
}
int main(void)
{
//LED灯控制端口设置
LED_PORT=0X00;
LED_DDR =0XFF;
//定时器0初始化
TCCR0=0X05;
TIMSK=0X01;
lcd_init();
//开始运行任务
start();
while(1);
}
最后请教大家:SP指针为16位的,为何不能如下面那样使用,编译会出错
*SP-- = ((unsigned int)(TaskFuction)) % 256;
*SP-- = ((unsigned int)(TaskFuction)) / 256;
或者:
*(unsigned int *)SP-- = ((unsigned int)(TaskFuction)) % 256;
*(unsigned int *)SP-- = ((unsigned int)(TaskFuction)) / 256; |
|