/*
* Copyright(C) Wind-Son.
* 2006-2007
*
* 实时应用:RT-Linux用于高精度脉冲控制
* 利用实时线程模拟定时器时钟产生连续的脉冲控制信号,
* 用于对实时性有较高要求的运动控制系统等。
*
*/
/*
* 安装和添加实时补丁
* 1. RedHat Linux Installation
* If you have not done it before, make a separate disk partition and install
* RedHat linux on the partition. RedHat 8.0 (Psyche) is recommended since
* it is most compatible with the RT-Linux we will use in this course. Also,
* make sure to install kernel development tools, gcc compiler, and utilities
* such as patch, depmod, make, and bzip2.
* 2. Get linux kernel
* Download \linux-2.4.18.tar.bz2" from the course website.
* 3. Get rtlinux kernel and patches
* Download \rtlinux-3.2-pre1.tar.bz2" from the course website.
* 4. Put a fresh copy of the rtlinux kernel in /usr/src/rtlinux-3.2-pre1
* Use the following commands.
* cd /usr/src
* tar xjf rtlinux-3.2-pre1.tar.bz2
* This will create \rtlinux-3.2-pre1" directory under /usr/src.
* 5. Put a fresh copy of the linux kernel in /usr/src/rtlinux-3.2-pre1/linux
* Use the following commands.
* cd /usr/src/rtlinux-3.2-pre1
* tar xjf linux-2.4.18.tar.bz2
* This will create \linux" directory under /usr/src/rtlinux-3.2-pre1.
* 6. Patch the linux kernel with the rtlinux patch
* cd /usr/src/rtlinux-3.2-pre1/patches
* bzip2 -d kernel patch-2.4.18-rtl3.2-pre1.bz2
* cd /usr/src/rtlinux-3.2-pre1/linux
* patch -p1 < /usr/src/rtlinux-3.2-pre1/patches/kernel patch-2.4.18
* 7. Clean all "\.o" files and stale dependencies
* cd /usr/src/rtlinux-3.2-pre1/linux
* make mrproper
* 8. Configure the linux kernel
* cd /usr/src/rtlinux-3.2-pre1/linux
* make config (text mode) or
* make xconfig (X mode)
* 9. Build a new linux kernel
* Use the following commands in order.
* cd /usr/src/rtlinux-3.2-pre1/linux
* make dep
* make bzImage
* make modules
* su
* make modules install
* cp arch/i386/boot/bzImage /boot/rtzImage
* 10. Configure your boot loader
* In the following explanation, we assume that the root file system \/" is
* mapped to /dev/hda3 and the boot file system \/boot" is mapped to /dev/hda2.
* You can check out your mapping using \df" command. If you are using LILO
* as your boot manager, add the following lines to the file \/etc/lilo.conf".
* image = /boot/rtzImage
* label = rtlinux
* read-only
* root = /dev/hda3
* The /dev/hda3 should be the device on which your root file system has been
* installed. Then, use the following command.
* /sbin/lilo
* For more details for your specific setting,
* see [url][/url] and
* [url][/url]
* If your are using GRUB as your boot manager, add the following lines to the file \/etc/grub.conf".
* title rtlinux
* root (hd0, 1)
* kernel /rtzImage ro root = /dev/hda3
* The /dev/hda3 should be the device on which your root file system has been
* installed. (hd0,1) corresponds to the first (0) physical disk derive's
* second (1) partition, which is /dev/hda2. It should be the partition where
* the boot file system \/boot" resides. For more details for
* your specific setting, see [url][/url]
* 11. Reboot and select rtlinux from boot image options
* RTLinux should boot.
* 12. Configure RTLinux
* cd /usr/src/rtlinux-3.2-pre1
* make xconfig (accept default)
* 13. Compile RTLinux Use the following commands in order.
* cd /usr/src/rtlinux-3.2-pre1
* make
* su (become the root to do the followings)
* make devices
* make install
* 14. Reboot and select rtlinux from boot image options
*/
/*
* 运行实时内核
* First of all, you should be the root to do the followings.
* In order to run rtlinux applications given in \examples" directory,
* you first insert dynamic rtlinux kernel modules like mbuff, rtl_fifo,
* rtl, rtl posixio, rtl sched, and rtl time. You can insert each module
* with \insmod modulename.o". Fortunately, the script \rtlinux" can do this
* for you at a single step. Try
* rtlinux status ( look at which module has been loaded )
* rtlinux start ( insert all rtlinux modules )
* rtlinux status ( check whether all rtlinux modules are loaded successfully )
* If you can see all the modules loaded, you are done. Otherwise, you might see
* error messages while you were doing \rtlinux start". This may be due to incorrect
* configuration of linux kernel.
* 运行实时内核模块
* insmod hello.o 载入模块
* lsmod 查看已经载入的模块
*/
#include
#include
#include
#include
#include
#include
#include
#include "rt_com.h"
#include /* printk level */
#include /* kernel version etc. */
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Wind-Son");
MODULE_DESCRIPTION("Pulse-Control system");
typedef unsigned short __u16;
/* 实时应用-IO输出控制部分 */
void io_bit_on(__u16 port, unsigned int pos, __u16 *status)
{
__asm__ __volatile__(
"movl %1,%%edx\n\t"
"movl %0,%%ecx\n\t"
"btsl %2,(%%ecx)\n\t"
"mov (%%ecx),%%al\n\t"
"out %%al,(%%dx)\n\t"
"out %%al,$0x80\n\t"
:
:"m"(status), "rm"(port), "Ir"(pos)
);
}
void io_bit_off(__u16 port, unsigned int pos, __u16 *status)
{
__asm__ __volatile__(
"movl %1,%%edx\n\t"
"movl %0,%%ecx\n\t"
"btrl %2,(%%ecx)\n\t"
"mov (%%ecx),%%al\n\t"
"out %%al,(%%dx)\n\t"
"out %%al,$0x80\n\t"
:
:"m"(status), "rm"(port), "Ir"(pos)
);
}
/*
* 实时应用-以实时线程模拟定时器产生脉冲输出部分
*/
#define dbg_print rtl_printf
#define MIN_TIME 5000
static void get_time_interval(void)
{
}
void* pulse_generate_thread(void *arg)
{
static __u16 io_status = 0;
struct sched_param p;
hrtime_t current_time;
REAL_TIME_GET_ENABLE;
int intrrupt_sched_period = 180000;
p.sched_priority = 1; /* 设置实时线程的优先级 */
struct timespec resolution;
/* RT时钟设置 */
rtl_setclockmode(CLOCK_REALTIME, RTL_CLOCK_MODE_PERIODIC,
intrrupt_sched_period);
clock_getres(rtl_getschedclock(), &resolution);
intrrupt_sched_period = timespec_to_ns(&resolution);
/* 设置RT-调度参数 */
pthread_make_periodic_np(pthread_self(), clock_gethrtime(rtl_getschedclock()),
intrrupt_sched_period);
pthread_setschedparam (pthread_self(), SCHED_FIFO, &p);
for (;;) {
dbg_print("debug entry\n");
while (!ready) /* 空闲等待 */
pthread_wait_np(); /* 空闲状态一定调用该函数让出对cpu的控制,否则死机!*/
dbg_print("debug exit\n");
if (!init_rt_clock) {
/* 初始化或重新设置RT时钟 */
init_rt_clock = 1;
pthread_wait_np();
current_time = clock_gethrtime(CLOCK_REALTIME);
} else {
if (intrrupt_sched_period < MIN_TIME)
intrrupt_sched_period = MIN_TIME;
current_time += intrrupt_sched_period;
/*
* 这一步很关键!clock_nanosleep()使本线程直接睡眠在定时器上,
* 睡眠时间current_time(ns),然后被唤醒。实验结果,这种方式
* 不但保证实时性且基本上接近于硬件定时器精度了。
* 而通过pthread_wait_np()实时调度虽然在实时性上也是有保证的,
* 但在精度上是有限的。
*/
clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, hrt2ts(current_time), NULL);
}
/* 脉冲输出控制…… */
io_bit_on(IO_PORT_OUT, XPULSE, &io_status);
/*
* 获取下一个脉冲间隔,从而产生精确的脉冲控制序列
* Note! 该实时线程用于模拟定时器输出,因此不能有太复杂的计算。
* 一般情况下将脉冲间隔时间的计算以及其他控制部分放到另外一个独立的线程。
* 在本系统中time_interval_calc_thread即用于该目的。
*/
intrrupt_sched_period = get_time_interval();
}
return 0;
}
/*
* 实时应用-动态内存申请部分。
* 直接在RT-thread中申请内存实际结果证明很不稳定,时不时的crash,
* 大概是因为alloc过程被高优先级RT-thread强制中断产生异常。
* 因此需要动态申请内存时开辟独立的内核线程专门负责内存申请。
*/
static void init_for_rt_mm(void)
{
}
static void rt_alloc_mm(void)
{
thread_wait_np();
buf = kmalloc(size, GFP_ATOMIC);
}
static int kmalloc_thread(void * kthread_arg)
{
unsigned long timeout = HZ;
init_for_rt_mm();
for (;;) {
while (!get_flag(MM_ALLOC_FLAG)) {
/* 没有内存申请任务则睡眠 */
if( signal_pending(current))
return 0;
timeout = interruptible_sleep_on_timeout(&wq, timeout);
}
rt_alloc_mm();
clear_flag(MM_ALLOC_FLAG);
}
return -1;
}
/* 实时应用-主程序-脉冲控制部分 */
wait_queue_head_t wq;
static pid_t kmalloc_kthread_id;
static int kmalloc_kthread_state = 1;
static int pulse_generate_thread_created = 0;
static int main_ctrl_thread_created = 0;
static pthread_t pulse_generate_pthread;
static pthread_t main_ctrl_pthread;
static pthread_mutex_t cache_mutex;
void rt_mm_request(void)
{
set_flag(MM_ALLOC_FLAG);
/*
* 通过设置标志位通知kmalloc_thread内核线程申请内存
*/
while(get_flag(MM_ALLOC_FLAG))
pthread_wait_np();
}
void* main_ctrl_thread(void *arg)
{
int work_sched_period = 160000;
struct timespec resolution;
int ret1 = rtl_setclockmode(rtl_getschedclock(), RTL_CLOCK_MODE_PERIODIC,
work_sched_period);
if (ret1) {
dbg_print("seting periodic mode failed\n");
clear_flag(WORK_SCHED_MODE);
}
clock_getres(rtl_getschedclock(), &resolution);
work_sched_period = timespec_to_ns(&resolution);
pthread_make_periodic_np(pthread_self(), clock_gethrtime(rtl_getschedclock()),
work_sched_period);
init_task();
for (;;) {
if (work) {
dbg_print("work\n");
rt_mm_request();
calc_time_interval();
if (exit)
break;
} else
pthread_wait_np();
}
exit_task();
return 0;
}
int init_module(void)
{
pthread_attr_t attr;
struct sched_param p;
int ret;
rtf_destroy(0);
rtf_destroy(1);
rt_com_clr_in(0);
rt_com_clr_out(0);
/* 创建实时管道,用于RT模块和普通应用程序之间的通信 */
int fifo_status = rtf_create(0,100);
if(fifo_status)
dbg_print("FIFO Create failed!");
fifo_status = rtf_create(1, 4000);
if(fifo_status)
dbg_print("FIFO Create failed!");
/* 设置实时串口,用于RT模块控制串口输出 */
rt_com_setup(0, 9600, RT_COM_PARITY_NONE, 1, 8);
hrtime_t now = gethrtime();
pthread_attr_init(&attr);
pthread_mutex_init(&cache_mutex, NULL);
pthread_attr_setfp_np(&attr, 1);
/* pulse_generate_thread */
ret = pthread_create(&pulse_generate_pthread, &attr,
pulse_generate_thread, (void *)0);
if (!ret)
pulse_generate_thread_created = 1;
pthread_make_periodic_np (pulse_generate_pthread, now + 2 * 240000, 80000);
p . sched_priority = 1;
pthread_setschedparam (pulse_generate_pthread, SCHED_FIFO, &p);
/* main_ctrl_thread */
ret = pthread_create(&main_ctrl_pthread, &attr, main_ctrl_thread, (void *)1);
if (!ret)
main_ctrl_thread_created=1;
pthread_make_periodic_np (main_ctrl_pthread, now + 2 * 160000, 30000);
p . sched_priority = 2;
pthread_setschedparam (main_ctrl_pthread, SCHED_FIFO, &p);
init_waitqueue_head(&wq);
kmalloc_kthread_id = kernel_thread(kmalloc_thread, NULL, 0);
if (kmalloc_kthread_id < 0) {
printk(KERN_ERR "fork failed, errno %d\n", -kmalloc_kthread_id);
return kmalloc_kthread_id;
}
return ret;
}
void cleanup_module(void)
{
/* send a term signal to the kthread */
int ret = kill_proc(kmalloc_kthread_id, SIGKILL, 1);
if (!ret) {
int count = 10 * HZ;
/* wait for the kthread to exit befor terminating */
while (kmalloc_kthread_state && --count) {
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(1);
}
}
if (main_ctrl_thread_created) {
pthread_cancel(main_ctrl_pthread);
pthread_join(main_ctrl_pthread, NULL);
pthread_delete_np(main_ctrl_pthread);
}
if (pulse_generate_thread_created) {
pthread_cancel(pulse_generate_pthread);
pthread_join(pulse_generate_pthread, NULL);
pthread_delete_np(pulse_generate_pthread);
}
rt_com_setup(0, -1, 0, 0, 0);
rtf_destroy(0);
rtf_destroy(1);
pthread_mutex_destroy (&cache_mutex);
}