stm32基于Ucos消息队列的空闲中断信息读取储存和发送

原创 2015年11月20日 22:36:51

最近做一个中继,一边是基于Wifi的网络,一边是基于串口的嵌入式设备。两者之间通过中继进行通信。

具体结构:


网络设备(手机,pad,PC等等)→→ WIFI网络→→ UART1→→ STM32消息队列→→UART2→→ 嵌入式设备


直连的话会遇到以下 2  个问题:

1.当WIFI网络或嵌入式设备发送来的数据过多,接收后来不及发送给另一边。数据会丢失

2.数据不定长


所以用了下面 2 个解决方法:

1.基于Ucos的消息队列:将接收到的数据立刻存入队列中,达到一个缓冲的目的。保证接收到数据都发送出去,当然缓冲时的数据不能多余消息队列的开的大小。(消息队列开的过大会造成硬件中断,所有程序无法执行。)

2.串口空闲中断,当串口idle中断时,通过控制DMA通道的使能,来接收数据。节约CPU资源。稳定可靠。


PCB截图:




开始码代码:

STM32的Ucos移植就不说了,网上一抓一大把。


首先搭建任务,很明显这个项目里需要两个Task。

一个是Task_ReadFromWifi(),用来从wifi网络中读取数据,然后存入消息队列

另一个是Task_ReadFromUart(),用来从嵌入式设备中读取数据,然后存入消息队列


构建任务,每一个任务相当于一个线程。他需要三个必须的东西:身份证(任务管理模块),房子(栈),大脑(代码块)。

首先分别设置优先级和分配栈

<span style="font-size:18px;"><span style="font-size:18px;">/*******************ÉèÖÃÈÎÎñÓÅÏȼ¶*******************/
#define TASK_READFROMWIFI_PRIO 5
#define TASK_READFROMUART_PRIO 6
//#define TASK_TESTLED_PRIO 6

/************ÉèÖÃÕ»´óС£¨µ¥Î»Îª OS_STK £©************/
#define TASK_READFROMWIFI_STK_SIZE 256
#define TASK_READFROMUART_STK_SIZE 256
//#define TASK_TESTLED_STK_SIZE 20</span></span>

定义任务

<span style="font-size:18px;">OS_STK task_readfromwifi[TASK_READFROMWIFI_STK_SIZE];
OS_STK task_readfromuart[TASK_READFROMUART_STK_SIZE];</span>


完成准备任务后,再去main内创建并开始任务:

<span style="font-size:18px;">  BSP_Init();
  OSInit();
  OSTaskCreate(Task_ReadFromWifi,(void *)0, &task_readfromwifi[TASK_READFROMWIFI_STK_SIZE-1], TASK_READFROMWIFI_PRIO);
	OSTaskCreate(Task_ReadFromUart,(void *)0, &task_readfromuart[TASK_READFROMUART_STK_SIZE-1], TASK_READFROMUART_PRIO);
	OSStart();</span>


然后在APP.c内准备好给两个任务的大脑装东西,一开始写的时候直接留空白就好了,我这里直接把我的代码贴上来:

<span style="font-size:18px;">void Task_ReadFromWifi(void *p_arg)
{
	uint8_t *s; 
	INT8U err;
	 while (1)
	 {
      s = OSQPend(QSem,0,&err);
      if(err==OS_NO_ERR)
      {
       printf("\r\nMsgFromPhone   =\r\n       ");
       Puts_UART2(s);//´®¿Ú2·¢Ë͸øPad 
      }      
			OSTimeDly(20);
   }
}
//↑任务1</span>
<span style="font-size:18px;">//↓任务2
void Task_ReadFromUart(void *p_arg)
{
	uint8_t *s;
	INT8U err;
	while(1)
	{
		s=OSQPend(QSem2,0,&err);
		if(err==OS_NO_ERR)
		{
			printf("\r\nMsgFromPC   =\r\n        ");
			Puts_UART2(s);
			Puts_UART1(s);//´®¿Ú1·¢Ë͸øµçÄÔ
		}
		OSTimeDly(20);
	}
}</span>

搭建好基本的后,准备消息队列:(调试过程中,出于贪心,我想把数列开的大一点,所以#define了

MsgQequeTabNum 20,结果硬件中断,单步调试发现,程序都不运行到main的第一句话就直接炸了,后来发现时栈溢出的原因,坑了好久。


<span style="font-size:18px;">#define MSG_QUEUE_TABNUM 10		
extern OS_EVENT *QSem;
extern OS_MEM   *PartitionPt;
extern uint8_t  Partition[MSG_QUEUE_TABNUM][400];
extern  void *MsgQeueTb[MSG_QUEUE_TABNUM];

extern OS_EVENT *QSem2;
extern OS_MEM *PartitionPt2;
extern uint8_t  Partition2[MSG_QUEUE_TABNUM][400];
extern  void *MsgQeueTb2[MSG_QUEUE_TABNUM];</span>


然后再Main内:

<span style="font-size:18px;">	QSem = OSQCreate(&MsgQeueTb[0],MSG_QUEUE_TABNUM); 
	PartitionPt=OSMemCreate(Partition,MSG_QUEUE_TABNUM,400,&err);
	QSem2 = OSQCreate(&MsgQeueTb2[0],MSG_QUEUE_TABNUM); 
	PartitionPt2=OSMemCreate(Partition2,MSG_QUEUE_TABNUM,400,&err);</span>


因为任务代码是写在app.c内,所以要用到的消息队列的变量需要继续定义一下

<span style="font-size:18px;">//
OS_EVENT *QSem;
void *MsgQeueTb[MSG_QUEUE_TABNUM];
OS_MEM   *PartitionPt;
uint8_t  Partition[MSG_QUEUE_TABNUM][400];
//
 OS_EVENT *QSem2;
 void *MsgQeueTb2[MSG_QUEUE_TABNUM];
 OS_MEM *PartitionPt2;
 uint8_t Partition2[MSG_QUEUE_TABNUM][400];</span>


接下来只需要配置串口的空闲中断就可以了,空闲中断有很多优点:

可以接受不定长数组;

并且更加节约资源,不需要你在一个线程内一直在等待接收数据,或者像之前一样用查询的方式去监听;

谁用谁知道!。



之前转的一篇文章已经很详细的给了stm32串口空闲中断的代码和介绍了,

里面有各个模块的配置 GPIO DMA UART NVIC

在此就不多说了。

可移步:

http://blog.csdn.net/lxk7280/article/details/49700663



注意一点就是DMA的不同频道对应的外设是不同的,不要弄错了,整理了一下,贴上来:

DMA1 Channel1:
ADC1  TIM2_CH3  TIM4_CH1  DMA1 
DMA1 Channel2:
USART3_TX  TIM1_CH1  TIM2_UP  TIM3_CH3  SPI1_RX
DMA1_Chanel3:
USART3_RX
TIM1_CH2
TIM3_CH4
TIM3_UP
SPI1_TX
DMA1_Chanel4:
USART1_TX
TIM1_CH4
TIM1_TRIG
TIM1_COM
TIM4_CH2
SPI/I2S2_RX
I2C2_TX
DMA1_Chanel5:
USART1_RX
TIM1_UP
SPI/I2S2_TX
TIM2_CH1
TIM4_CH3
I2C2_RX
DMA1_Chanel6:
USART2_RX
TIM1_CH3
TIM3_CH1
TIM3_TRIG
I2C1_TX
DMA1_Chanel7:
USART2_TX
TIM2_CH2
TIM2_CH4
TIM4_UP
I2C1_RX
DMA2_Chanel1:
SPI/I2S3_RX
TIM5_CH4
TIM5_TRIG
TIM8_CH3
TIM8_UP
DMA2_Chanel2:
SPI/I2S3_TX
TIM5_CH3
TIM5_UP
TIM8_CH4
TIM8_TRIG
TIM8_COM
DMA2_Chanel3:
UART4_RX
TIM6_UP
DAC1
TIM8_CH1
DMA2_Chanel4:
SDIO
TIM5_CH2
TIM7_UP
DAC2
DMA2_Chanel5:
ADC3
UART4_TX
TIM5_CH1
TIM8_CH2



果然不丢失数据了诶,实际测试以10ms的间隔同时互相发送数据,完全没有问题。我们有理由相信1ms的间隔也是没问题的,`(*∩_∩*)′,不过这个项目的中继也不会接受那么高频率的数据的。。。


WIFI网络的手机和设备串口效果图:


WIFI网络的PC和设备串口效果图:




2015、11、20
                                                                                                                                                                                                                                            Themelody

相关文章推荐

深入理解FreeRTOS的任务机制和消息队列+附完整项目代码

FreeRTOS是一个迷你的实时操作系统内核。FreeRTOS系统相当的小巧,最小化的FreeRTOS内核仅包括3个.c文件和少数头文件,总共不到9000行代码,编译后的映像小于10KB。 FreeR...
  • sac761
  • sac761
  • 2015年07月25日 11:45
  • 10352

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

UCOS-III 消息队列正确使用方法

UCOS-III 消息队列正确使用方法 2016-1-8           在基于gprs消息传输过程中,使用ucos-iii自带的消息队列最为缓存是十分方便的。可最近却发现了一个很奇怪的问题。起初...

STM32串口接收使用DMA双缓冲

#define IMAGE_BUFFER_SIZE 100  //以字为单位  经试验,增大这个数值速度并未提升 u32 Image_Buffer1[IMAGE_BUFFER_SIZE]={0}...

ZigBee协议栈中定时器的几种使用方法(在Zigbee中使用定时器)

原文链接:http://www.kaleidscope.cn:1020/archives/1056 Zigbee协议栈中如果要实现一个定时事件或者延时的话,有很多种方法,定时事件呢其实就是我们熟悉的使...
  • PZ0605
  • PZ0605
  • 2017年03月15日 15:43
  • 886

ucOS消息队列使用

  • 2011年06月23日 17:24
  • 76KB
  • 下载

UCOS之消息队列

UCOS中的邮箱一次可以传送一个变量指针,而消息队列可以一次传送多个变量指针,而且指针指向的数据类型也支持多样化。一个消息队列可视为多个邮箱的组合。使用消息队列需要注意的地方是:一个任务或者中断服务子...

ucos中的消息队列代码详解

  • 2013年06月19日 14:55
  • 61KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:stm32基于Ucos消息队列的空闲中断信息读取储存和发送
举报原因:
原因补充:

(最多只允许输入30个字)