STM32 + RT Thread OS 学习笔记[二]
串口通讯例程
通过上面的练习,对STM32项目开发有了一个直观印象,接下来尝试对串口RS232进行操作。
1. 目标需求:
开机打开串口1,侦听上位机(使用电脑串口测试软件)发送的信息,然后原样输送到串口1。
2. 创建项目
a) 禁用Finsh和console
b) 默认情况下,项目文件包含了finsh,它使用COM1来通讯,另外,console输出(rt_kprintf)也使用了COM1。因此,在运行scons命令生成项目文件之前,修改rtconfig.h,禁用这两项。(下图L65, L70)
c) 生成项目文件
运行scons --target=mdk4 –s
打开生成的项目文件,可以看到,文件组finsh已经不再被包含进来了。
d) 创建echo.c
新建一个C文件echo.c,编写RT_Thread任务入口,COM1侦听,以及初始化函数。示例代码如下:
#include "echo.h" struct rx_msg
{
rt_device_t dev;
rt_size_t size;
}; static struct rt_messagequeue rx_mq;
static char uart_rx_buffer[64];
static char msg_pool[2048]; // 串口侦听回调函数
rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
struct rx_msg msg;
msg.dev = dev;
msg.size = size; // 将接收内容放入消息队列
rt_mq_send(&rx_mq, &msg, sizeof(struct rx_msg)); return RT_EOK;
} // 任务入口函数
void usr_echo_thread_entry(void* parameter)
{
struct rx_msg msg; rt_device_t device;
rt_err_t result = RT_EOK; // 从RT系统中获取串口1设备
device = rt_device_find("uart1");
if (device != RT_NULL)
{
// 指定接收串口内容的回调函数
rt_device_set_rx_indicate(device, uart_input);
// 以读写方式打开设备
rt_device_open(device, RT_DEVICE_OFLAG_RDWR);
} while(1)
{
// 从消息队列中获取被回调函数放入消息队列中的内容
result = rt_mq_recv(&rx_mq, &msg, sizeof(struct rx_msg), 50);
if (result == -RT_ETIMEOUT)
{
// timeout, do nothing
} if (result == RT_EOK)
{
rt_uint32_t rx_length; rx_length = (sizeof(uart_rx_buffer) - 1) > msg.size ?
msg.size : sizeof(uart_rx_buffer) - 1; rx_length = rt_device_read(msg.dev, 0, &uart_rx_buffer[0], rx_length);
uart_rx_buffer[rx_length] = '\0';
// 将内容写回到串口1
rt_device_write(device, 0, &uart_rx_buffer[0], rx_length);
}
}
}
// 串口例程初始化函数
void usr_echo_init()
{
rt_thread_t thread ; rt_err_t result;
// 创建消息队列,分配队列存储空间
result = rt_mq_init(&rx_mq, "mqt", &msg_pool[0], 128 - sizeof(void*), sizeof(msg_pool), RT_IPC_FLAG_FIFO); if (result != RT_EOK)
{
rt_kprintf("init message queue failed.\n");
return;
}
// 创建任务线程
thread = rt_thread_create("devt",
usr_echo_thread_entry, RT_NULL,
1024, 25, 7);
// 启动任务线程
if (thread != RT_NULL)
rt_thread_startup(thread);
}
在application.c中加入初始化代码(echo.h略)
L189:usr_echo_init()
;
在开始编译前,还要修改board.c,注释掉第183行,不然将报错。因为我们禁用了console,所以不需要设置console输出设备。
e) 测试
编译,下载,测试。
3. 程序分析
a) 内存分布
查看编译生成的 obj/rtthread-stm32.map文件,可以看到代码及常量,被下载到芯片的0x8000000地址段,最前面的是中断矢量表,第一个中断地址是RESET,矢量表共0x130个字节。
有初始值的变量定义,从地址段0x20000000开始
对应的Stm32内存映射表,代码和常量被下载到Flash,已初始化变量定位到SRAM(可能是下载到Flash,开机初始化后复制到RAM,而不是直接下载到RAM,不然下次运行,初始值可能已被修改)
这是MDK中芯片内存区域的地址分配
这是J-Link对芯片的定义,内存是512K,类型是On-chip Flash,地址空间从0x08000000到0x0807FFFF
b) 程序运行流程
开机后,从Flash 0x080000000处的中断矢量表,取得RESET中断的处理函数入口地址,跳转到入口函数开始执行RESET中断服务,如下图,RESET中断服务函数定义在startup_stm32f10x_hd.s中,先执行了stm32类库中的SystemInit(),再然后转到main()函数。
SystemInit()主要是对芯片的基本设置,如时钟频率。
RT-Thread中,在BSP目录下提供了startup.c,包含了main()函数,它调用了同文件中的rtthread_startup(),再然后rtthread_startup()调用了rt_application_init(),rt_application_init()则在application.c中定义,用户代码就从这里开始。
另外还有一个重要文件是stm32f10x_it.c,这里面定义了中断服务例程,中断矢量表中的地址指向这个文件中相应的服务函数入口地址。比如,我们的串口1收到上位机的消息后,会产生USART1_IRQ,这时芯片就会在0x08000000开始的中断向量表中找到USART1_IRQHandler()的入口地址,跳转后开始执行中断服务函数USART1_IRQHandler()。
当然,要产生中断,需要在初始化代码中开启中断。
<echo.c>
上面贴出了这个文件的源代码,除了uart_input(),其它都比较直观。在RTT系统中,uart_input()只是USART1_IRQHandler()的一部分,在echo.c的初始化代码中,被注册为uart1这个device(RTT封装对象)的回调函数:
L34:rt_device_set_rx_indicate(device, uart_input);
流程参照下图:
STM32 + RT Thread OS 学习笔记[二]的更多相关文章
- STM32 + RT Thread OS 学习笔记[三]
RTGUI 据说RTGUI是多线程的,因此与RT-Thread OS的耦合度较高,有可能要访问RT-Thread的线程控制块.如果要移植到其它OS,估计难度较大.目前还处于Alpha状态,最终将会包含 ...
- STM32 + RT Thread OS 学习笔记[四]
1. 补注 a) 硬件,打通通讯通道 若学习者购买了学习板,通常可以在学习板提供的示例代码中找到LCD的相关驱动代码,基本上,这里的驱动的所有代码都可以从里面找到. 从上面的示意图可见,M ...
- STM32 + RT Thread OS 串口通讯
1. 创建项目 a) 禁用Finsh和console b) 默认情况下,项目文件包含了finsh,它使用COM1来通讯,另外,console输出(rt_kprintf)也使用了COM1.因 ...
- JMX学习笔记(二)-Notification
Notification通知,也可理解为消息,有通知,必然有发送通知的广播,JMX这里采用了一种订阅的方式,类似于观察者模式,注册一个观察者到广播里,当有通知时,广播通过调用观察者,逐一通知. 这里写 ...
- muduo学习笔记(二)Reactor关键结构
目录 muduo学习笔记(二)Reactor关键结构 Reactor简述 什么是Reactor Reactor模型的优缺点 poll简述 poll使用样例 muduo Reactor关键结构 Chan ...
- python3.4学习笔记(二) 类型判断,异常处理,终止程序
python3.4学习笔记(二) 类型判断,异常处理,终止程序,实例代码: #idle中按F5可以运行代码 #引入外部模块 import xxx #random模块,randint(开始数,结束数) ...
- ZooKeeper学习笔记二:API基本使用
Grey ZooKeeper学习笔记二:API基本使用 准备工作 搭建一个zk集群,参考ZooKeeper学习笔记一:集群搭建. 确保项目可以访问集群的每个节点 新建一个基于jdk1.8的maven项 ...
- WPF的Binding学习笔记(二)
原文: http://www.cnblogs.com/pasoraku/archive/2012/10/25/2738428.htmlWPF的Binding学习笔记(二) 上次学了点点Binding的 ...
- AJax 学习笔记二(onreadystatechange的作用)
AJax 学习笔记二(onreadystatechange的作用) 当发送一个请求后,客户端无法确定什么时候会完成这个请求,所以需要用事件机制来捕获请求的状态XMLHttpRequest对象提供了on ...
随机推荐
- C#高级编程零散知识点
1.206-实现单链表的添加和插入 207-实现单链表的其他功能和 3.209-Lambda表达式 4.301-栈的介绍和BCL中的栈 4.501-进程和线程的概念[00_12_06][2015122 ...
- [html5] canvas 绘图:八卦图
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8" /> <title&g ...
- 【转】Tomcat7.0.42源代码运行环境搭建
转自:http://tyrion.iteye.com/blog/1903608 以前看过Tomcat5的一部分源代码,当时只看了个大概的启动分析,后来看了<How Tomcat Works> ...
- 一个简单链表的C++实现
/* LList.cpp * Author: Qiang Xiao * Time: 2015-07-12 */ #include<iostream> using namespace std ...
- [Swust OJ 794]--最近对问题(分治)
题目链接:http://acm.swust.edu.cn/problem/794/ Time limit(ms): 1000 Memory limit(kb): 10000 Description ...
- 字符串匹配算法(KMP)
字符串匹配运用很广泛,举个简单例子,我们每天登QQ时输入账号和密码,大家有没有想过账号和密码是怎样匹配的呢?登录需要多长时间和匹配算法的效率有直接的关系. 首先理解一下前缀和后缀的概念: 给出一个问题 ...
- C、C++中“*”操作符和“后++”操作符的优先级
假设有如下的定义 char carr[] = {"test"}; char cp = carr; 那么表达式 *cp++; 的右值是什么呢? 这个表达式在数组遍历的程序中非常常见, ...
- YII2 使用js
1.在 /backend/assets/ 中新建一个文件 CollectionAsset.php <?php /** * @link http://www.yiiframework.com/ * ...
- Xamarin.Android开发实践(四)
原文:Xamarin.Android开发实践(四) Xamarin.Android下获取与解析JSON 一.新建项目 1.新建一个Android项目,并命名为为NetJsonList 2.右击引用,选 ...
- cocos2dx进阶学习之CCSpriteBatchNode
继承关系 CCSpriteBatchNode -> CCNode, CCTextureProtocol 成员变量 inline CCTextureAtlas* getTextureAtlas(v ...