art硬件模块通常都有内置的硬件接收buff,比如51822的硬件uart模块图如下

因为通常接收到uart数据时都会做一些处理。比如保存到数据,或者对数据做一些判断之类的。

如果uart的波特率设置的很快,mcu的处理速度又不是很快或者是处理的过程比较耗时,那么当uart串口连续过来很多数据时,你在处理第一个数据时,后续的数据就可能丢失。所以通常uart模块都会内置一个很小的硬件接收buff,51822就内置了一个6字节的硬件接收buff。这样就能起到缓存作用。

Uart一般有两种工作方式  带流控和不带流控的。

带流控的工作方式 就是多了RTS(表示可以是否可以接收)和CTS(表示是否可以发送)引脚。

RTS引脚作为输出,由uart硬件模块自动控制。通常就是与上面所说的硬件Buff协调自动工作。比如在硬件buff已经填充了2个字节还剩下4个字节的时候RTS引脚就输出高电平的deactivate 信号(51822的工作方式就是这样),当buff中的数据都被读出后回复有效信号(低电平)。

CTS作为输入由外部输入。 当CTS有效时(低电平)模块可以发送,当为无效时,模块自动暂停发送,并在CTS恢复有效时继续发送。

那么将两个uart模块的rts与cts交叉相接。 如果发送方发送太快,当接收方的接收硬件buff已经存了两个字节后,接收方自动无效rts信号,表示不能接收了。 因为接收方rts与发送方cts相接。 所以发送方的cts也编程无效信号,于是发送方自动停止发送。

这样就保证了接收方不会接收溢出。流量控制也就是体现在这里。

这里说的是51822的,至于为什么收到两个字节还剩4个字节的buff就设置rts无效信号,表示自己不收了,手册中uart部分有详细的说明。

不同的mcu中的uart特性都会有一些各自的特性。不过原理都是跟上面一样的。

关于硬件流控不熟悉的可能不好理解,建议百度多看看相关说明。看看手册中的发送时序也有助于理解。

但是针对使用来说,不理解他的工作方式也不会有问题。只要将两个uart模块的cts,rts交叉相接,使能Uart的流控就可以了。因为是硬件流控,上面所说的过程都是硬件自动进行的。

 

不带流控的工作方式  就只需要tx ,rx,电源,地就行了。现代mcu工作频率一般都比较高,处理速度也比较快。所以不带流控的工作方式基本能满足很多很多应用了。

后面的教程代码也是使用不带流控的工作方式(我的板子上usb转串口芯片没接rts,cts)。

首先看一下寄存器的介绍:

ENABLE: 使能uart模块

PSELRXD: 选择GPIO作为RX引脚

PSELTXD: 选择GPIO作为TX引脚

RXD:接收寄存器。从改寄存器中获取uart接收到的数据

TXD: 将需要发送的数据填入该寄存器。

BAUDRATE: 设置波特率。只支持固定的几个波特率。具体查看手册。

CONFIG:用来使能校验(偶校验)和流控。

下面介绍uart轮训方式和中断方式具体操作。

新建工程选择自己板子使用的芯片型号:

教程中为了更直接的理解模块的使用。不使用sdk中提供的库函数,而直接操作寄存器来实现。

所以运行时环境勾选下必要的CMSIS下的CORE,Device下的Startup。因为用了gpio的函数 勾选一下nRF_Drivers下的nrf_gpio 就可以了。

然后配置jlink的设置(我的板子使用的是jlink的sw方式下载程序)。

创建main.c文件,然后添加到工程中

下面介绍main.c代码细节。

使用轮训方式工作

#include "nrf51.h"
#include "nrf_gpio.h"

#define RX_PIN       (11)
#define TX_PIN       (9)

void uart_init(void){
    //设置引脚输入输出方向
    nrf_gpio_cfg_input(RX_PIN, NRF_GPIO_PIN_NOPULL);
    nrf_gpio_cfg_output(TX_PIN);   

    //设置输入输出引脚。 流控引脚置位无效值
    NRF_UART0->PSELRXD = RX_PIN;
    NRF_UART0->PSELTXD = TX_PIN;
    NRF_UART0->PSELRTS = 0XFFFFFFFF;
    NRF_UART0->PSELCTS = 0XFFFFFFFF;

    NRF_UART0->BAUDRATE = 0x00275000;      //9600波特率
    NRF_UART0->CONFIG = ;             //不使用流控,不使用校验

    //清零一下事件
    NRF_UART0->EVENTS_RXDRDY = ;
    NRF_UART0->EVENTS_TXDRDY = ;  

    NRF_UART0->ENABLE = ;          //开启uart
    NRF_UART0->TASKS_STARTRX = ;   //使能接收
    NRF_UART0->TASKS_STARTTX = ;   //使能发送
}

uint8_t get_uart_data(void){
    uint8_t temp;
    //轮训等待直到收到数据
     ){
       ;
    }
    temp = NRF_UART0->RXD;
    NRF_UART0->EVENTS_RXDRDY = ;

    return temp;
}

void send_uart_byte(uint8_t data){
    uint8_t temp = data;

    NRF_UART0->EVENTS_TXDRDY = ;
    NRF_UART0->TXD = temp;
    //轮训等待直到数据发送完毕
    ){
       ;
    }
}

int main(void){
    uint8_t data;
    uart_init();
    //死循环等待输入,电脑串口输入 0-9字符后板子会发送回去在电脑串口上    //显示
     ){
       data = get_uart_data();
       ' ){
           send_uart_byte(data);
       }
    }
    ;
}

中断方式工作:

使用中断方式,需要设置使能事件触发中断

INTENSET:用来设置使能在 rx,tx完成事件发生时触发中断。

这里写的例子比较简单,也不方便使用。仅仅只是为了演示一下uart中断接收和发送。

例子中接收中断收到20字节数据就会置位一个 标志变量,main循环中检查这个标量被置位后就会启动发送。Main中只是发送了一个字节。因为每个字节发送完成后都会产生发送完成事件并进入中断。所以后续的19个字节 会在中断中依次发送。

如果需要在自己的工程中使用uart的中断发送和接收。应该参考nordic sdk中的app_uart_fifo.c中的实现, 使用一个状态机和两个缓冲buff来控制uart的接收和发送。

void uart_init(void){

    nrf_gpio_cfg_input(RX_PIN, NRF_GPIO_PIN_NOPULL);
    nrf_gpio_cfg_output(TX_PIN);

    //设置输入输出引脚。 流控引脚置位无效值
    NRF_UART0->PSELRXD = RX_PIN;
    NRF_UART0->PSELTXD = TX_PIN;
    NRF_UART0->PSELRTS = 0XFFFFFFFF;
    NRF_UART0->PSELCTS = 0XFFFFFFFF;

    NRF_UART0->BAUDRATE = 0x00275000;      //9600波特率
    NRF_UART0->CONFIG = ;             //不适用流控,不使用校验

    NRF_UART0->INTENSET = ( << ) | ( << );    //使能 RXDRDY和TXDRDY事件触发中断

    //清零一下事件
    NRF_UART0->EVENTS_RXDRDY = ;
    NRF_UART0->EVENTS_TXDRDY = ;  

    NRF_UART0->ENABLE = ;          //开启uart
    NRF_UART0->TASKS_STARTRX = ;   //使能接收
    NRF_UART0->TASKS_STARTTX = ;   //使能发送

    //开启MCU的uart中断
    NVIC_SetPriority(UART0_IRQn, );
    NVIC_ClearPendingIRQ(UART0_IRQn);
    NVIC_EnableIRQ(UART0_IRQn);

}

#define BUFF_SIZE (30)
uint8_t data[BUFF_SIZE];
uint8_t receive_index = ;
uint8_t send_index = ;
uint8_t flag = ;

int main(){

    uart_init();

    ){
        ){
           flag = ;  //重置flag
           NRF_UART0->TXD = data[send_index++];   //这里只发送一个字节,剩余19的字节通过中断发送

       }
    }
    ;
}

void UART0_IRQHandler(void){
   )
   {
       /// 注意清0事件标志
       NRF_UART0->EVENTS_RXDRDY = ;
       data[receive_index++] = (uint8_t)NRF_UART0->RXD;
   ){
           receive_index = ;
           flag = ;
       }
   }

   )
   {
       // 注意清0事件标志
       NRF_UART0->EVENTS_TXDRDY = ;
       ){     //只要没发送完就继续发送
           NRF_UART0->TXD = data[send_index++];
           ){
              send_index = ;          //发送完了,重置0
           }
       }
   }
}

nrf51822裸机教程-UART的更多相关文章

  1. nrf51822裸机教程-SPI(主)

    关于SPI总线的介绍这里就不细说了,网上有很多介绍SPI总线时序的. SPI总线的本质就是一个环形总线结构,在时钟驱动下两个双向移位寄存器进行数据交换. 所以SPI总线的特色就是:传输一字节数据的同时 ...

  2. nrf51822裸机教程-IIC

    关于IIC总线的核心有以下几点: :时钟线高电平期间必须保持数据线不变. :时钟线低电平期间可以改变数据. :时钟线和数据线上都要接上拉电阻,以使总线不工作时,两根线的电平都处于高电平状态. :每个传 ...

  3. nrf51822裸机教程-RTC

    RTC0被协议栈使用了.所以在跑蓝牙程序的情况下.RTC0不能使用. RTC相关寄存器如下: EVTEN,EVTENSET,EVTENCLR. 这三个寄存器用来设置是否使能某个事件.(TICK,OVR ...

  4. nrf51822裸机教程-PWM

    先简单介绍一下PWM的原理. 原理很简单. 假设COUNTER是个从0开始递增的计数器.  我们设置两个值 counter0 和counter1 在 COUNTER 计数到counter0的值时候翻转 ...

  5. nrf51822裸机教程-PPI

    Programmable Peripheral Interconnect即可编程外设互联 系统,该模块是51822 提供的一个特性. 目的是为了让51822 的外围模块可以不通过处理器而自动相互作用. ...

  6. nrf51822裸机教程-GPIOTE

    GPIO通常都会具有中断功能,上一讲的GPIO中并没有涉及到中断的相关寄存器. 51822将GPIO的中断相关做成了一个单独的模块GPIOTE,这个模块不仅提供了GPIO的中断功能,同时提供了 通过t ...

  7. nrf51822裸机教程-硬件timer

    该讲介绍51822的Timer/Counter模块工作在timer模式下(定时器模式,还可以工作为计数器模式) 如何操作 51822的Timer/Counter结构如下图所示 Timer模块从PCLK ...

  8. nrf51822裸机教程-GPIO

    首先看看一下相关的寄存器说明 Out寄存器 输出设置寄存器 每个比特按顺序对应每个引脚,bit0对应的就是 引脚0 该寄存器用来设置 引脚作为输出的时候的 输出电平为高还是低. 与输出设置相关的 还有 ...

  9. AVR单片机教程——UART进阶

    本文隶属于AVR单片机教程系列.   在第一期中,我们已经开始使用UART来实现单片机开发板与计算机之间的通信,但只是简单地讲了讲一些概念和库函数的使用.在这一篇教程中,我们将从硬件与软件等各方面更深 ...

随机推荐

  1. java 线程的几种状态

    java thread的运行周期中, 有几种状态, 在 java.lang.Thread.State 中有详细定义和说明: NEW 状态是指线程刚创建, 尚未启动 RUNNABLE 状态是线程正在正常 ...

  2. js:语言精髓笔记13--语言技巧

    消除代码全局变量名占用: //本质是使用匿名函数: void function(x, y, z) { console.log(x + y + z); }(1,2,3); //要使函数内的变量不被释放, ...

  3. 2015ACM/ICPC亚洲区长春站 E hdu 5531 Rebuild

    Rebuild Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)Total S ...

  4. 【python游戏编程之旅】第五篇---嗷大喵爱吃鱼小游戏开发实例

    本系列博客介绍以python+pygame库进行小游戏的开发.有写的不对之处还望各位海涵. 我们一同在前几期的博客中已经学到了很多pygame的基本知识了,现在该做个小游戏实战一下了. 前几期博客链接 ...

  5. BZOJ3757 : 苹果树

    树上莫队,有几个坑: a可能等于b 数据范围是100000 #include<cstdio> #include<algorithm> #include<cmath> ...

  6. TYVJ P1078 删数 Label:区间dp

    描述 有N个不同的正整数数x1, x2, ... xN 排成一排,我们可以从左边或右边去掉连续的i个数(只能从两边删除数),1<=i<=n,剩下N-i个数,再把剩下的数按以上操作处理,直到 ...

  7. TYVJ P1088 treat Label:鞭笞人的DP

    时间: 1000ms / 空间: 131072KiB / Java类名: Main 背景 广东汕头聿怀初中 Train#2 Problem2 描述 给出长度为N的数列{A_i},每次可以从最左边或者最 ...

  8. Git Shell 安装版本

    #!/bin/sh v1.; do echo "Begin install Git $ver."; git reset --hard git clean -fdx git chec ...

  9. 如何在spring框架中解决多数据源的问题

    在我们的项目中遇到这样一个问题:我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库.我们以往在spring和hibernate框架中总是配置一个数据源,因而sessi ...

  10. iOS开发项目之四 [ 调整自定义tabbar的位置与加号按钮的位置]

    自定义tabbar与按钮的添加 01 - 把系统的tabbar用我们自己的覆盖 LHQTabBar *lhqTabBar = [[LHQTabBar alloc]init]; [self setVal ...