依成名的博客

---学无止境,折腾不止


  • 首页

  • 分类

  • 关于

  • 归档

  • 标签

PX4/Pixhawk之基于NSH调试的uORB第一个应用测试

发表于 2015-07-16   |   分类于 无人机   |     |   阅读次数

详见博客:http://blog.csdn.net/freeape/article/details/46912815

PX4/Pixhawk—基于NSH调试的uORB第一个应用测试

1 NSH连接测试

  (1) 测试前准备:

  • PX4FMU(已刷好固件)
  • USB线
  • 安装好PX4 Toolchain

  (2) Pixhawk通过USB连接电脑,并安装好了驱动:
  PX4FMU.png

  (3) 打开Tera Term软件(PX4 Toolchain->TeraTerm):
  TeraTerm.png

2 开始第一个应用

  http://www.pixhawk.com/start?id=zh/dev/px4_simple_app

PX4/Pixhawk之uORB深入理解和应用

发表于 2015-07-14   |   分类于 无人机   |     |   阅读次数

详见博客:http://blog.csdn.net/freeape/article/details/46880637

The Instructions of uORB

『PX4/Pixhawk』   『软件体系结构』 『uORB』 『主题发布』 『主题订阅』

1 简介

1.1 PX4/Pixhawk的软件体系结构

 PX4/Pixhawk的软件体系结构主要被分为四个层次,这可以让我们更好的理解PX4/Pixhawk的软件架构和运作:

  • 应用程序的API:这个接口提供给应用程序开发人员,此API旨在尽可能的精简、扁平及隐藏其复杂性。
  • 应用程序框架: 这是为操作基础飞行控制的默认程序集(节点)。
  • 库: 这一层包含了所有的系统库和基本交通控制的函数。
  • 操作系统: 最后一层提供硬件驱动程序,网络,UAVCAN和故障安全系统。

PX4/Pixhawk软件体系结构
  uORB(Micro Object Request Broker,微对象请求代理器)是PX4/Pixhawk系统中非常重要且关键的一个模块,它肩负了整个系统的数据传输任务,所有的传感器数据、GPS、PPM信号等都要从芯片获取后通过uORB进行传输到各个模块进行计算处理。实际上uORB是一套跨「进程」 的IPC通讯模块。在Pixhawk中, 所有的功能被独立以进程模块为单位进行实现并工作。而进程间的数据交互就由为重要,必须要能够符合实时、有序的特点。
  Pixhawk使用的是NuttX实时ARM系统,uORB实际上是多个进程打开同一个设备文件,进程间通过此文件节点进行数据交互和共享。进程通过命名的「总线」交换的消息称之为「主题」(topic),在Pixhawk 中,一个主题仅包含一种消息类型,通俗点就是数据类型。每个进程可以「订阅」或者「发布」主题,可以存在多个发布者,或者一个进程可以订阅多个主题,但是一条总线上始终只有一条消息。

1.2 PX4/Pixhawk应用程序框架

PX4应用程序框架

  应用层中操作基础飞行的应用之间都是隔离的,这样提供了一种安保模式,以确保基础操作独立的高级别系统状态的稳定性。而沟通它们的就是uORB。

2 uORB文件夹说明

2.1 uORB文件夹结构

uORB文件夹结构

2.2 文件/目录说明

topics : 系统通用接口定义的标准主题,比如电池电量转态、GPS的位置参数等
module.mk : uORB模块makefile文件
objects_common.cpp: 通用接口标准主题定义集合,如添加新主题在这里定义
ORBMap.hpp : 对象请求器节点链表管理(驱动节点)
ORBSet.hpp : 对象请求器节点管理(非驱动节点)
Publication.cpp : 在不同的发布中遍历使用
Publication.hpp : 在不同的发布中遍历使用
Subscription.cpp : 在不同的订阅中遍历使用
Subscription.hpp : 在不同的订阅中遍历使用
uORB.cpp : uORB的实现
uORB.h : uORB头文件
uORBCommon.hpp : uORB公共部分变量定义实现
uORBCommunicator.hpp : 远程订阅的接口实现,实现了对不同的通信通道管理,如添加/移除订阅者,可以基于TCP/IP或fastRPC;传递给通信链路的实现,以提供在信道上接收消息的回调。
uORBDevices.hpp :
uORBDevices_nuttx.cpp : 节点操作,close,open,read,write
uORBDevices_nuttx.hpp :
uORBDevices_posix.cpp :
uORBDevices_posix.hpp :
uORBMain.cpp : uORB入口
uORBManager.hpp : uORB功能函数实现头文件
uORBManager_nuttx.cpp : uORB功能函数实现(Nuttx)
uORBManager_posix.cpp : uORB功能函数实现(Posix)
uORBTest_UnitTest.cpp : uORB测试
uORBTest_UnitTest.hpp : uORB测试头文件,包括主题定义和声明等
uORBUtiles.cpp :
uORBUtiles.hpp :

3 常用函数功能解析

int poll(struct pollfd fds[], nfds_t nfds, int timeout)

功能:监控文件描述符(多个);
说明:timemout=0,poll()函数立即返回而不阻塞;timeout=INFTIM(-1),poll()会一直阻塞下去,直到检测到return > 0;
参数:
    fds:struct pollfd结构类型的数组;
    nfds:用于标记数组fds中的结构体元素的总数量;
    timeout:是poll函数调用阻塞的时间,单位:毫秒;
返回值:
    >0:数组fds中准备好读、写或出错状态的那些socket描述符的总数量;
    ==0:poll()函数会阻塞timeout所指定的毫秒时间长度之后返回;
    -1:poll函数调用失败;同时会自动设置全局变量errno;

int orb_subscribe(const struct orb_metadata *meta)

功能:订阅主题(topic);
说明:即使订阅的主题没有被公告,但是也能订阅成功;但是在这种情况下,却得不到数据,直到主题被公告;
参数:
    meta:uORB元对象,可以认为是主题id,一般是通过ORB_ID(主题名)来赋值;
返回值:
    错误则返回ERROR;成功则返回一个可以读取数据、更新话题的句柄;如果待订阅的主题没有定义或声明则会返回-1,然后会将errno赋值为ENOENT;
eg:
    int fd = orb_subscribe(ORB_ID(topicName));

int orb_copy(const struct orb_metadata *meta, int handle, void *buffer)

功能:从订阅的主题中获取数据并将数据保存到buffer中;
参数:
    meta:uORB元对象,可以认为是主题id,一般是通过ORB_ID(主题名)来赋值;
    handle:订阅主题返回的句柄;
    buffer:从主题中获取的数据;
返回值:
    返回OK表示获取数据成功,错误返回ERROR;否则则有根据的去设置errno;
eg:
    struct sensor_combined_s raw;
    orb_copy(ORB_ID(sensor_combined), sensor_sub_fd, &raw);

orb_advert_t orb_advertise(const struct orb_metadata *meta, const void *data)

功能:公告发布者的主题;
说明:在发布主题之前是必须的;否则订阅者虽然能订阅,但是得不到数据;
参数:
    meta:uORB元对象,可以认为是主题id,一般是通过ORB_ID(主题名)来赋值;
    data:指向一个已被初始化,发布者要发布的数据存储变量的指针;
返回值:错误则返回ERROR;成功则返回一个可以发布主题的句柄;如果待发布的主题没有定义或声明则会返回-1,然后会将errno赋值为ENOENT;
eg:
    struct vehicle_attitude_s att;
    memset(&att, 0, sizeof(att));
    int att_pub_fd = orb_advertise(ORB_ID(vehicle_attitude), &att);

int orb_publish(const struct orb_metadata *meta, orb_advert_t handle, const void *data)

功能:发布新数据到主题;
参数:
    meta:uORB元对象,可以认为是主题id,一般是通过ORB_ID(主题名)来赋值;
    handle:orb_advertise函数返回的句柄;
    data:指向待发布数据的指针;
返回值:OK表示成功;错误返回ERROR;否则则有根据的去设置errno;
eg: 
    orb_publish(ORB_ID(vehicle_attitude), att_pub_fd, &att);

int orb_set_interval(int handle, unsigned interval)

功能:设置订阅的最小时间间隔;
说明:如果设置了,则在这间隔内发布的数据将订阅不到;需要注意的是,设置后,第一次的数据订阅还是由起初设置的频率来获取,
参数:
    handle:orb_subscribe函数返回的句柄;
    interval:间隔时间,单位ms;
返回值:OK表示成功;错误返回ERROR;否则则有根据的去设置errno;
eg:
    orb_set_interval(sensor_sub_fd, 1000);

orb_advert_t orb_advertise_multi(const struct orb_metadata *meta, const void *data, int *instance, int priority)

功能:设备/驱动器的多个实例实现公告,利用此函数可以注册多个类似的驱动程序;
说明:例如在飞行器中有多个相同的传感器,那他们的数据类型则类似,不必要注册几个不同的话题;
参数:
    meta:uORB元对象,可以认为是主题id,一般是通过ORB_ID(主题名)来赋值;
    data:指向一个已被初始化,发布者要发布的数据存储变量的指针;
    instance:整型指针,指向实例的ID(从0开始);
    priority:实例的优先级。如果用户订阅多个实例,优先级的设定可以使用户使用优先级高的最优数据源;
返回值:
    错误则返回ERROR;成功则返回一个可以发布主题的句柄;如果待发布的主题没有定义或声明则会返回-1,然后会将errno赋值为ENOENT;
eg:
    struct orb_test t;
    t.val = 0;
    int instance0;
    orb_advert_t pfd0 = orb_advertise_multi(ORB_ID(orb_multitest), &t, &instance0, ORB_PRIO_MAX);

int orb_subscribe_multi(const struct orb_metadata *meta, unsigned instance)

功能:订阅主题(topic);
说明:通过实例的ID索引来确定是主题的哪个实例;
参数:
    meta:uORB元对象,可以认为是主题id,一般是通过ORB_ID(主题名)来赋值;
    instance:主题实例ID;实例ID=0与orb_subscribe()实现相同;
返回值:
    错误则返回ERROR;成功则返回一个可以读取数据、更新话题的句柄;如果待订阅的主题没有定义或声明则会返回-1,然后会将errno赋值为ENOENT;
eg:
    int sfd1 = orb_subscribe_multi(ORB_ID(orb_multitest), 1);

int orb_unsubscribe(int handle)

功能:取消订阅主题;
参数:
    handle:主题句柄;
返回值:
    OK表示成功;错误返回ERROR;否则则有根据的去设置errno;
eg:
    ret = orb_unsubscribe(handle);

int orb_check(int handle, bool *updated)

功能:订阅者可以用来检查一个主题在发布者上一次更新数据后,有没有订阅者调用过ob_copy来接收、处理过;
说明:如果主题在在被公告前就有人订阅,那么这个API将返回“not-updated”直到主题被公告。可以不用poll,只用这个函数实现数据的获取。
参数:
    handle:主题句柄;
    updated:如果当最后一次更新的数据被获取了,检测到并设置updated为ture;
返回值:
    OK表示检测成功;错误返回ERROR;否则则有根据的去设置errno;
eg:
    if (PX4_OK != orb_check(sfd, &updated)) {
        return printf("check(1) failed");
    }
    if (updated) {
        return printf("spurious updated flag");
    }

    //or

    bool updated;
    struct random_integer_data rd;

    /* check to see whether the topic has updated since the last time we read it */
    orb_check(topic_handle, &updated);

    if (updated) {
        /* make a local copy of the updated data structure */
        orb_copy(ORB_ID(random_integer), topic_handle, &rd);
        printf("Random integer is now %d\n", rd.r);
    }

int orb_stat(int handle, uint64_t *time)

功能:订阅者可以用来检查一个主题最后的发布时间;
参数:
    handle:主题句柄;
    time:存放主题最后发布的时间;0表示该主题没有发布或公告;
返回值:
    OK表示检测成功;错误返回ERROR;否则则有根据的去设置errno;
eg:
    ret = orb_stat(handle,time);

int orb_exists(const struct orb_metadata *meta, int instance)

功能:检测一个主题是否存在;
参数:
    meta:uORB元对象,可以认为是主题id,一般是通过ORB_ID(主题名)来赋值;
    instance:ORB 实例ID;
返回值:
    OK表示检测成功;错误返回ERROR;否则则有根据的去设置errno;
eg:
    ret = orb_exists(ORB_ID(vehicle_attitude),0);

int orb_priority(int handle, int *priority)

功能:获取主题优先级别;
参数:
    handle:主题句柄;
    priority:存放获取的优先级别;
返回值:
    OK表示检测成功;错误返回ERROR;否则则有根据的去设置errno;
eg:
    ret = orb_priority(handle,&priority);

4 例程

4.1 例程前准备工作
  • archives已编译完成(注:2015/10/6号后改为cmake编译系统,不再需要编译archives了);

  • 添加一个新的模块

    • 在Firmware/src/modules中添加一个新的文件夹,命名为px4_simple_app
    • 在px4_simple_app文件夹中创建module.mk文件,并输入以下内容:
      • MODULE_COMMAND = px4_simple_app
      • SRCS = px4_simple_app.c
    • 在px4_simple_app文件夹中创建px4_simple_app.c文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* @file px4_simple_app.c
* Minimal application example for PX4 autopilot.
*/


#include <nuttx/config.h>
#include <stdio.h>
#include <errno.h>

__EXPORT int px4_simple_app_main(int argc, char *argv[]);

int px4_simple_app_main(int argc, char *argv[])
{

printf("Hello Sky!\n");
return OK;
}
  • 注册新添加的应用到NuttShell中,并编译上传
    • Firmware/makefiles/config_px4fmu-v2_default.mk文件中添加如下内容:
      • MODULES += modules/px4_simple_app
    • 编译
      • make clean
      • make px4fmu-v2_default
    • 上传到板子中
      • make upload px4fmu-v2_default
  • 在QGC 中的Terminal(终端)中运行新应用
    • nsh > px4_simple_app

 接下来的代码修改均是基于此应用。

4.2 订阅主题

 sensor_combined主题是官方提供的通用接口标准主题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/**
* @file px4_simple_app.c
* Minimal application example for PX4 autopilot
*/


#include <nuttx/config.h>
#include <unistd.h>
#include <stdio.h>
#include <poll.h>

#include <uORB/uORB.h>
#include <uORB/topics/sensor_combined.h>

__EXPORT int px4_simple_app_main(int argc, char *argv[]);

int px4_simple_app_main(int argc, char *argv[])
{

printf("Hello Sky!\n");

/*订阅sensor_combined 主题*/
int sensor_sub_fd = orb_subscribe(ORB_ID(sensor_combined));

/*一个应用可以等待多个主题,在这里只等待一个主题*/
struct pollfd fds[] = {
{ .fd = sensor_sub_fd, .events = POLLIN },
/* 这里可以添加更多的文件描述符;
* { .fd = other_sub_fd, .events = POLLIN },
*/

};

int error_counter = 0;

while (true) {
/*poll函数调用阻塞的时间为1s*/
int poll_ret = poll(fds, 1, 1000);

/*处理poll返回的结果 */
if (poll_ret == 0) {
/* 这表示时间溢出了,在1s内没有获取到发布者的数据 */
printf("[px4_simple_app] Got no data within a second\n");
} else if (poll_ret < 0) {
/* 出现问题 */
if (error_counter < 10 || error_counter % 50 == 0) {
/* use a counter to prevent flooding (and slowing us down) */
printf("[px4_simple_app] ERROR return value from poll(): %d\n"
, poll_ret);
}
error_counter++;
} else {

if (fds[0].revents & POLLIN) {
/*从文件描述符中获取订阅的数据*/
struct sensor_combined_s raw;
/* copy sensors raw data into local buffer */
orb_copy(ORB_ID(sensor_combined), sensor_sub_fd, &raw);
printf("[px4_simple_app] Accelerometer:\t%8.4f\t%8.4f\t%8.4f\n",
(double)raw.accelerometer_m_s2[0],
(double)raw.accelerometer_m_s2[1],
(double)raw.accelerometer_m_s2[2]);
}
/* 如果有更多的文件描述符,可以这样:
* if (fds[1..n].revents & POLLIN) {}
*/

}
}

return 0;
}
测试需要在QGC终端启动uORB和初始化该传感器,最后运行应用:
    nsh > uorb start
    nsh > sh /etc/init.d/rc.sensors
    nsh > px4_simple_app &
4.3 订阅和发布主题

 sensor_combined主题是官方提供的通用接口标准主题。
 vehicle_attitude主题是官方提供的通用接口标准主题。

 程序流程图如下:

pub_sub_test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/**
* @file px4_simple_app.c
* Minimal application example for PX4 autopilot
*/


#include <nuttx/config.h>
#include <unistd.h>
#include <stdio.h>
#include <poll.h>

#include <uORB/uORB.h>
#include <uORB/topics/sensor_combined.h>
#include <uORB/topics/vehicle_attitude.h>

__EXPORT int px4_simple_app_main(int argc, char *argv[]);

int px4_simple_app_main(int argc, char *argv[])
{

printf("Hello Sky!\n");

/* 订阅 sensor_combined 主题 */
int sensor_sub_fd = orb_subscribe(ORB_ID(sensor_combined));
orb_set_interval(sensor_sub_fd, 1000);

/* 公告 attitude 主题 */
struct vehicle_attitude_s att;
memset(&att, 0, sizeof(att));
int att_pub_fd = orb_advertise(ORB_ID(vehicle_attitude), &att);

/*一个应用可以等待多个主题,在这里只等待一个主题*/
struct pollfd fds[] = {
{ .fd = sensor_sub_fd, .events = POLLIN },
/* there could be more file descriptors here, in the form like:
* { .fd = other_sub_fd, .events = POLLIN },
*/

};

int error_counter = 0;

while (true) {
/* wait for sensor update of 1 file descriptor for 1000 ms (1 second) */
int poll_ret = poll(fds, 1, 1000);

/* handle the poll result */
if (poll_ret == 0) {
/* this means none of our providers is giving us data */
printf("[px4_simple_app] Got no data within a second\n");
} else if (poll_ret < 0) {
/* this is seriously bad - should be an emergency */
if (error_counter < 10 || error_counter % 50 == 0) {
/* use a counter to prevent flooding (and slowing us down) */
printf("[px4_simple_app] ERROR return value from poll(): %d\n"
, poll_ret);
}
error_counter++;
} else {

if (fds[0].revents & POLLIN) {
/* obtained data for the first file descriptor */
struct sensor_combined_s raw;
/* copy sensors raw data into local buffer */
orb_copy(ORB_ID(sensor_combined), sensor_sub_fd, &raw);
printf("[px4_simple_app] Accelerometer:\t%8.4f\t%8.4f\t%8.4f\n",
(double)raw.accelerometer_m_s2[0],
(double)raw.accelerometer_m_s2[1],
(double)raw.accelerometer_m_s2[2]);

/* 赋值 att 并且发布这些数据给其他的应用 */
att.roll = raw.accelerometer_m_s2[0];
att.pitch = raw.accelerometer_m_s2[1];
att.yaw = raw.accelerometer_m_s2[2];
orb_publish(ORB_ID(vehicle_attitude), att_pub_fd, &att);
}
/* there could be more file descriptors here, in the form like:
* if (fds[1..n].revents & POLLIN) {}
*/

}
}

return 0;
}
4.4 创建自己的主题

  官方提供的通用接口标准主题都放在了topics文件夹下了。如果要定义我们自己的主题,比如我们新添加了超声波传感器,为了将超声波传感器的数据发布出去给其他需要的应用订阅,那么久需要创建我们的主题了。

  • 主题头文件(mytopic.h)
    • ORB_DECLARE(myTopicName);//声明一个主题
    • 定义一个存放发布数据的结构体;
  • 主题源文件(mytopic.c)
    • ORB_DEFINE(myTopicName);//定义一个主题
    • 初始化发布数据
    • 公告主题
    • 发布主题数据

mytopic.h

1
2
3
4
5
6
7
/* 声明自定义主题,名字可以自定义,不过最好具有一定的意义,如下为随机产生整数数据 */
ORB_DECLARE(random_integer);

/* 定义要发布的数据结构体 */
struct random_integer_data {
int r;
};

mytopic_publish.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <topic.h>

/* 定义主题 */
ORB_DEFINE(random_integer);

/* 待发布的主题句柄 */
static int topic_handle;

int init()
{

/* 随机产生一个数初始化数据结构体 */
struct random_integer_data rd = { .r = random(), };

/* 公告主题 */
topic_handle = orb_advertise(ORB_ID(random_integer), &rd);
}

int update_topic()
{

/* 产生新的数据 */
struct random_integer_data rd = { .r = random(), };

/* 发布主题,更新数据 */
orb_publish(ORB_ID(random_integer), topic_handle, &rd);
}

  对于订阅者来说,就可以参考主题「4.2 订阅例程」了。不过这里还是提供下简单处理例程:

mytopic_subscriber.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <topic.h>

/* 订阅主题的句柄*/
static int topic_handle;

int init()
{

/* 订阅主题 */
topic_handle = orb_subscribe(ORB_ID(random_integer));
}

void check_topic()
{

bool updated;
struct random_integer_data rd;

/* check to see whether the topic has updated since the last time we read it */
orb_check(topic_handle, &updated);

if (updated) {
/* make a local copy of the updated data structure */
orb_copy(ORB_ID(random_integer), topic_handle, &rd);
printf("Random integer is now %d\n", rd.r);
}
}

5 参考资料

  http://www.pixhawk.com/start?id=zh/dev/px4_simple_app
  http://www.pixhawk.com/dev/shared_object_communication
  http://blog.arm.so/armteg/pixhawk/183-0503.html
  http://pixhawk.org/start?id=dev/software_architecture
  http://www.pixhawk.com/dev/add_uorb_topic?s[]=objects&s[]=common

PX4/Pixhawk之快速成为开发者(Windows)

发表于 2015-07-09   |   分类于 无人机   |     |   阅读次数

详见博客:http://blog.csdn.net/freeape/article/details/46820807

快速成为开发者入门教程(翻译)—官方

安装版本控制系统git

  • 安装MSysGIT 安装完成后,配置GIT。

    • 安装注意 : 安装过程中除了下面一步外,其他的步骤都采用默认安装。
  • GIT Bash中文乱码

    1
    2
    D:\Program Files (x86)\Git\etc中的git-completion.bash文件最后一行加上:
    alias ls='ls --show-control-chars --color=auto'
  • 在GIT终端输入下面的命令可以提高GIT的输出格式

    1
    2
    3
    4
    git config --global color.branch auto
    git config --global color.diff auto
    git config --global color.interactive auto
    git config --global color.status auto
  • 配置用户名和邮箱(GITHUB注册用户名和邮箱,在使用git push时有用,可以不用配置)

    1
    2
    git config --global user.name "Your Name"
    git config --global user.email you@example.com

工具链安装和配置

  只需要安装工具链就够了,源码什么的可以到https://github.com/PX4/Firmware上下载,也可以通过git命令行下载

  • 本次使用的工具链版本为px4_toolchain_installer_v14_win.exe
  • 安装好后,路径为D:\px4(默认的为C:\px4);附带的PX4的驱动程序也安装好了(win8需另外配置);
  • 通过PX4 Toolchain下载PX4源码:开始菜单—>应用程序—>PX4 Toolchain—>PX4 Software Download。或者直接到github.com网站上去下载,前提是需要指导仓库地址,这样就可以省去这一步和下一步。
  • 下载的源码包含路径和文件夹:

    1
    2
    3
    4
    5
    px4
    Firmware – PX4 固件(所有模块),包括MAVLink
    NuttX – NuttX实时操作系统(RTOS)
    libopencm3 – 可选: 开源Cortex Mx 库, 仅仅用于引导(bootloaders)
    Bootloader – 可选: Bootloaders, 通常不需要操作、修改。
  • 配置eclipse(可以方便查看固件源码以及交互式编译烧写固件)

1
2
3
4
5
6
安装了JAVA,注意不是JRE,JAVA(JDK)是JAVA程序运行环境,JRE是开发工具包;
注意:当windows操作系统为64位时,JAVA的安装位置在c:\Program Files (x86)\Java\jre_xxx,而不是通常的c:\Program files\Java\jre_xxx,因此必须在eclipse文件夹中找到eclipse.ini文件添加“-vm c:\Program Files (x86)\Java\jre_xxx\javaw.exe” ;或者配置JAVA的环境变量也可以。
JAVA(jre not jdk)环境变量配置成功标志:
安装完PX4 Toolchain后就已经集成了Eclipse,当然我们也可以另外去下载Eclipse,不过要配置环境变量和工具链。打开Eclipse:开始菜单--->所有程序--->PX4 Toolchain--->PX4 Eclipse。第一次启动时,选择好workspace,并勾选Use this as the default and...。
建立工程文件。File--->New--->Makefile Project...。然后点击Browse...,到D:\px4\Firmware,并选择Cross GCC,点击Finish。
可以在右边板块中"Make Target",选中根文件夹,可以创建新的make Target(绿色圆形按钮)。
  • 各make target 说明
    1
    2
    3
    4
    5
    6
    7
    8
    all – builds the autopilot software (depends on archives)
    archives– builds the NuttX OS(编译实时系统NuttX OS,时间需要很久)
    distclean– cleans everything, including the NuttX build
    clean – cleans only the application (autopilot) part,不会清除archives
    pixfmu-v2_default---FMU固件
    px4io-v2_default---IO固件
    upload px4fmu-v1_default – uploads to PX4FMU v1.x boards
    upload px4fmu-v2_default – uploads to PX4FMU v2.x boards

编译和刷固件

1
2
3
4
5
6
7
8
9
10
两种方式:
方式一,通过Eclipse,将第五步配置好后,可以先双击"distclean" ,然后是"archives",再然后是"all",最后双击"upload px4fmu-v1_default",将其编译好的文件上传到PX4FMU v1.x板子上。或者双击"upload px4fmu-v2_default",将其编译好的文件上传到PX4FMU v2.x板子上。上传到板子上的前提是各种连接均已配置好,如驱动、串口等。
方式二,通过控制台,开始菜单--->应用程序--->PX4 Toolchain--->PX4 console;
cd Firmware/
make distclean # Only needed after changes on header files, NuttX or a fresh GIT checkout / update
make archives # 这条命令仅仅是当使用了"make distclean"之后才用,编译Nuttx系统。
make px4fmu-v2_default #编译固件,版本为fmu-v2
make upload px4fmu-v2_default
方式三,通过地面站刷固件,如QGroundControl(QGC)或者Mission Plan,可以刷官方稳定版的固件或者自己编译的固件都可以。
用工具链的控制台编译固件之后:

注:自2015年10月6号后,编译系统有所更改(变为cmake方式,不再是通过makefile),请参见文章:http://blog.csdn.net/freeape/article/details/49024053

STM8S之TIM2产生PWM与TIM1定时器周期中断的时钟问题

发表于 2015-07-08   |   分类于 硬件   |     |   阅读次数

详见博客:http://blog.csdn.net/freeape/article/details/46802003

1 问题

  在下面的测试程序中,如果将Init_CLK()函数中的 CLK_CKDIVR |= 0x08;去掉’|‘,则TIM1的功能实现跟预设定相同(10ms中断一次),但是TIM2的PWM频率就变高了;如果加上,则TIM2的功能实现跟预设定相同(产生1Hz的PWM),但是TIM1的周期就变长了;
  尝试了很多测试,均无效(买的开发板和另一块gs自画板测试也都一样)。

2 尝试

  因为STM8S默认使用内部16M高速RC振荡器,且8分频,则系统启动主时钟为2M。即CLK_CKDIVR = 0X18;,如果再去赋值CLK_CKDIVR |= 0X08; 则主时钟还是不变即0X18,但是如果赋值为CLK_CKDIVR = 0X08;,则主时钟就会改变,变为8M。但是TIM1和TIM2的Fmaster时钟应该是一样的,这样的赋值应该会对TIM1和TIM2都会产生影响,但是两个赋值不同,实现功能的配置正确,而总只有一个能按预设定工作,这到底是怎么回事呢?
  发现设置为CLK_CKDIVR = 0X08时(8M),TIM1能按预设定工作,而TIM2的工作频率却明显快了,像是快了一倍,带着这个发现,我就将现在的TIM2的Fmaster时钟频率当做为TIM1的一倍,即16M,再去重新配置TIM2的寄存器,再编译、下载进单片机,居然和TIM1实现的功能相同了。这又到底是怎么回事呢?难道TIM2的Fmaster时钟总是TIM1的Fmaster时钟的一倍吗?又去尝试几种不同的CLK_CKDIVR(当然则TIM1的Fmaster频率不能超过8M)。
  接下来测试了 CLK_CKDIVR = 0X10;(4M), CLK_CKDIVR = 0X18;(2M)均是如此。是不是猜想正确了,还是想不明白是怎么回事。看着STM8S的时钟树怎么也想不明白。

3 测试程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/* MAIN.C file
function : TIM1定时器周期中断实现LED1周期为2S的亮灭;TIM2用PWM波实现同样的功能;
TIM1 10ms定时器周期中断;
TIM2_CH3 PD2 输出周期为1Hz的PWM;
problem :
(1) 在时钟初始化函数Init_CLK()里,|= 与=的问题,加上‘|’
与不加,TIM1和TIM2总有其中一个跟预期实现不一样。
*/


#include<stm8s003f3p.h>
_Bool PD2 @PD_ODR:2;
_Bool PC7 @PC_ODR:7;
#define LED1 PD2
#define LED2 PC7
void Init_CLK(void)
{

//当是'|='时,实际CLK = 2M
//当是' ='时,实际CLK = 8M
CLK_CKDIVR = 0X08;
}
void Init_GPIO(void)
{

/*LED/PWM 配置为推挽输出*/
PD_DDR |= 0X04; //PD2--PWM输出---连接LED1
PD_CR1 |= 0X04;
PD_CR2 &= 0XFD;

PC_DDR |= 0X80; //PC7---LED2;
PC_CR1 |= 0X80;
PC_CR2 &= 0X7F;
}
void Init_TIM1(void)
{

_asm("sim");
TIM1_IER = 0x00;
TIM1_CR1 = 0x00;
TIM1_EGR |= 0x01;
TIM1_PSCRH = 199/256; //2M系统时钟经预分频f=fck/(PSCR+1) TIM1 为16位分频器
TIM1_PSCRL = 199%256; //f=8M/(199+1)=40000Hz,每个计数周期1ms

TIM1_CNTRH = 0x00;
TIM1_CNTRL = 0x00;

TIM1_ARRH = 400/256; // 自动重载寄存器ARR=400
TIM1_ARRL = 400%256; // 每记数400次产生一次中断,即10ms
TIM1_CR1 |= 0x81;
TIM1_IER |= 0x01;
}
/*TIM1---8M TIM2---4M*/
void Init_TIM2(void)
{

//TIM2_IER = 0x00; //禁止各种中断
//TIM2_EGR |= 0X01; //如果要 新的 预分频值生效,必须产生更新事件

TIM2_CCMR3 |= 0X60; //设置定时器2三通道(PD2)输出比较三模式
TIM2_CCMR3 |= 0X08; //输出比较3预装载使能

TIM2_CCER2 |= 0x03; //通道3使能,低电平有效,配置为输出

//初始化时钟分频器为1,即计数器的时钟频率为Fmaster=8M/128=0.0625MHZ
TIM2_PSCR = 0X07;
//初始化自动装载寄存器,决定PWM 方波的频率,Fpwm=0.0625M/62500=1HZ
TIM2_ARRH = 62500/256;
TIM2_ARRL = 62500%256;
//初始化比较寄存器,决定PWM 方波的占空比:50%
TIM2_CCR3H = 31250/256;
TIM2_CCR3L = 31250%256;
//启动计数;更新中断失能
TIM2_CR1 = 0x01;
}
main()
{
Init_CLK();
Init_GPIO();
Init_TIM1();
Init_TIM2();
LED1 = 1;LED2 = 1;
_asm("rim");
while (1);
}
@far @interrupt void TIM1_UPD_OVF_TRG_BRK_IRQHandler(void)
{

static unsigned int i = 0;

TIM1_SR1 &=~(0x01);
++i;
if(100 == i){
LED2 = ~LED2;
i = 0;
}
}

STM8S之独立按键IO口设置及按下事件问题

发表于 2015-07-07   |   分类于 硬件   |     |   阅读次数

详见博客:http://blog.csdn.net/freeape/article/details/46793457

1 GPIO设置

                STM8 I/O 口引脚配置表

Px_DDR Px_CR1 Px_CR2 I/O 方式 引脚状态
0 0 0 输入 悬浮输入
0 0 1 输入 上拉输入
0 1 0 输入 中断悬浮输入
0 1 1 输入 中断上拉输入
1 0 0 输出 开漏输出
1 1 0 输出 推挽输出
1 x 1 输出 输出(最快速度为10MHZ)

对STM8S的IO配置,我们只需要操作五个寄存器就行了:

  • 输出数据寄存器 (ODR)
  • 输入数据寄存器 (IDR)
  • 数据方向寄存器 (DDR)
  • 控制寄存器1(CR1)
  • 控制寄存器2(CR2)

2 按键检测

  在做独立按键检测的时候,设置成上拉输入不能实现功能,设置成中断悬浮输入就可以了。
  两次短按键之间的时间间隔大约在300ms~600ms之间。一次短按键按下的时间大约在14ms~26ms之间;

2.1 连续按键检测(短按+长按)

定时器TIM1 + 按键 = 连续按键检测(短按键+长按键)
两个标记:

  • 短按+长按—flag0
  • 短按后时间在规定范围之内—flag1

如果两个标记都满足,则开/关电源;每次按键都启动按键计时;

当两次按键的时间间隔在300ms~600ms之间的时候,怎么得到第一次(短按)和第二次(长按)按键之间的时间呢?
  如果判断了是短按,则开启计时,同时将第一次短按flag置一,超过600ms停止计时并清零,等待第二次的按键;有了第二次的按键之后,在短按置一flag条件中中断计时,判断是否在规定范围之内的时间间隔,是则将flag1置一;并接下来判断该按键是长按还是短按,如果是长按,则将flag0置一,满足flag0、flag1均置一,则是连续按键。

3 关键代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/*
return =
0 : No Key Press
1 : Single Key Press
2 : 将长按作为一次单独按键,并执行Single Key Press功能
3 : Double Key Press
*/

unsigned int Key_Scan(void)
{

unsigned int count = 0;

if(0 == KEY)
{
Delay(2);
if(0 == KEY)
{
if(1 == keytimesFlag)
{
afterOnceShortPressFlag = 0;
if((afterOnceShortPressCount <=30)
&& (afterOnceShortPressCount > 15))
{
isSetTimeFlag = 1;
}
else isSetTimeFlag = 0;
}
keyFlag = 1;
while(!KEY);
keyFlag = 0;
count = keyCount;
keyCount = 0;
}
else
{
count = 0;
}
}

if(count >= 200)
{
if(1 == isSetTimeFlag)
{
isSetTimeFlag = 0;
keytimesFlag = 0;
return 3;
}
else
{
keytimesFlag = 0;
return 2;
}
}
else if(count >= 4)
{
afterOnceShortPressFlag = 1;
afterOnceShortPressCount = 0;
keytimesFlag = 1;
return 1;
}
else return 0;
}

在定时周期为10ms的定时器中断函数里:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@far @interrupt void TIM1_UPD_OVF_TRG_BRK_IRQHandler(void)
{

TIM1_SR1 &=~(0x01);
if(1 == keyFlag)
{
++keyCount;
}
else ;
if(1 == afterOnceShortPressFlag)
{
++afterOnceShortPressCount;
if(afterOnceShortPressCount > 80)
{
afterOnceShortPressFlag = 0;
afterOnceShortPressCount = 0;
keytimesFlag = 0;
}
}
else ;
}

  另参见使用外部中断长按键识别:使用外部中断识别长按键

STM8S之定时器产生PWM(TIM2)

发表于 2015-07-06   |   分类于 硬件   |     |   阅读次数

详见博客:http://blog.csdn.net/freeape/article/details/46778003

1 说明

  • 采用STM8S内部时钟(HSI);
  • PWM模式2;
  • 占空比为50%,频率为2Hz(方便测试LED灯);
  • PD2口外接LED灯,PD2口输出PWM波;
  • 系统时钟初始化很重要:CLK_CKDIVR |= 0x08;

2 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/*
TIM2_CH3 PWM
PD2 output
*/

#include <STM8S003F3P.h>

void CLK_init(void)
{

CLK_ICKR |= 0X01; //使能内部高速时钟 HSI
CLK_CKDIVR |= 0x08; //16M内部RC经2分频后系统时钟为8M
while(!(CLK_ICKR&0x02)); //HSI准备就绪
CLK_SWR=0xe1; //HSI为主时钟源
}

void Init_Tim2(void)
{

TIM2_CCMR3 |= 0X70; //设置定时器2三通道(PD2)输出比较三模式
TIM2_CCMR3 |= 0X04; //输出比较3预装载使能

TIM2_CCER2 |= 0x03; //通道3使能,低电平有效,配置为输出

//初始化时钟分频器为1,即计数器的时钟频率为Fmaster=8M/64=0.125MHZ
TIM2_PSCR = 0X06;
//初始化自动装载寄存器,决定PWM 方波的频率,Fpwm=0.125M/62500=2HZ
TIM2_ARRH = 62500/256;
TIM2_ARRL = 62500%256;
//初始化比较寄存器,决定PWM 方波的占空比:5000/10000 = 50%
TIM2_CCR3H = 31250/256;
TIM2_CCR3L = 31250%256;


// 启动计数;更新中断失能
TIM2_CR1 |= 0x81;
//TIM2_IER |= 0x00;
}

void Init_GPIO(void)
{

/*设置为推挽输出,PD2接了LED灯*/
PD_DDR |= 0X04; //设置PD2端口为输出模式
PD_CR1 |= 0X04; //设置PD2端口为推挽输出模式
PD_CR2 &= 0XFD;

PA_DDR |= 0X08; //设置PA3端口为输出模式
PA_CR1 |= 0X08; //设置PA3端口为推挽输出模式
PA_CR2 |= 0XF7;
}

void main(void)
{

CLK_init();
Init_GPIO();
Init_Tim2();
while (1);
}

STM8S之IO复用配置(STVP方式)

发表于 2015-07-06   |   分类于 硬件   |     |   阅读次数

详见博客:http://blog.csdn.net/freeape/article/details/46773155

1 说明

: STM8S的IO复用用程序代码配置起来比较麻烦,一般是操作flash来操作option byte字节,配置寄存器更加麻烦,可以使用STM 标准外设驱动库来设置。本文使用一种界面配置的方式来配置IO复用管脚,即使用STVP来配置。 因为FLASH保存的数据是掉电不丢失的,先用STVP把Option Bytes擦写好后,再用STVD仿真器烧写程序就可实现IO复用了。

  程序方式配置IO复用见这里

文件下载

2 配置流程

: 【step1】打开STVP软件

stvp配置界面
: 【step2】打开我们需要下载的xxx.s19文件,CTRL+F5(File->Ram Exec)
: 【step3】配置需要复用的引脚
IO口的复用功能主要配置在于AFR0-AFR7。这里我们配置定时器TIM2_CH3通道的PWM输出管脚复用,默认是PD2为输出,将其配置成复用为PA3输出。如上图,在AFR1中的下拉菜单中选择PA3即可。
: 【step4】下载程序到STM8S中,Progam->All tabs。这样就实现了IO复用配置。

3 测试程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/*
TIM2_CH3 PWM
PD2 output
*/

#include <STM8S103F3P.h>

void CLK_init(void)
{

CLK_ICKR |= 0X01; //使能内部高速时钟 HSI
CLK_CKDIVR |= 0x08; // 16M内部RC经2分频后系统时钟为8M
while(!(CLK_ICKR&0x02));//HSI准备就绪
CLK_SWR=0xe1; //HSI为主时钟源
}

void Init_Tim2(void)
{

TIM2_CCMR3 |= 0X70; //设置定时器2三通道(PD2)输出比较三模式
TIM2_CCMR3 |= 0X04; //输出比较3预装载使能

TIM2_CCER2 |= 0x03; //通道3使能,低电平有效,配置为输出

// 初始化时钟分频器为1,即计数器的时钟频率为Fmaster=8M/64=0.125MHZ
TIM2_PSCR = 0X06;
//初始化自动装载寄存器,决定PWM 方波的频率,Fpwm=0.125M/62500=2HZ
TIM2_ARRH = 62500/256;
TIM2_ARRL = 62500%256;
//初始化比较寄存器,决定PWM 方波的占空比:5000/10000 = 50%
TIM2_CCR3H = 31250/256;
TIM2_CCR3L = 31250%256;


// 启动计数;更新中断失能
TIM2_CR1 |= 0x81;
//TIM2_IER |= 0x00;
}

void Init_GPIO(void)
{

/*设置为推挽输出,PD2接了LED灯*/
PD_DDR |= 0X04; //设置PD2端口为输出模式
PD_CR1 |= 0X04; //设置PD2端口为推挽输出模式
PD_CR2 &= 0XFD;

PA_DDR |= 0X08; //设置PA3端口为输出模式
PA_CR1 |= 0X08; //设置PA3端口为推挽输出模式
PA_CR2 |= 0XF7;
}

void main(void)
{

CLK_init();
Init_GPIO();
Init_Tim2();
while (1);
}

Hexo && Github 建立个人博客

发表于 2015-04-01   |   分类于 文档格式   |     |   阅读次数
采用Hexo+github搭建个人博客,从为啥要写博客和建立个人博客的方式出发,一步步到流行的免费的建立个人博客的Hexo方式,建立个人唯美简洁的博客。
阅读全文 »

Hello World

发表于 2015-04-01   |   分类于 编程语言   |     |   阅读次数
1
2
3
4
5
6
7
8
9
10
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{

cout << "hello world!" << endl;

return 0;
}
12
依成名

依成名

19 日志
4 分类
7 标签
GitHub CSDN Weibo
© 2016 依成名
由 Hexo 强力驱动
主题 - NexT.Pisces