求助,hal库函数手册 串口中断接收,急急急,加入悬赏

21ic官方微信-->
后使用快捷导航没有帐号?
查看: 609|回复: 9
HAL库串口接收中断问题
&&未结帖(20)
主题帖子积分
中级技术员, 积分 117, 距离下一级还需 183 积分
中级技术员, 积分 117, 距离下一级还需 183 积分
主题帖子积分
专家等级:结帖率:42%
主题帖子积分
中级技术员, 积分 117, 距离下一级还需 183 积分
中级技术员, 积分 117, 距离下一级还需 183 积分
最近换了ST的HAL库,在使用串口中断遇到了些问题&&我的大致配置流程是这样的void MX_USART2_UART_Init(void)
&&huart2.Instance = USART2;
&&huart2.Init.BaudRate = 115200;
&&huart2.Init.WordLength = UART_WORDLENGTH_8B;
&&huart2.Init.StopBits = UART_STOPBITS_1;
&&huart2.Init.Parity = UART_PARITY_NONE;
&&huart2.Init.Mode = UART_MODE_TX_RX;
&&huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
&&if (HAL_UART_Init(&huart2) != HAL_OK)
& & Error_Handler();
& & & & while(HAL_UART_Receive_IT(&huart2, &rxbuffer, 1) != HAL_OK);
& & & & //__HAL_UART_ENABLE_IT(&huart2,UART_IT_RXNE);
}& &这里是初始化串口2 然后开接收中断
void USART2_IRQHandler(void)
& && &&&HAL_UART_IRQHandler(&huart2);
& & & & copy_IMU_data(rxbuffer);
& & & & while (HAL_UART_GetState(&huart2) != HAL_UART_STATE_READY);
& & & & //while(HAL_UART_Receive_IT(&huart2, &rxbuffer, 1) != HAL_OK);
}&&这是串口2的中断处理 下面那句HAL_UART_Receive_IT我注调是因为发现如果执行了这句话他就一直在USART2_IRQHandler中出不去了
copy_IMU_data就是一个数据的复制 把接收到的一个字节复制到结构体内
运行后发现第一次进中断并跳出以后,程序就会跳到HardFault_Handler中去。。。
21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里
移步更多21ic独家微课:
主题帖子积分
中级技术员, 积分 117, 距离下一级还需 183 积分
中级技术员, 积分 117, 距离下一级还需 183 积分
主题帖子积分
专家等级:结帖率:42%
主题帖子积分
中级技术员, 积分 117, 距离下一级还需 183 积分
中级技术员, 积分 117, 距离下一级还需 183 积分
21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里
移步更多21ic独家微课:
主题帖子积分
初级工程师, 积分 2871, 距离下一级还需 129 积分
初级工程师, 积分 2871, 距离下一级还需 129 积分
主题帖子积分
专家等级:结帖率:0%打赏:0.00受赏:15.10
主题帖子积分
初级工程师, 积分 2871, 距离下一级还需 129 积分
初级工程师, 积分 2871, 距离下一级还需 129 积分
是一直接收到数据产生中断了跳不出去,还是你没用清理中断标志
21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里
移步更多21ic独家微课:
主题帖子积分
主题帖子积分
专家等级:结帖率:0%打赏:0.00受赏:38.00
主题帖子积分
清下中断标志位看看
21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里
移步更多21ic独家微课:
主题帖子积分
中级技术员, 积分 117, 距离下一级还需 183 积分
中级技术员, 积分 117, 距离下一级还需 183 积分
主题帖子积分
专家等级:结帖率:42%
主题帖子积分
中级技术员, 积分 117, 距离下一级还需 183 积分
中级技术员, 积分 117, 距离下一级还需 183 积分
是一直接收到数据产生中断了跳不出去,还是你没用清理中断标志
接收到中断了也跳出去了,可第二次进去的时候他就调到HardFault_Handler 中去了
21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里
移步更多21ic独家微课:
主题帖子积分
中级技术员, 积分 117, 距离下一级还需 183 积分
中级技术员, 积分 117, 距离下一级还需 183 积分
主题帖子积分
专家等级:结帖率:42%
主题帖子积分
中级技术员, 积分 117, 距离下一级还需 183 积分
中级技术员, 积分 117, 距离下一级还需 183 积分
清下中断标志位看看
用的是库函数,函数内已经处理过了
21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里
移步更多21ic独家微课:
主题帖子积分
助理工程师, 积分 1005, 距离下一级还需 995 积分
助理工程师, 积分 1005, 距离下一级还需 995 积分
主题帖子积分
专家等级:结帖率:0%
主题帖子积分
助理工程师, 积分 1005, 距离下一级还需 995 积分
助理工程师, 积分 1005, 距离下一级还需 995 积分
中断标志位需不需要清除
21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里
移步更多21ic独家微课:
主题帖子积分
中级技术员, 积分 117, 距离下一级还需 183 积分
中级技术员, 积分 117, 距离下一级还需 183 积分
主题帖子积分
专家等级:结帖率:42%
主题帖子积分
中级技术员, 积分 117, 距离下一级还需 183 积分
中级技术员, 积分 117, 距离下一级还需 183 积分
中断标志位需不需要清除
不与要 不过参考手册上说了可以软件清除,我也试过了没有用&&用的是F4的片子
21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里
移步更多21ic独家微课:
主题帖子积分
中级工程师, 积分 4625, 距离下一级还需 375 积分
中级工程师, 积分 4625, 距离下一级还需 375 积分
主题帖子积分
专家等级:结帖率:100%
主题帖子积分
中级工程师, 积分 4625, 距离下一级还需 375 积分
中级工程师, 积分 4625, 距离下一级还需 375 积分
应该是自动清除的,好久没用这个了,以前都是用标准库来做的。
21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里
移步更多21ic独家微课:
主题帖子积分
中级技术员, 积分 117, 距离下一级还需 183 积分
中级技术员, 积分 117, 距离下一级还需 183 积分
主题帖子积分
专家等级:结帖率:42%
主题帖子积分
中级技术员, 积分 117, 距离下一级还需 183 积分
中级技术员, 积分 117, 距离下一级还需 183 积分
应该是自动清除的,好久没用这个了,以前都是用标准库来做的。
我也试用的ST的官方库只不过是新出的HAL库所以可能使用上还不太熟悉
21ic公开课,21ic网友共同的学习圈子!学单片机、嵌入式、模拟、电源……就看这里
移步更多21ic独家微课:
技术领袖奖章
人才类勋章
时间类勋章
坚毅之洋流
发帖类勋章
荣誉元老奖章
等级类勋章求助!HAL库函数 串口中断接收!急急急! - STM32 - 意法半导体STM32/STM8技术社区
后使用快捷导航没有帐号?
查看: 2725|回复: 4
求助!HAL库函数 串口中断接收!急急急!
在线时间19 小时
主题帖子精华
初级会员, 积分 175, 距离下一级还需 25 积分
初级会员, 积分 175, 距离下一级还需 25 积分
首先介绍一下实验环境:单片机为stm32f415
调试器:st-link
硬件初始化:
*串口波特率为614400
*采用定时器每隔500us触发一次串口中断发送
*发送完成后进入中断接收模式
定时器中断回调函数如下:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
& & & & if(htim==&htim14)
& & & & & & & & GYRO_Handle.cmd_GYRO=GYRO_CMD;& & & & & & & &
& & & & & & & & HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);& & & && && && && && && && && && &使能发送
& & & & & & & & HAL_UART_Transmit_IT(&huart3, &GYRO_Handle.cmd_GYRO, 1);& & & && && && && && && &开启中断发送,然后进入发送模式
发送完成后进入发送中断完成回调函数:
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
& & & & if(huart==&huart3)
& & & & & & & & HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);& & & & & & & & & & & && && && &使能接收
& & & & & & & & HAL_UART_Receive_IT(&huart3, GYRO_Handle.frame_GYRO, 7);& & & & & & & & & & & & 开启中断接收,然后进入接收模式
接收中断完成回调函数:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
& & & & if(huart==&huart3)
& & & && && && & 用户函数区域
实验结果:
发送没有问题,可成功发送。
接收只能收到第一个字节。
接收服务函数老是进入错误服务函数,错误号为数据溢出
已经分析两天了,还是没有找到问题,求大师分析。
欢迎加入STM32/STM8社区技术交流群:& & &STM32Lxx开发群:
ST金币7536
在线时间330 小时
主题帖子精华
3.png (2.11 KB, 下载次数: 0)
09:46 上传
在线时间19 小时
主题帖子精华
初级会员, 积分 175, 距离下一级还需 25 积分
初级会员, 积分 175, 距离下一级还需 25 积分
木有问题!
欢迎加入STM32/STM8社区技术交流群:& & &STM32Lxx开发群:
在线时间19 小时
主题帖子精华
初级会员, 积分 175, 距离下一级还需 25 积分
初级会员, 积分 175, 距离下一级还需 25 积分
我帖子没加,重新发帖?还是怎么转给你
在线时间19 小时
主题帖子精华
初级会员, 积分 175, 距离下一级还需 25 积分
初级会员, 积分 175, 距离下一级还需 25 积分
若回复,请移步悬赏贴!
欢迎加入STM32/STM8社区技术交流群:& & &STM32Lxx开发群:
站长推荐 /1
本次课程将使用理论结合实践的授课方式,介绍智能硬件的全新开发方式,让大家理解STM32 MCU的软硬件资源来帮助开发者快速高效地入门智能硬件开发。
Tel: 3-8064
备案号: 苏ICP备号-2
|||意法半导体STM32/STM8技术社区
Powered byHAL库的串口DMA操作方法很难理解,非常的不好用 - STM32 - 意法半导体STM32/STM8技术社区
后使用快捷导航没有帐号?
查看: 3460|回复: 16
HAL库的串口DMA操作方法很难理解,非常的不好用
在线时间58 小时
主题帖子精华
中级会员, 积分 291, 距离下一级还需 209 积分
中级会员, 积分 291, 距离下一级还需 209 积分
最近用HAL库做个单工的无线串口,因为无线通信是阻塞的,就用串口的DMA接收函数HAL_UART_Receive_DMA。
实际用的时候发现很多问题:
1.串口接收一旦溢出就会丢数据。
例如串口接收满了,稍等几秒再启动新的DMA接收函数HAL_UART_Receive_DMA时,就丢失数据了,而且是再也收不到串口数据。如果接满后马上启动就没这个问题。
看官方示例代码,停止DMA接收后似乎要DeInit后重新初始化Init和启动DMA接收
2.串口DMA接收不能单独停止。
例如串口同时在DMA发送和DMA接收,DMA接收到一半我要终止DMA的话,只能调用HAL_UART_DMAStop把接收DMA和串口DMA都停止。
换言之,不能单独停止DMA接收。
我理解的接收DMA和发送DMA是两个独立的操作,为什么不能单独停止?
大家有没有类似的经验,应当怎样规避这些坑?
还是说我的做法和理解其实是有问题的?有错误的请大家批评指正,一起学习提高
欢迎加入STM32/STM8社区技术交流群:& & &STM32Lxx开发群:
在线时间84 小时
主题帖子精华
高级会员, 积分 522, 距离下一级还需 478 积分
高级会员, 积分 522, 距离下一级还需 478 积分
本帖最后由 任风吹吹 于
17:17 编辑
DMA停止后,HAL_UART_Receive_DMA执行前,如果串口有数据接收就会出问题。我测试时就是这种情况。
当你将接收DMA关闭后,此时串口还是激活的,若此时串口来数据,无法触发DMA传输,此时产生上溢错误(ORE),由于串口数据寄存器里的数据不能及时转移走,后面来的数据就进不来。一旦出现这种错误后,就不会再触发DMA请求,即使再开启DMA也不行。要恢复正常的话就只有Deinit后再重新初始化串口,或者使用read接口直接将数据寄存器中的数据读走后,后面的数据才能正常进入,从而正常产生DMA请求,这个DMA请求是指硬件请求。
ST金币1786
在线时间143 小时
主题帖子精华
金牌会员, 积分 2114, 距离下一级还需 2886 积分
金牌会员, 积分 2114, 距离下一级还需 2886 积分
我刚做过用串口DMA接收不定长数据,使能空闲中断,在空闲中断触发的时候停止DMA,取出数据,之后使用HAL_UART_Receive_DMA重新接收没有问题。(没有DeInit和Init)
2,HAl的库函数HAL_UART_DMAStop确实是同时关收发DMA,如果只关一个的话可以直接操作寄存器。至于只关一个会不会出问题就不知道了;
欢迎加入STM32/STM8社区技术交流群:& & &STM32Lxx开发群:
ST金币3957
在线时间594 小时
主题帖子精华
金牌会员, 积分 3698, 距离下一级还需 1302 积分
金牌会员, 积分 3698, 距离下一级还需 1302 积分
你应该用DMA中断将数据移到缓冲区
在线时间58 小时
主题帖子精华
中级会员, 积分 291, 距离下一级还需 209 积分
中级会员, 积分 291, 距离下一级还需 209 积分
我刚做过用串口DMA接收不定长数据,使能空闲中断,在空闲中断触发的时候停止DMA,取出数据,之后使用HAL_UA ...
DMA停止后,HAL_UART_Receive_DMA执行前,如果串口有数据接收就会出问题。我测试时就是这种情况。
那我试试直接操作寄存器。多谢你的建议
欢迎加入STM32/STM8社区技术交流群:& & &STM32Lxx开发群:
在线时间58 小时
主题帖子精华
中级会员, 积分 291, 距离下一级还需 209 积分
中级会员, 积分 291, 距离下一级还需 209 积分
你应该用DMA中断将数据移到缓冲区
的确这也是一种方法
在线时间58 小时
主题帖子精华
中级会员, 积分 291, 距离下一级还需 209 积分
中级会员, 积分 291, 距离下一级还需 209 积分
当你将接收DMA关闭后,此时串口还是激活的,若此时串口来数据,无法触发DMA传输,此时产生上溢错误(ORE) ...
这下算是完全明白了,多谢多谢
欢迎加入STM32/STM8社区技术交流群:& & &STM32Lxx开发群:
在线时间13 小时
主题帖子精华
初级会员, 积分 153, 距离下一级还需 47 积分
初级会员, 积分 153, 距离下一级还需 47 积分
谢谢分享,让我明白了
在线时间5 小时
主题帖子精华
初级会员, 积分 56, 距离下一级还需 144 积分
初级会员, 积分 56, 距离下一级还需 144 积分
确实我在调试串口的时候也遇到了这个问题,串口dma回调函数只能进入一次,稍后我把问题解决了和大家一起分享吧
欢迎加入STM32/STM8社区技术交流群:& & &STM32Lxx开发群:
在线时间60 小时
主题帖子精华
金牌会员, 积分 1097, 距离下一级还需 3903 积分
金牌会员, 积分 1097, 距离下一级还需 3903 积分
站长推荐 /1
本次课程将使用理论结合实践的授课方式,介绍智能硬件的全新开发方式,让大家理解STM32 MCU的软硬件资源来帮助开发者快速高效地入门智能硬件开发。
Tel: 3-8064
备案号: 苏ICP备号-2
|||意法半导体STM32/STM8技术社区
Powered by博客访问: 2761156
博文数量: 249
博客积分: 5347
博客等级: 大校
技术积分: 13415
注册时间:
认证徽章:
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: 嵌入式
&&& 串口接收发送数据有两种方式,一种是中断的模式,另一种是DMA方式,这里主要以中断的方式,来看一下使用串口来发送,接收数据的整个流程。这里以SerialApp例程为例子。
&& 在mian函数中的调用HalDriverInit();函数,在函数中初始化串口,主要是配置管脚和DMA通道
void HalDriverInit (void)
...................................
/* UART */
#if (defined HAL_UART) && (HAL_UART == TRUE)
& HalUARTInit();
....................................
& 从程序中可以看出要想使用协议栈中串口,初始化串口必须定义HAL_UART和HAL_UART &TRUE 在hal_board_cfg.h文件中。
#ifndef HAL_UART
#if (defined ZAPP_P1) || (defined ZAPP_P2) || (defined ZTOOL_P1) || (defined ZTOOL_P2)
#define HAL_UART TRUE
#define HAL_UART FALSE
#endif /* ZAPP, ZTOOL */
#endif /* HAL_UART */
&&& 然后在osal_start_system()开始系统后,会调用Hal_ProcessPoll()来读取时间和串口。
&&& 在CC2430的数据手册中有这样一段话。
Data reception on the UART is initiatedwhen a 1 is written to the& UxCSR.RE bit
The UART will then search for a valid start bit on the RXDx input pin and set the
UxCSR.ACTIVE bit high. When a validstart bit has been detected the received& byte is shifted into the receive register .The& UxCSR.RX_BYTE bit is set and a receive interrupt is generated when the operation has completed. The received data byte is available through the UxBUF register. When UxBUF is read,& UxCSR.RX_BYTE is cleared by hardware.
&&& 当有数据接收时,UxCSR.RE位将被置1,然后,UART将在RXDx的输入引脚上查找一个有效的开始位,当找到这个开始位时,将设置UxCSR.ACTIVE位为高电平。当一个有效的开始位被查找到,收到的字节将被移动到接收寄存器中。然后,UxCSR.RX_BYTE位设为1.并且,当这个接收操作完成后接收中断会被产生。接收到的数据可以通过操作UxBUF寄存器,当UxBUF寄存器的数据被读出后,UxCSR.RX_BYTE位被硬件清除。串口发生中断首先调用中断的处理函数,这个是接收的中断函数。
#if HAL_UART_0_ENABLE
HAL_ISR_FUNCTION( halUart0RxIsr, URX0_VECTOR )
& cfg0->rxBuf[cfg0->rxHead] = U0DBUF;
& if ( cfg0->rxHead == cfg0->rxMax )
&&& cfg0->rxHead = 0;
&&& cfg0->rxHead++;
&&& 该中断函数主要是把U0DBUF寄存器,也就是接收到数据的寄存器,把数据读取来放到UART的结构体中的,cfg0->rxBuf[],中,这个数组的内存分配是在HalUARTOpen()函数中。
SerialApp.c中有下面的定义
#if !defined( SERIAL_APP_RX_MAX )
& #if (defined( HAL_UART_DMA )) && HAL_UART_DMA
&&& #define SERIAL_APP_RX_MAX& 128
&&& /* The generic safe Rx minimum is 48, but if you know your PC App will not
&&&& * continue to send more than a byte after receiving the ~CTS, lower max
&&&& * here and safe min in _hal_uart.c to just 8.
&&& #define SERIAL_APP_RX_MAX& 64
SerialApp_Init()函数中有下面的赋值,
&uartConfig.rx.maxBufSize&&&&&&& = SERIAL_APP_RX_MAX;
HalUARTOpen()函数中有下面的赋值:所以其cfg->rxMax=128,
cfg->rxMax = config->rx.maxBufS
&&&& 其中rxHead这个参数始终指向像一个参数被存放到rxBuf的位置。因为硬件串口缓存器U0DBUF只能存放一个字节,如果不及时把这个接收到的转移出去,那么就会被下一个到来的字节覆盖掉,所以rxHead变量就指向了这个存放的地址,当然是基于定义的rxBuf存储空间。
而if ( cfg0->rxHead == cfg0->rxMax )这一句判断也说明的很清楚,一旦这个计数达到了定义的最大接收数量,也就是说已经把rxBuf存储空间占满了,那么就不能在继续存放了。
&&& 中断函数执行完后,就应该跳到发生中断时执行的地方,这时程序继续执行,然后在osal_start_system()开始系统后,会循环调用Hal_ProcessPoll()来读取时间和串口,
void Hal_ProcessPoll ()
& /* Timer Poll */
& HalTimerTick();
& /* UART Poll */
#if (defined HAL_UART) && (HAL_UART == TRUE)
& HalUARTPoll();
下面是HalUARTPoll();函数的源代码,在这里有对接收到的数据进行处理的程序。
void HalUARTPoll( void )
#if ( HAL_UART_0_ENABLE | HAL_UART_1_ENABLE )
& static uint8 tickS
& uartCfg_t *
#if HAL_UART_0_ENABLE
& //当发生串口接收中断时cfg0就会改变,如果串口没有数据输入cfg0为空,当接收到数据时cfg0将在串口中断服务程序中被改变
& if ( cfg0 )
&&& cfg = cfg0;
#if HAL_UART_1_ENABLE
& if ( cfg1 )
&&& cfg = cfg1;
& // Use the LSB of the sleep timer (ST0 must be read first anyway).
//系统上电后,睡眠定时器就会自动启动做自增计数ST0即睡眠定时器启动到现在计算值的最低8位
& tick = ST0 - tickS
& tickShdw = ST0;
&//下面是一个无限循环
& //------------发送超时时间
&&& if ( cfg->txTick > tick )
&&&&& cfg->txTick -=
&&&&& cfg->txTick = 0;
& //---------------------接收超时时间
&&& if ( cfg->rxTick > tick )
&&&&& cfg->rxTick -=
&&&&& cfg->rxTick = 0;
//是使用DMA方式还是使用中断方式
#if HAL_UART_ISR
#if HAL_UART_DMA
&&& if ( cfg->flag & UART_CFG_DMA )
&&&&& pollDMA( cfg );
&&& else//中断方式
&&&&& pollISR( cfg );
#elif HAL_UART_DMA
&&& pollDMA( cfg );
&&& /* The following logic makes continuous callbacks on any eligible flag
&&&& * until the condition corresponding to the flag is rectified.
&&&& * So even if new data is not received, continuous callbacks are made.
//如果接收缓存中有数据,当接收数据时rxHead会增计数,当读取数据时rxTail会增计数,两个标志的初始值都为0,所以这两个标志的差值就指示了缓存中有多少的数据*/
&&&&& if ( cfg->rxHead != cfg->rxTail ) //不相等表示有数据
&&&&& uint8
&&&&& if ( cfg->rxHead >= (cfg->rxMax - SAFE_RX_MIN) )
//已保存的数据已经超过了安全界限,发送接收满事件
&&&&&&& evt = HAL_UART_RX_FULL;
&&&&& else if ( cfg->rxHigh && (cfg->rxHead >= cfg->rxHigh) )
//rxBuf[ ]接收到预设值(默认80字节),则触发事件,为什么是80,在上一篇转载的文章中有介绍,这里重点关注执行的流程。
&&&&&&& evt = HAL_UART_RX_ABOUT_FULL;
&&&&& else if ( cfg->rxTick == 0 )
//超时事件
&&&&&&& evt = HAL_UART_RX_TIMEOUT;
&&&&&&& evt = 0;
//如果发生事件,并且配置了回调函数则调用回调函数
&&& if ( evt && cfg->rxCB )
//(cfg->flag & UART_CFG_U1F)!=0)判读是那个串口,如果是串口1则为1,否则为0
&&&&&&& cfg->rxCB( ((cfg->flag & UART_CFG_U1F)!=0), evt );
#if HAL_UART_0_ENABLE
&&& if ( cfg == cfg0 )
#if HAL_UART_1_ENABLE
&&&&& if ( cfg1 )
&&&&&&& cfg = cfg1;
&&&&& else
& } while ( TRUE );
说明:(1)下面我们看一下pollISR()函数
static void pollISR( uartCfg_t *cfg )
//计算rxBuf[]中还有多少数据没有读出(以字节为单位)
& uint8 cnt = UART_RX_AVAIL( cfg );
//如果串口没有接收到数据,也就是说没有发生过串口接收中断,那么cfg应为是为空的,则cnt=0如果发生了串口中断,则cnt计算出串口缓存中还有多少数据没有读出,这个缓存并不是硬件寄存器的缓存,而是程序中开辟一段空间
& if ( !(cfg->flag & UART_CFG_RXF) )
//这里是针对流控制的,如果又有新的数据接收到了那么就要重置超时时间(超时时间由睡眠定时器来控制),而且需要把已经读出的数据数目减去!
&&& // If anything received, reset the Rx idle timer.
&&& if ( cfg->rxCnt != cnt )
&&&&& cfg->rxTick = HAL_UART_RX_IDLE;
&&&&& cfg->rxCnt =
&&& /* It is necessary to stop Rx flow in advance of a full Rx buffer because
bytes can keep coming while sending H/W fifo flushes.
//当接收缓存超过安全界限的时候停止RX流*/
&&& if ( cfg->rxCnt >= (cfg->rxMax - SAFE_RX_MIN) )
&&&&& RX_STOP_FLOW( cfg );
pollISR()函数主要作用就是设置rxTick和rxCn,
//关于安全界限,在程序中有下面一段:
/* Need to leave enough of the Rx buffer free to handle the incoming bytes
&* after asserting flow control, but before the transmitter has obeyed it.
&* At the max expected baud rate of 115.2k, 16 bytes will only take ~1.3 msecs,
&* but at the min expected baud rate of 38.4k, they could take ~4.2 msecs.
&* SAFE_RX_MIN and DMA_RX_DLY must both be consistent according to
&* the min & max expected baud rate.
//如果声明了流控制,为保证数据的正确接收需要在RX缓存区中预留出足够的空间。CC2430可以使用的最大串口波特率为115.2k。这个安全界限的数字跟使用的波特率还有串口tick有关。具体参考《Z-STACK问题之串口结构uartCfg_t乱说》文章。
&&& 可以看到,在初始化时rxHead=rxTail=0,如果发生接收中断,在中断服务函数中把U0DBUF寄存器中的数据传送到rxbuf[]中,这时rxHead和rxTail的值不在相等,其中,rxHead是rxBuf[]接收到数据的个数,rxTail是rxBuf[]移出的数据个数,再根据两者的差值,判断具体的事件evt。然后,根据evt和设置的回调函数,通过cfg->rxCB调用相应的回调函数。代码也是体显在下面一句。
cfg->rxCB( ((cfg->flag & UART_CFG_U1F)!=0), evt );
第一个参数主要是判断,是UART1还是UART0.第二个参数是触发的事件类型,那个个回调函数,具体是指向函数呢?
&&& 首先,我们在void SerialApp_Init( uint8 task_id )初始化函数中,对串口进行了配置,其中下面两句中有关于回调函数的。
& uartConfig.callBackFunc&&&&&&& &= rxCB;
& HalUARTOpen (SERIAL_APP_PORT, &uartConfig);
其中,在HalUARTOpen()函数中,有下面的一条语句,
uint8 HalUARTOpen( uint8 port, halUARTCfg_t *config )
&...................
cfg->rxCB = config->callBackF
...................
&& 也就是调用下面的rxCB函数。程序中定义了两个串口接收缓冲区:otaBuf上otaBuf2.当otaBuf中无数据时,处于空闲状态时,由otaBuf接收串口数据;当otaBuf中保留有数据时,下等待接收节点发送接收数据响应或由于某些正在重新给接收节点发送数据时,可通过otaBuf2接收数据,当otaBuf和otaBuf2都没有处于空闲状态时,说明数据没有及时发送给接收节点,发生了数据累积,缓冲区被占用,需要进行流量控制,所以直接退出接收回调函数,暂不接收数据。
static void rxCB( uint8 port, uint8 event )
& uint8 *buf,
& /* While awaiting retries/response, only buffer 1 next buffer: otaBuf2.
&& * If allow the DMA Rx to continue to run, allocating Rx buffers, the heap
&& * will become so depleted that an incoming OTA response cannot be received.
&& * When the Rx data available is not read, the DMA Rx Machine automatically
&& * sets flow control off - it is automatically re-enabled upon Rx data read.
&& * When the back-logged otaBuf2 is sent OTA, an Rx data read is scheduled.
& if ( otaBuf2 ) //缓冲区被占用
& if ( !(buf = osal_mem_alloc( SERIAL_APP_RX_CNT )) )
& /* HAL UART Manager will turn flow control back on if it can after read.
&& * Reserve 1 byte for the 'sequence number'.这里的SERIAL_APP_RX_CNT为80
&& 这里为什么是80,上篇文章中也有分析*/
& len = HalUARTRead( port, buf+1, SERIAL_APP_RX_CNT-1 );
& if ( !len )& // Length is not expected to ever be zero.
&&& osal_mem_free( buf );
& /* If the local global otaBuf is in use, then either the response handshake
&& * is being awaited or retries are being attempted. When the wait/retries
&& * process has been exhausted, the next OTA msg will be attempted from
&& * otaBuf2, if it is not NULL. otaBuf正在使用,则就使用otaBuf2
& if ( otaBuf ) //otaBuf正在被占用
&&& otaBuf2 = //otaBuf2接收数据
&&& otaLen2 =
&&& otaBuf = //otaBuf接收数据
&&& otaLen =
&&& /* Don't call SerialApp_SendData() from here in the callback function.
&&&& * Set the event so SerialApp_SendData() runs during this task's time slot.
&&&& 产生发送数据事件*/
&&& osal_set_event( SerialApp_TaskID, SERIALAPP_MSG_SEND_EVT );
在事件处理函数中,有下面的判断。
UINT16 SerialApp_ProcessEvent( uint8 task_id, UINT16 events )
........................
if ( events & SERIALAPP_MSG_SEND_EVT )
&&& SerialApp_SendData( otaBuf, otaLen );//
&&& return ( events ^ SERIALAPP_MSG_SEND_EVT );
.........................
&&& 下面是SerialApp_SendData()函数的源代码,调用AF_DataRequest(),通过OTA发送数据。由于在数据包之前增加了序列号SerialApp_SeqTx,多次重发的数据不会被接收节点重复发送到串口。
static void SerialApp_SendData( uint8 *buf, uint8 len )
& afStatus_
& // Pre-pend sequence number to the start of the Rx buffer.
& *buf = ++SerialApp_SeqTx;
& otaBuf =
& otaLen = len+1;
& stat = AF_DataRequest( &SerialApp_DstAddr,
&&&&&&&&&&&&&&&&&&&&&&&& (endPointDesc_t *)&SerialApp_epDesc,
&&&&&&&&&&&&&&&&&&&&&&&&& SERIALAPP_CLUSTERID1,
&&&&&&&&&&&&&&&&&&&&&&&&& otaLen, otaBuf,
&&&&&&&&&&&&&&&&&&&&&&&&& &SerialApp_MsgID, 0, AF_DEFAULT_RADIUS );
& if ( (stat == afStatus_SUCCESS) || (stat == afStatus_MEM_FAIL) )
//在设定的时间内没有发送成功,则重新发送。
&&& osal_start_timerEx( SerialApp_TaskID, SERIALAPP_MSG_RTRY_EVT,
&&&&&&&&&&&&&&&&&&&&& SERIALAPP_MSG_RTRY_TIMEOUT );
&&& rtryCnt = SERIALAPP_MAX_RETRIES;
&&& FREE_OTABUF();//重发的次数
void SerialApp_ProcessMSGCmd( afIncomingMSGPacket_t *pkt )
& switch ( pkt->clusterId )
& // A message with a serial data block to be transmitted on the serial port.
& //接收节点收到的接收数据命令,
& case SERIALAPP_CLUSTERID1:
&&& seqnb = pkt->cmd.Data[0];
&&& // Keep message if not a repeat packet
&&& if ( (seqnb > SerialApp_SeqRx) ||&&&&&&&&&&&&&&&&&&& // Normal
&&&&&&& ((seqnb
0x80)) ) // Wrap-around
&&&&& // Transmit the data on the serial port.接收到的发送到串口
&&&&& if ( HalUARTWrite( SERIAL_APP_PORT, pkt->cmd.Data+1,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& (pkt->cmd.DataLength-1) ) )
&&&&&&& // Save for next incoming message
&&&&&&& SerialApp_SeqRx =
&&&&&&& stat = OTA_SUCCESS;
&&&&& else
&&&&&&& stat = OTA_SER_BUSY;
&&&&& stat = OTA_DUP_MSG;
&&& // Select approproiate OTA flow-control delay.
&&& delay = (stat == OTA_SER_BUSY) ? SERIALAPP_NAK_DELAY : SERIALAPP_ACK_DELAY;
&&& // Build & send OTA response message. 发送响应消息
&&& rspBuf[0] =
&&& rspBuf[1] =
&&& rspBuf[2] = LO_UINT16( delay );
rspBuf[3] = HI_UINT16( delay );
//发送接收数据响应命令
&&& stat = AF_DataRequest( &(pkt->srcAddr),&
&&&&&&&&&&&&&&&&&&&&&&&(endPointDesc_t*)&SerialApp_epDesc,
&&&&&&&&&&&&&&&&&&&&&&& SERIALAPP_CLUSTERID2,&
&&&&&&&&&&&&&&&&&&&&&& SERIAL_APP_RSP_CNT ,&
&&&&&&&&&&&&&&&&&&&&&& rspBuf,&SerialApp_MsgID, 0,&
&&&&&&&&&&&&&&&&&&&&&& AF_DEFAULT_RADIUS );
&&& if ( stat != afStatus_SUCCESS )
&&&&& osal_start_timerEx( SerialApp_TaskID, SERIALAPP_RSP_RTRY_EVT,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& SERIALAPP_RSP_RTRY_TIMEOUT );
&&&&& // Store the address for the timeout retry. 存储发送超时的,目的地址
&&&&& osal_memcpy(&SerialApp_RspDstAddr, &(pkt->srcAddr), sizeof( afAddrType_t ));
& // A response to a received serial data block. 接收到接收数据响应命令
& case SERIALAPP_CLUSTERID2:
&&& if ( (pkt->cmd.Data[1] == SerialApp_SeqTx) &&
&&&&&&& ((pkt->cmd.Data[0] == OTA_SUCCESS) ||
&&&&&&&& (pkt->cmd.Data[0] == OTA_DUP_MSG)) ) //目的设备接收数据的状态
&&&&& // Remove timeout waiting for response from other device. 接收到返回的状态后,关闭定时器
&&&&& osal_stop_timerEx( SerialApp_TaskID, SERIALAPP_MSG_RTRY_EVT );
&&&&& FREE_OTABUF(); //释放缓存区
&&&&& delay = BUILD_UINT16( pkt->cmd.Data[2], pkt->cmd.Data[3] );
&&&&& // Re-start timeout according to delay sent from other device.
&&&&& osal_start_timerEx( SerialApp_TaskID, SERIALAPP_MSG_RTRY_EVT, delay );
&&& default:
UINT16 SerialApp_ProcessEvent( uint8 task_id, UINT16 events )
& if ( events & SYS_EVENT_MSG ) //ZDO层接收到注册过的消息
&&& afIncomingMSGPacket_t *MSG
&&& while ( (MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive(
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& SerialApp_TaskID )) )
&&&&& switch ( MSGpkt->hdr.event )
&&&&&&& case ZDO_CB_MSG:
&&&&&&&&& SerialApp_ProcessZDOMsgs( (zdoIncomingMsg_t *)MSGpkt );
&&&&&&& case KEY_CHANGE:
&&&&&&&&& SerialApp_HandleKeys( ((keyChange_t *)MSGpkt)->state,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ((keyChange_t *)MSGpkt)->keys );
& //接收到命令,然后执行,zigbee协议信息的传递有两种方式:消息和命令,消息长度不限,命令的大小则严格规定
&&&&&&& case AF_INCOMING_MSG_CMD:&&&&
//执行发送过来消息命令的回调函数
SerialApp_ProcessMSGCmd( MSGpkt );
&&&&&&&&& break
&&&&&&& default:
&&&&& osal_msg_deallocate( (uint8 *)MSGpkt );& // Release the memory.
&&& // Return unprocessed events
&& &return ( events ^ SYS_EVENT_MSG );
//发送数据的事件,这里是串口通过CC2430发送数据到另一个设备
& if ( events & SERIALAPP_MSG_SEND_EVT )
&&& SerialApp_SendData( otaBuf, otaLen );
&&& return ( events ^ SERIALAPP_MSG_SEND_EVT );
//重发数据的事件,如果发送数据没有成功的话
& if ( events & SERIALAPP_MSG_RTRY_EVT )
&&& if ( --rtryCnt )
&&&&& AF_DataRequest( &SerialApp_DstAddr,
&&&&&&&&&&&&&&&&&&&&& (endPointDesc_t *)&SerialApp_epDesc,
&&&&&&&&&&&&&&&&&&&&&& SERIALAPP_CLUSTERID1, otaLen,&
&&&&&&&&&&&&&&&&&&&&& otaBuf,
&&&&&&&&&&&&&&&&&&&&& &SerialApp_MsgID, 0,&
&&&&&&&&&&&&&&&&&&&&& AF_DEFAULT_RADIUS );
&&&&& osal_start_timerEx( SerialApp_TaskID,&
&&&&&&&&&&&&&&&&&&&&&&& SERIALAPP_MSG_RTRY_EVT,
&&&&&&&&&&& &&&&&&&&&&&SERIALAPP_MSG_RTRY_TIMEOUT );
&&&&& FREE_OTABUF();
&&& return ( events ^ SERIALAPP_MSG_RTRY_EVT );
//发送接收数据响应的重发事件
& if ( events & SERIALAPP_RSP_RTRY_EVT )
&&& afStatus_t stat = AF_DataRequest(&
&&&&&&&&&&&&&&&&&&&&SerialApp_RspDstAddr,
&&&&&&&&&&&&&&&&&&&(endPointDesc_t *)&SerialApp_epDesc,
&&&&&&&&&&&&&&&&&&&SERIALAPP_CLUSTERID2,
&&&&&&&&&&&&&&&&&& SERIAL_APP_RSP_CNT, rspBuf,
&&&&&&&&&&&&&&&&&& &SerialApp_MsgID, 0,&&
&&&&&&&&&&&&&&&&&& AF_DEFAULT_RADIUS );
&&& if ( stat != afStatus_SUCCESS )
&&&&& osal_start_timerEx( SerialApp_TaskID,&
&&&&&&&&&&&&&&&&&&&&&&&& SERIALAPP_RSP_RTRY_EVT,
&&&&&&&&&&&&&&&&&&&&&&& &SERIALAPP_RSP_RTRY_TIMEOUT );
&&& return ( events ^ SERIALAPP_RSP_RTRY_EVT );
#if SERIAL_APP_LOOPBACK
& if ( events & SERIALAPP_TX_RTRY_EVT )
&&& if ( rxLen )
&&&&& if ( !HalUARTWrite( SERIAL_APP_PORT, rxBuf, rxLen ) )
&&&&&&& osal_start_timerEx( SerialApp_TaskID, SERIALAPP_TX_RTRY_EVT,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& SERIALAPP_TX_RTRY_TIMEOUT );
&&&&& else
&&&&&&& rxLen = 0;
&&& return ( events ^ SERIALAPP_TX_RTRY_EVT );
& return ( 0 );& // Discard unknown events.
&&&& 在串口通信中设置了多个重发机制,增加数据通信的可靠性,首先是利用重发数据事件SERIALAPP_MSG_RTRY_EVT重发数据,重发的次数由rtyCnt设定。由于在数据包之前增加了序列号SerialApp_SeqTx,多次生发的数据不会被接收节点重复发送到串口。别外,加入了数据接收响应机制,发送节点在发送完数据后,等待接收节点返回接收数据响应,收到返回接收数据响应的命令SERIALAPP_CLUSTERID2:后,判断信息包中的接收状态参数。若接收状态为OTA_DUP_MSG,表明接收节点串口繁忙,应启动重发机制,延时后产生重发数据事件,若接收状态为OTA_SUCCESS,表明接收节点将数据成功发送到串口,就释放缓存区,等待串口接收下一包数据。
&& 下面是串口的接受和发送的流程图:
500)this.width=500;" height=1370>
阅读(8014) | 评论(0) | 转发(12) |
相关热门文章
给主人留下些什么吧!~~
请登录后评论。}

我要回帖

更多关于 北京月嫂招聘急急急 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信