zoukankan      html  css  js  c++  java
  • 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] = '';
                // 将内容写回到串口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.
    "); 
            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);

    流程参照下图:

  • 相关阅读:
    seata 1.3.0 seata Global lock wait timeout
    Tika解析word文件
    我的第一款微信小程序:iteye附件下载器,希望大家好好爱惜
    读书《尸检报告》 [英]卡拉·瓦伦丁 / 中信出版集团2019-08
    读书《另一种选择》 [美] 谢丽尔·桑德伯格 / 中信出版集团2017-08
    读书《不朽的失眠》 张晓风 / 四川人民出版社2018-09
    Uniapp 修改内置组件样式无效解决方法
    Android studio中.9图片的含义及制作教程
    Diff算法
    js new一个对象的过程,实现一个简单的new方法
  • 原文地址:https://www.cnblogs.com/snake-hand/p/3153420.html
Copyright © 2011-2022 走看看