STM32
直播中

大彭

10年用户 1064经验值
擅长:电源/新能源 嵌入式技术
私信 关注
[问答]

如何实现不带控制流方式下的串口中断的接收与发送呢

受到警告
提示: 作者被禁止或删除 内容自动屏蔽

回帖(1)

何夏庄

2021-11-11 09:57:26
  一、移植官方串口样例
  经过上篇知道了如何将官方的样例移植出来作为自己的项目工程使用之后,我就开始着手移植官方SDK中给的串口样例了,毕竟掌握串口的输出和接收对于后续程序开发调试相当的重要。
  在移植的过程中处理将相关文件复制到自己工程文件夹下,还是得注意头文件引用的配置和添加 NRF51 BOARD_PCA10001 这句定义以及Jlink的配置了。
  一切准备就绪后,我把USB转TTL和开发板连接起来,打开串口,烧写程序,观察串口。..。..。..。.
  结果没有任何输出 ~_~ 我简单的阅读了串口样例程序, 程序开始初始化了串口相应的端口配置之后就会输出“Start”的字样串口没有输出,那要不是接线操作不对,那就是程序不对了。可是想着官方的样例怎么会不对,然后我拿出了示波器,测试RXD、TXD引脚是否有信号输出。
  ~_~ 好吧,没有信号输出。然后我就开始在网上找各位大神的帖子,看有没有大神可以指点一下我的,在参考了很多篇文章之后,终于找到了一篇好文,强推——nrf51822裸机教程-UART
  看了上篇博文之后,我知道了原来在使用串口之前还是得先好好学习下不同51822的串口特性和相关配置呀 ~_~好吧我本来是准备先看到样例的效果再一步步调试学习的。好了废话不多说,进正题~~
  二、nRF51822串口概述
  1.nRF51822串口引脚配置
  nRF51822 没有固定的串口引脚 RX 和 TX。在nRF51822中与UART相关的信号RXD、TXD、CTS和RTS主要是根据寄存器:PSELRXD、PSELTXD、PSELCTS、PSELRTS独立配置映射到相应的物理引脚。这点我们通过阅读样例代码能够更加清楚理解。
  其中 RTS (Require ToSend,发送请求:为输出信号,用于指示本设备准备好可接收数据)低电平有效,低电平说明本设备可以接收数据。CTS (Clear ToSend,发送允许:为输入信号,用于判断是否可以向对方发送数据)低电平有效,低电平说明本设备可以向对方发送数据。
  //------------串口相关物理引脚定义------
  #define RX_PIN_NUMBER 11
  #define TX_PIN_NUMBER 9
  #define CTS_PIN_NUMBER 10
  #define RTS_PIN_NUMBER 8
  //------------------------------------
  //串口初始化配置
  void simple_uart_config( uint8_t rts_pin_number,
  uint8_t txd_pin_number,
  uint8_t cts_pin_number,
  uint8_t rxd_pin_number,
  bool hwfc)
  {
  /** @snippet [Configure UART RX and TX pin] *///设置引脚输入输出方向
  nrf_gpio_cfg_output(txd_pin_number);
  nrf_gpio_cfg_input(rxd_pin_number, NRF_GPIO_PIN_NOPULL);
  NRF_UART0-》PSELTXD = txd_pin_number;//配置TXD的物理引脚
  NRF_UART0-》PSELRXD = rxd_pin_number;//配置RXD的物理引脚
  /** @snippet [Configure UART RX and TX pin] */
  if (hwfc)
  {
  nrf_gpio_cfg_output(rts_pin_number);
  nrf_gpio_cfg_input(cts_pin_number, NRF_GPIO_PIN_NOPULL);
  NRF_UART0-》PSELCTS = cts_pin_number;//配置CTS的物理引脚
  NRF_UART0-》PSELRTS = rts_pin_number;//配置RTS的物理引脚
  NRF_UART0-》CONFIG = (UART_CONFIG_HWFC_Enabled 《《 UART_CONFIG_HWFC_Pos);//流控制使能
  }
  NRF_UART0-》BAUDRATE = (UART_BAUDRATE_BAUDRATE_Baud9600 《《 UART_BAUDRATE_BAUDRATE_Pos);//配置波特率为9600
  NRF_UART0-》ENABLE = (UART_ENABLE_ENABLE_Enabled 《《 UART_ENABLE_ENABLE_Pos);//开启串口
  NRF_UART0-》TASKS_STARTTX = 1; //使能发送
  NRF_UART0-》TASKS_STARTRX = 1; //使能接收
  NRF_UART0-》EVENTS_RXDRDY = 0; //清零接收事件
  }
  2.nRF51822串口两种工作方式
  根据nRF51822官方SDK提供的串口相关物理引脚定义,可以看出nRF51822的串口可以工作在带流控的工作方式,只要我们配置好相关引脚的定义就好了。
  (1)带流控的工作方式
  带流控的工作方式 就是多了RTS和CTS两个控制引脚。
  RTS引脚作为输出,由uart硬件模块自动控制。RTS通常就是与硬件提供给串口的接收Buff协调自动工作。nRF51822内置了一个6字节的硬件接收Buff,以起到缓存的作用。而RTS和硬件Buff协调动作大致就如,已经填充了2个字节Buff中还剩下4个字节的时候RTS引脚就输出高电平的deactivate 信号(51822的工作方式就是这样),当buff中的数据都被读出后回复有效信号(低电平)。
  CTS作为输入由外部输入。 当CTS有效时(低电平)模块可以发送,当为无效时,模块自动暂停发送,并在CTS恢复有效时继续发送。
  如果将两个 nRF51822 uart模块的rts与cts交叉相接。 在发送方发送太快的情况下,当接收方的接收硬件Buff已经存了两个字节后,接收方自动无效RTS信号,表示不能接收了。 因为接收方RTS与发送方CTS相接。 所以发送方的CTS也变成无效信号,于是发送方自动停止发送。
  这样就保证了接收方不会接收溢出。流量控制也就是体现在这里。这里所说的流控是属于硬件流控,是硬件自动进行的。
  (2)不带流控的工作方式
  平时我们常用的是不带流控的串口工作方式,就只用把RX、TX交叉连接,再接上电源和GND就可以了。我上面的连线操作就是默认的使用的不带流控的工作方式,但是阅读了样例代码之后发现,样例代码中默认的工作方式是带流控的工作方式。
  三、样例代码修改
  因为我只是想实现简单的串口打印输出,可方便用于后续程序开发调试,所以直接采用不带流控的串口工作方式就好了。在这里就需要改一下样例代码了。其实通过上面已经贴出的样例代码中的串口初始化就可以看出,想要将串口工作方式改为不带流控的工作方式,只需要在调用函数 simple_uart_config(RTS_PIN_NUMBER, TX_PIN_NUMBER, CTS_PIN_NUMBER, RX_PIN_NUMBER, HWFC)传入参数的时候将HWFC置为false即可,不过这里写false可不行,还是直接 #define HWFC 0
  然后完成初始化,但是这个时候串口还是没有打印输出任何东西,通过调试代码发现,由电脑串口向nRF51822串口发送的数据nRF51822能够正常接收到,但是再将数据原样返回发送给电脑的时候就出不来数据了,所以初步猜测是函数simple_uart_put() 出了问题,通过对比其他大神的帖子,我发现他们有的是先进行发送事件清零的操作,但在nRF51822的英文使用手册上也有详细说明在接收之前需要先将接收事件清零,并没有说在发送之前需要将发送事件清零,不过测试说明先清零也是可以的。但是经过我后面反复的测试发现后清零也是没问题的呀~_~ 重新擦除了Flash烧写就可以了
  //发送一字节数据
  void simple_uart_put(uint8_t cr)
  {
  NRF_UART0-》EVENTS_TXDRDY=0;
  NRF_UART0-》TXD = (uint8_t)cr;
  while (NRF_UART0-》EVENTS_TXDRDY == 0)
  {
  // Wait for TXD data to be sent 等待发送数据完成
  }
  // NRF_UART0-》EVENTS_TXDRDY=0;
  }
  最后将修改后的样例工程重新编译下载终于电脑串口有数据输出了。这里总结修改的两点,第一,将HWFC置为0;第二,重新擦除Flash再烧写程序或者在发送函数中先将发送事件清0,再进行发送。不通过修改样例实现不带流控制的串口工作方式的话,那直接参考nrf51822裸机教程-UART里面的教程就可以了。最后效果图如下
  
  四、串口中断通信
  为了实现不带控制流方式下的串口中断的接收与发送,首先得初始化配置串口中断,即在simple_uart_config()函数中添加以下代码:
  NRF_UART0-》INTENSET = AAR_ENABLE_ENABLE_Enabled《《UART_INTENCLR_RXDRDY_Pos;//中断使能设置寄存器
  NVIC_EnableIRQ(UART0_IRQn);//使能串口0中断
  NVIC_SetPriority(UART0_IRQn,1);//设置中断优先级
  NVIC_ClearPendingIRQ(UART0_IRQn);//清除外部中断的悬挂位
  然后就是主函数和串口中断处理函数了,这里实现的是由电脑串口向nRF51822发送5个字节的数据,再由nRF51822发送给电脑串口,从而回显出来。代码如下:
  #define Buff_Size (5)
  uint8_t data[Buff_Size];
  uint8_t receive_index = 0;
  uint8_t send_index = 0;
  uint8_t flag = 0;
  int main(void)
  {
  simple_uart_config(RTS_PIN_NUMBER, TX_PIN_NUMBER, CTS_PIN_NUMBER, RX_PIN_NUMBER, HWFC);
  while(true)
  {
  if(flag == 1)
  {
  flag = 0;
  NRF_UART0-》TXD = data[send_index++];
  }
  }
  return 0;
  }
  //中断处理函数
  void UART0_IRQHandler()
  {
  //接收
  if(NRF_UART0-》EVENTS_RXDRDY != 0)
  {
  NRF_UART0-》EVENTS_RXDRDY = 0;
  data[receive_index++] = (uint8_t)NRF_UART0-》RXD;
  if(receive_index == 5)
  {
  receive_index = 0;
  flag = 1;
  }
  }
  //发送
  if(NRF_UART0-》EVENTS_TXDRDY != 0)
  {
  NRF_UART0-》EVENTS_TXDRDY = 0;
  if(send_index != 0)
  {
  NRF_UART0-》TXD = data[send_index++]; //只要没发送完就继续发送
  if(send_index == 5)
  {
  send_index = 0; //发送完了,重置0
  }
  }
  }
  }
  五、nRF51822 多串口
  nRF51822实际上只有一组串口,这点我们通过查看串口配置函数中的 NRF_UART0 定义文件就可以看出,因为该文件中只有NRF_UART0的定义,没有其他串口的定义了。那么我们要怎么实现多组串口通信呢。
  如果不是需要多组串口同时通信,可以直接通过切换串口定义的IO口就可以实现多组串口的功能了。这种方式只适用于分时使用串口通信。
  void uart_disable()
  {
  NRF_UART0-》ENABLE = UART_ENABLE_ENABLE_Disabled;
  }
  。..
  //配置串口1
  simple_uart_config(RTS_PIN_NUMBER, TX_PIN_NUMBER, CTS_PIN_NUMBER, RX_PIN_NUMBER, HWFC);
  uart_start();
  。..
  //需要使用串口2则先禁用串口,然后重新配置串口参数即可
  uart_disable();
  simple_uart_config(RTS_PIN_NUMBER,TX2_PIN_NUMBER,CTS_PIN_NUMBER,RX2_PIN_NUMBER,HWFC);
  uart_start();
  。..
  六、总结
  nRF51822的串口初始化配置和我们常用的STM32不一样,但是它更加灵活。在发送函数和接收函数的实现部分基本就和STM32处理一样了,只不过这里每次不是把接收和发送标志位清0,而是把接收和发送事件清0,对于字符串的发送和接收处理也是一样的,通过轮询接收缓冲区的数据就可以了。
  然后配置nRF51822的串口中断跟STM32操作也差不多,也是需要先设置中断寄存器、使能中断、配置中断优先级,清除中断悬挂位。在中断处理函数里面接收和发送的时候也要注意清除先接收事件和发送事件。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分