实验任务:
这个实验的目的是理解uC/OS II的任务调度方式,编写uC/OS II的应用程序,通过寄存 器直接操纵GPIO来驱动外 部设备。
实验步骤:
- 设计输出方案,画连线示意图;
- 在面包板上连线,完成外部电路;
- 编写C/C++程序,测试程序和电路;
- 测试、实现uC/OS II对GPIO的访问;
- 实现DHT-11数据的读;
- 实现以时分复用方式在四位7段数码管上依次显示0000-9999的数字;
- 用两个uc/OS II任务,一个定时读DHT-11数据,一个轮流驱动数码管,一秒一次显示当 前温度和湿度。注意处理 好两个任务之间的数据共享。
板卡:wrtnode
首先从github上下载ucos-ii-for-pcduino的代码,我们在此基础上做wrtnode的移植:
git clone https://github.com/Pillar1989/ucos-ii-for-pcDuino
下文中的”./”均指ucos-ii-for-pcDuino文件夹
其中 ./app存放我们的用户代码,我们稍候写的代码就放在./app/sample.c中。
./build存放编译的目标文件.o
./arduino 存放arduino库
./ucos存放ucos的源码
./config.mk存储编译的配置文件
./scp.sh 是我后来编写的脚本,主要是用scp命令拷贝二进制代码和必须的库到板卡上。
./Scp.sh文件的内容如下
ssh-keygen 命令用来删除之前预留的wrtnode指纹。因为我的板卡经常会被莫名其妙地reset重启并恢复出厂设置。(后来发现只要#GPIO2 引脚输出1就会reset)每次reset后指纹都会变更。linux默认会检验指纹,避免man-in-the-middle-attack(中间人攻击)。
Scp source destination 拷贝文件到板卡上。
ucos_sample是编译后的我们的二进制文件,剩余三个是程序运行时所需要的动态链接库,也需要一并拷贝到板卡上。
linux中,动态链接库以.so结尾,静态链接库以.a结尾(archive)
首先更改./arduino/makefile文件和./makefile文件,用mipsel-openwrt-linux-xxx工具链作交叉编译。
上图这样写的前提是我们已经把工具链编译程序放在了系统的path中,如果没有,请更改~/.bashrc文件并重启terminal。更改方法是,在~/.bashrc文件的末尾加上export PATH=”$PATH:[your crosstool-chain-directory]”
首先用交叉编译工具编译arduino库,它允许我们通过arduino函数的接口来调用gpio口。
./ Cd arduino
./arduino/make
然后退到上层目录,编译ucos和app程序。
编译期间(准确的说时链接期间)可能会遇到找不到xxx.so文件的情况,这时请在crosstool-chain的文件夹下用find指令找到该xxx.so文件,再在./config.mk 的LDFLAGS中添加该文件目录,这样链接器会在该文件目录下寻找动态库。语法为 -L[dir]
tips:用 nm xxx.so命令可以查看动态链接库中的label
编译成功后,会出现./ucos_sample文件,拷贝该文件和相关的动态链接库到板卡上,即可运行sample程序。
至此编译过程结束,下面开始编程。
wrtnode对GPIO的操作方面,不能直接用arduino库提供的pinMode等函数,因为它是为pcDuino所写,wrtnode和它还不太一样。我的解决方法是利用网上提供的一个wrtnode GPIO引脚中断的例程改编的,google wrtnode(or openwrt) + gpio + interrupt应该可以搜到,文件名为interrupt.c,我改成了gpio.c下图是代码的版权部分。
改段例程封装了wrtnode的gpio操作,但每次操作gpio口很费时,需要100us左右。因为实验中用到的DHT11的数据输出时也是以us级别来输出总线数据,所以在代码中我直接用了
lseek(fd, 0, SEEK_SET);
read(fd, &bit, 1);
来读取gpio对应的fd的数据,这样每次读数据的时间缩短在26us左右.
DHT11读取温度湿度,根据它的datasheet来编程。http://wenku.baidu.com/view/1955cc70a417866fb84a8e7b.html
四位七段数码管要用分时复用来编程。我一开始尝试用完全分时的28段分时编程,结果频闪过于严重,惨不忍睹。权衡之下采用了4段分时编程。
以下是代码片段,直接改的sample.c,版权信息没删。
/*
********************************************************************************************************* * sample.c * * Description: This sample program uses the ucos linux port to start 5 simple tasks. * * Author: Philip Mitchell * ********************************************************************************************************* */
#include <stdio.h> #include <stdlib.h> #include “ucos_ii.h” #include <core.h> #include <string.h>
void showDigit(uint8_t* digits);
uint8_t led_pins_slice[] = {41, 21, 0, 18};//gpio 2 can not use, cause reset uint8_t led_pins_segment[] = {43, 72, 37, 19, 17, 40, 42, 44}; int32_t led_pins_slice_fd[4]; int32_t led_pins_segment_fd[8]; uint8_t data_pin = 20; uint8_t data[5]; uint8_t dataAvailable = 0; void hardware_init() { //set slice pins uint8_t i = 0; for (i = 0; i < 4; ++i) { gpio_export(led_pins_slice[i]); gpio_set_dir(led_pins_slice[i], OUTPUT); gpio_set_value(led_pins_slice[i], LOW); led_pins_slice_fd[i] = gpio_fd_open(led_pins_slice[i]); }
//set segment pins for (i = 0; i < 8; ++i) { gpio_export(led_pins_segment[i]); gpio_set_dir(led_pins_segment[i], OUTPUT); gpio_set_value(led_pins_segment[i], HIGH); led_pins_segment_fd[i] = gpio_fd_open(led_pins_segment[i]); }
//set data pin gpio_export(data_pin); gpio_set_dir(data_pin, OUTPUT); gpio_set_value(data_pin, HIGH); } /* Function common to all tasks */
void controlLed(void *p_arg){ #if OS_CRITICAL_METHOD == 3 OS_CPU_SR cpu_sr = 0; #endif uint8_t i; while(1){ //OS_ENTER_CRITICAL(); uint8_t values[] = {0,0,0,0}; if(dataAvailable){ values[0] = data[2] / 10; values[1] = data[2] % 10; values[2] = data[0] / 10; values[3] = data[0] % 10; } showDigit(values); //OS_EXIT_CRITICAL(); } } void showDigit(uint8_t* digits){ //slice between 0 ~ 3 //digit between 0 ~ 9 uint8_t matrix[] = {0xD7, 0x14, 0xCD, 0x5D, 0x1E, 0x5B, 0xDB, 0x15, 0xDF, 0x5F}; uint8_t i; uint8_t j; for(i = 0; i < 4; i++){ //close led for(j = 0; j < 4; j++){ gpio_set_value(led_pins_slice[j], LOW); } //set segment for(j = 0; j < 8; j++){ uint8_t value = !(matrix[digits[i]] & (0x80 >> j)); gpio_set_value(led_pins_segment[j], value); } //chose slice for(j = 0; j < 4; j++){ if(i == j){ gpio_set_value(led_pins_slice[j], HIGH); } else{ gpio_set_value(led_pins_slice[j], LOW); } }
OSTimeDly(1); }
} void readData( void *p_arg ) { char* sTaskName = (char*)p_arg; uint8_t bit;//bit value uint8_t isError = 0; uint32_t t; uint8_t bitIndex, byteIndex; uint8_t byte; uint32_t i; uint32_t timeCnt; // static flag1 = 1; #if OS_CRITICAL_METHOD == 3 OS_CPU_SR cpu_sr = 0; #endif
//wait 1s for DHT11 to warm up — OK OSTimeDly(201); int fd = gpio_fd_open(data_pin);
while(1) { isError = 0; gpio_set_dir(data_pin, OUTPUT); gpio_set_value(data_pin, HIGH); OSTimeDly(201); OS_ENTER_CRITICAL(); //send start signal to DHT11 gpio_set_value(data_pin, LOW); delay(20);//send low signal longer than 18ms, avarage 29ms — OK gpio_set_dir(data_pin, INPUT); //140us
lseek(fd, 0, SEEK_SET); read(fd, &bit, 1);
//get ack bit timeCnt = 10000; while(bit != LOW){ //gpio_get_value(data_pin, &bit); lseek(fd, 0, SEEK_SET); read(fd, &bit, 1); bit -= ‘0’; if(–timeCnt == 0){// 200us isError = 1; printf(“didn\’t receive LOW ack from DHT11.\n”); break; } } if(isError == 1){ continue; }
//receive ack from DHT11 // t = micros(); timeCnt = 10000; while(bit != HIGH){ //gpio_get_value(data_pin, &bit); lseek(fd, 0, SEEK_SET); read(fd, &bit, 1); bit -= ‘0’; if(–timeCnt == 0){// 2000us isError = 1; printf(“didn\’t receive HIGH ack from DHT11.\n”); break; } } if(isError == 1){ continue; }
timeCnt = 10000; while(bit != LOW){ lseek(fd, 0, SEEK_SET); read(fd, &bit, 1); bit -= ‘0’; if(–timeCnt == 0){// 2000us isError = 1; printf(“didn\’t receive LOW gap from DHT11.\n”); break; } } if(isError == 1){ OS_EXIT_CRITICAL(); continue; } //omit some codes here… //… OS_EXIT_CRITICAL(); }
}
int main (void) { /* pthreads allocates its own memory for task stacks. This UCOS linux port needs a minimum stack size in order to pass the function information within the port. */ hardware_init(); INT8U Stk1[ OSMinStkSize() ]; INT8U Stk2[ OSMinStkSize() ]; INT8U Stk3[ OSMinStkSize() ]; INT8U Stk4[ OSMinStkSize() ]; INT8U Stk5[ OSMinStkSize() ];
char sTask1[] = “Task 1”; char sTask2[] = “Task 2”; char sTask3[] = “Task 3”; char sTask4[] = “Task 4”; // char sTask5[] = “Task 5”;
OSInit(); //delay 1s for DHT11 to warm up. OSTaskCreate( readData, sTask1, (void*)Stk1, 0 ); OSTaskCreate( controlLed, sTask2, (void*)Stk2, 5 ); // OSTaskCreate( MyTask, sTask3, (void*)Stk3, 6 ); // OSTaskCreate( MyTask, sTask4, (void*)Stk4, 7 ); // OSTaskCreate( MyTask, sTask5, (void*)Stk5, 8 );
OSStart();
return 0; }
|
前两位是温度26,后两位是湿度63