所有关于电路

与每个字节的ACK / NACK的UART通信

鲁卡

螺纹启动器

鲁卡

加入2018年1月25日
12.
主意:我正在尝试在发射机和接收器之间建立坚定的通信;坚定了解一些基本错误处理。我正在尝试实现ACK(确认)/ nack(负确认)风格的错误处理。我目前只是从一个设备(PIC18F46K80)传输数据并接收与其他一个(PIC18F452),其中发射机(T)发送“有用”数据和接收器(R)接受该数据并发送回变送器ACK或NACK每个数据的每个字节发送。

设置:由于对程序有一些复杂性,因此我为每个发射机和接收器制作了流程图(仅考虑代码的主要方面)。

变送器和接收器流程图 -  min-min.jpg

这是发射机的代码(pic18f46k80)。接收方的代码不一样,但与此类似。Also, I use LEDs to indicate current position of program execution (e.g. when program is looping through "error" path or when is looping through "normal operation" path) and received data interpretation (LED bar graph connected in parallel to PORTD) but this is not included here (to improve code readability):

C:
#include  #include  #include  #include“config_bits.h”void osc_initialize(void);void uart_initialize(无效);voidUART_RECEIVE(UINT8_T * C);void UART_TRANSMIT(UINT8_T * C);void __interrupt()high_isr(void);uint8_t data_rec;//收到数据存储变量UINT8_T ISR_FLAG = 0;//用于指示ISR UINT8_T ACK = 0x06的完成;//确认代码UINT8_T NACK = 0x15;//否定确认代码// intialize内部振荡器寄存器void osc_initialize(void){osconbits.scs = 0b10; // set internal oscillator block OSCCONbits.IRCF = 0b101; // set clock frequency to 4MHz while(OSCCONbits.HFIOFS == 0); // wait for oscillator frequency to stabilize } // INITIALIZE UART REGISTERS void uart_initialize(void){ // RX and TX inputs port initialization TRISDbits.TRISD6 = 1; TRISDbits.TRISD7 = 1; SPBRG2 = 25; // SPBRG = ((F_osc/BaudRate)/64)-1 => at F_osc = 4MHz, BaudRate = 2400, low speed TXSTA2bits.BRGH = 0; // 8-bit data mode setting for transmitter/receiver TXSTA2bits.SYNC = 0; // asynchronous mode RCSTA2bits.SPEN = 1; // RX and TX set as serial port pins TXSTA2bits.TXEN = 1; // transmitter enable RCSTA2bits.CREN = 1; // receiver enable INTCONbits.GIEH = 1; // must be set for HP interrupt to be generated INTCONbits.GIEL = 1; // must be set for LP interrupt to be generated PIE3bits.RC2IE = 1; // enable USART receive interrupt } // UART TRANSMISSION FUNCTION void uart_transmit(uint8_t *tran){ do{ TXREG2 = *tran; // load the value of "tran" into TXREG (data stored into TXREG, then send into TSR) while(TXSTAbits.TRMT == 0); // wait for data to be loaded into TSR and send while(isr_flag == 0); // loop is terminated after ISR isr_flag = 0; // reset "isr_flag" } while(data_rec != ack); // got NACK (or anything else) -> re-transmit current data } // UART RECEPTION FUNCTION void uart_receive(uint8_t *rec){ // MAIN ERROR - overrun if(RCSTA2bits.OERR){ RCSTA2bits.CREN = 0; // to clear error, reception module must be reset RCSTA2bits.CREN = 1; RCREG2; // dummy read - part of module reset RCREG2; // dummy read *rec = nack; // current setting: if(OERR); discard data and re-transimission } // MINOR ERROR - framing else if(RCSTA2bits.FERR){ RCREG2; // dummy read - part of module reset *rec = nack; // current setting: if(FERR); discard data and re-transmission } // NORMAL OPERATION else{ *rec = RCREG2; // store received data to "rec" } } // ISR WHEN REQUEST FOR RECEPTION HAPPENS void __interrupt() high_isr(void){ INTCONbits.GIEH = 0; if(PIR3bits.RC2IF){ uart_receive(&data_rec); isr_flag = 1; } INTCONbits.GIEH = 1; } void main(void) { TRISC = 0; TRISD = 0; LATC = 0; LATD = 0; osc_initialize(); uart_initialize(); uint8_t data_tran[] = {"ABCDE"}; //character set to be transmitted while(1){ for(int i = 0; data_tran; i++){ // GENERATE DELAY WHILE CONTINUOUSLY CALL TRANSMISSION for(float j = 0; j < 5; j += 0.1){ uart_transmit(&data_tran); } } } return; }
代码说明:来自Main(),我正在呼吁UART_TRANSMIT()函数,该函数在时间(并产生足够的延迟,所以我可以观察到LED条形图上的发送数据(串行接收的数据并行地输出到PORTD))。在发送字符之后,发送器只需等待来自接收器的ACK或NACK的传输 - 如果它获得ACK,则传输下一个字符;如果它得到NACK,请重新传输当前字符,直到它获得ACK。
至于接收器,它会在Main()(循环中)等待ISR请求。将数据加载到RSR寄存器中后,执行UART_READ()函数。如果在没有任何错误的情况下收到数据,则在Portd上输出数据,将ACK发送到发送器和程序返回Main()。如果关于接收字符的错误,则PORTD上的数据保持不变(丢弃不良数据),NACK将发送到发送器和程序返回到MAIN(),在那里它等待重新传输字符。

现实生活中实现:首先,如果TX和RX线(在设备的两侧)都没有略微加载(在两侧的两个引脚上向GND添加1M欧姆电阻向GND添加1M欧姆电阻),则程序甚至不会正确地开始正确。否则,程序按预期工作,如果没有特殊事件,例如断开数据线的连接(并触发发送器或接收器中的任何一个)以模拟错误处理)。如果发生这种情况,LED指示程序循环通过“错误”路径循环,但随着我连接的未连接线,路径返回到“正常操作”(这是最奇怪的事实)。此外,重置两个设备同时也可以不时工作。

结论(问题):我怀疑有一些关于时间的问题,当错误是(或应该是)重置和程序是(或应该是)返回到“正常的操作”路径。然而,我不明白,我在这里错过了什么时间错误。
在传输字符之后,F452等待直到TSR为空(即使在这里不需要那么),因为它等待ACK / NACK)。在F452接收到字符之后,只有在RSR完全且传输ACK / NACK之后仅调用UART_RECEIVE(),立即结束 - 不等待设置TMRT - 因为程序需要返回UART_RECEIVE()然后回到ISR然后在新中断发生之前返回Main(),因为重新传输从46K80重新传输。

考虑到所有这些时机事实,我真的无法通过自己进一步解决这个问题。

编辑:我意识到UART需要上拉电阻器正常运行(RX未拾取噪音),但在重新接线之后,现在程序甚至无法开始执行;也就是说,数据无法开始发送,并且我也没有任何错误的指示。
另一个事实是,当它有效(如果没有使用电阻)时,如果任何数据线断开连接,则应从发射器传输数据;也就是说,如果有任何错误触发。但它没有传输 - 一旦任何错误显示出来,TX线(变送器)就会变高。

版主注意:用于代码的代码标签

附件

最后编辑了主持人:
T.

Trebla.

加入2019年6月29日
316.
我认为您必须以更结构化的方式重写代码。首先,你的流程图和代码不匹配。在TX代码中是for()循环(在main()函数中)用数组data_tran[]而不是单个整数值定义。在中断例程中,调用接收函数会增加中断延迟。制定好的计划(流程图),并遵循这个。
Papabravo.

Papabravo.

加入2006年2月24日
15,596
从带宽利用立场方面越低效率。除了在异步通信中,每个8位数据有效载荷有2或3个架空位,带宽利用率低于50%。为了在嘈杂的信道上获得可靠的通信,所以要实现错误检测概率高的方案,并且重传的成本低给出了预期的比特错误率。在这方面,你似乎没有完成作业。可以使用CRC校验字进行良好的错误检测。您也希望在整个数据包,而不是单个字符。
Yaakov.

Yaakov.

加入2019年1月27日
2,809
从带宽利用立场方面越低效率。除了在异步通信中,每个8位数据有效载荷有2或3个架空位,带宽利用率低于50%。为了在嘈杂的信道上获得可靠的通信,所以要实现错误检测概率高的方案,并且重传的成本低给出了预期的比特错误率。在这方面,你似乎没有完成作业。可以使用CRC校验字进行良好的错误检测。您也希望在整个数据包,而不是单个字符。
我将要这样做,但首先我想了解接收者如何知道到ACK或NAK。
鲁卡

螺纹启动器

鲁卡

加入2018年1月25日
12.
我有点困惑。接收方如何知道到ACK或NAK?
如果在数据接收时发生框架(FERR)或overrun(OERR)错误,则接收器发送NACK,否则是ACK。

如果还有其他东西可以澄清,我很乐意解释它。,)
Yaakov.

Yaakov.

加入2019年1月27日
2,809
如果在数据接收时发生框架(FERR)或overrun(OERR)错误,则接收器发送NACK,否则是ACK。

如果还有其他东西可以澄清,我很乐意解释它。,)
我想我难以提供帮助,因为我无法理解你的目标。这是一个实用的项目吗?是学习的东西吗?如果是这样,你想学习什么?

我这样问是因为我看不出这有什么实际的应用,我也不确定为什么你会想要使用完全不切实际的东西作为学习工具。
鲁卡

螺纹启动器

鲁卡

加入2018年1月25日
12.
如果在数据接收时发生框架(FERR)或overrun(OERR)错误,则接收器发送NACK,否则是ACK。

如果还有其他东西可以澄清,我很乐意解释它。: D
我认为您必须以更结构化的方式重写代码。首先,你的流程图和代码不匹配。在TX代码中是for()循环(在main()函数中)用数组data_tran[]而不是单个整数值定义。在中断例程中,调用接收函数会增加中断延迟。制定好的计划(流程图),并遵循这个。
关于流程图和Code - 在流程图中,我试图仅考虑代码的主要方面(以降低复杂性,提高可读性)。
数组是定义的data_tran []因为我想发送一系列字符(当然是一对一)。那是有什么问题吗?
关于ISR,我同意从其调用一个函数,为整个程序增加了很大的复杂性,并且表现也是如此,因为ISR应该尽快终止。我也会试图解决这个问题。
djsfantasi.

djsfantasi.

加入2010年4月11日
7,597
从带宽利用立场方面越低效率。除了在异步通信中,每个8位数据有效载荷有2或3个架空位,带宽利用率低于50%。为了在嘈杂的信道上获得可靠的通信,所以要实现错误检测概率高的方案,并且重传的成本低给出了预期的比特错误率。在这方面,你似乎没有完成作业。可以使用CRC校验字进行良好的错误检测。您也希望在整个数据包,而不是单个字符。
完全同意100%。这是一个不切实际的,敢于我说荒谬的设计。我想不出任何适用于数据包大小的通信应用程序。没有必要。开销的效率低于。而且你正在切割带宽60%。根据要传输的字节的来源,您可以在处理ACK / NAK协议时丢失数据。

由于您有一系列char,为什么不计算CRC值并在一个传输中发送整个数组。通过重新计算接收端的CRC并将其与收到的CRC代码进行比较,然后可以ACK / NAK整个阵列/传输。
鲁卡

螺纹启动器

鲁卡

加入2018年1月25日
12.
我想我难以提供帮助,因为我无法理解你的目标。这是一个实用的项目吗?是学习的东西吗?如果是这样,你想学习什么?

我这样问是因为我看不出这有什么实际的应用,我也不确定为什么你会想要使用完全不切实际的东西作为学习工具。
为什么你认为学习UART如何工作和错误处理以确保“安全”和“稳定”通信是不切实际的?
实际上,这是我的第一个(小)关于两个数字组件之间的通信(微处理器)的项目 - 我选择了它,因为UART应该相对容易理解和实现。

我想了解如何在嵌入式系统中实现至少某种数字通信 - 因为它对所需的两个或多个数字组件之间的接口的任何项目是基础的(例如,微处理器和TFT(或LCD)显示器之间的接口)。

这些日子可能不会在使用中,但在我看来是足以开始的。我不是在说我要使用它控制航天飞机..: D但在继续其他人之前,学习是好的,更实际数字通信的形式。

此外,我只是一个嵌入式系统和C编程的初学者,所以我认为一个项目建立良好的UART通信两个设备是一个足够好的项目开始。
Yaakov.

Yaakov.

加入2019年1月27日
2,809
如果您想了解如何在设备之间创建可靠的频道,我会期待您将从简单开始实际的这不是。为什么不使用一些代表它确实意味着制作可靠频道的东西?

只是因为你试图学习并不意味着你必须抛弃一切实用。做实际的事情是学习的方式。

无论如何,当然,当然,你是如何选择学习的,但你要求人们帮助你调试部分错误的东西,因为你没有使用实用代码。

谢谢你的回答,祝你的学习好运,数据通信可能很有趣。
鲁卡

螺纹启动器

鲁卡

加入2018年1月25日
12.
从带宽利用立场方面越低效率。除了在异步通信中,每个8位数据有效载荷有2或3个架空位,带宽利用率低于50%。为了在嘈杂的信道上获得可靠的通信,所以要实现错误检测概率高的方案,并且重传的成本低给出了预期的比特错误率。在这方面,你似乎没有完成作业。可以使用CRC校验字进行良好的错误检测。您也希望在整个数据包,而不是单个字符。
我同意这是相当不低的,但仍然是,我不想在了解效率开始时潜入效率,以了解相对简单的UART通信应该运作。我不想清楚错误并类似于曾经发生过的事情 - 这可能会导致问题,这取决于每个数据的每个字节如何给接收器。
如何定义“数据包”?我应该每10字节或每1000字节检查错误吗?可能取决于错误发生的可能性(由于外部噪声,电网尖峰,附近的RF传输等)。
此外,现在,我只是考虑框架或超级错误;在我在两个微处理器之间建立一些稳定的通信形式之后,我不处理数据完整性(单个或多个比特的损坏)。
鲁卡

螺纹启动器

鲁卡

加入2018年1月25日
12.
如果您想了解如何在设备之间创建可靠的频道,我会期待您将从简单开始实际的这不是。为什么不使用一些代表它确实意味着制作可靠频道的东西?

只是因为你试图学习并不意味着你必须抛弃一切实用。做实际的事情是学习的方式。

无论如何,当然,当然,你是如何选择学习的,但你要求人们帮助你调试部分错误的东西,因为你没有使用实用代码。

谢谢你的回答,祝你的学习好运,数据通信可能很有趣。
我同意向大多数实际数据沟通跳跃的一点可能是一个更好的开始。但是,UART是(可能)以某种方式对其他串行通信的基础。我猜,通过一个简单的项目了解它(大多数基础)不会伤害。
此外,我目前正在使用的两张照片具有“集成”的硬件EUSART块(这是针对此类通信进行了优化的)。我认为它只是自然尝试出来,了解它(如果可能),实现了一个简单的项目实现它,然后继续下一个主题。

感谢您考虑理解解释主题!
Yaakov.

Yaakov.

加入2019年1月27日
2,809
我同意向大多数实际数据沟通跳跃的一点可能是一个更好的开始。但是,UART是(可能)以某种方式对其他串行通信的基础。我猜,通过一个简单的项目了解它(大多数基础)不会伤害。
此外,我目前正在使用的两张照片具有“集成”的硬件EUSART块(这是针对此类通信进行了优化的)。我认为它只是自然尝试出来,了解它(如果可能),实现了一个简单的项目实现它,然后继续下一个主题。

感谢您考虑理解解释主题!
要清楚的是,使用UART不是问题。物理层可以是任何东西,错误校正方案不是很有用。对实际错误纠正的一些研究将会提出一些简单的方案,这些方案的编码难度并不高(如果有的话),这将是一个更好的起点。

如果您只想学习制作UART工作,请放弃错误的东西并在两者之间进行通信。然后添加纠正,但是,如果一世在这样做,我会用更符合你要做的事情实际上制作可靠的频道。
鲁卡

螺纹启动器

鲁卡

加入2018年1月25日
12.
要清楚的是,使用UART不是问题。物理层可以是任何东西,错误校正方案不是很有用。对实际错误纠正的一些研究将会提出一些简单的方案,这些方案的编码难度并不高(如果有的话),这将是一个更好的起点。

如果您只想学习制作UART工作,请放弃错误的东西并在两者之间进行通信。然后添加纠正,但是,如果一世在这样做,我会用更符合你要做的事情实际上制作可靠的频道。
我忘了提到-我已经设置了工作UART com之前,我开始错误纠正(在那之前,我只是清除错误和继续)。那东西按预期工作了。但是代码也比现在简单得多。

你能解释一下你下面的意思吗?
我将使用更多的东西与你必须做的事情实际上制作可靠的频道。
谢谢!
djsfantasi.

djsfantasi.

加入2010年4月11日
7,597
如果我可以添加,那就像学习驾驶一样。人们可以在购物车上学习,但实际上不会帮助您获得许可证。

单个字节的错误检测和校正是您的购物车。但它不会帮助你在高速公路上开车。
鲁卡

螺纹启动器

鲁卡

加入2018年1月25日
12.
完全同意100%。这是一个不切实际的,敢于我说荒谬的设计。我想不出任何适用于数据包大小的通信应用程序。没有必要。开销的效率低于。而且你正在切割带宽60%。根据要传输的字节的来源,您可以在处理ACK / NAK协议时丢失数据。

由于您有一系列char,为什么不计算CRC值并在一个传输中发送整个数组。通过重新计算接收端的CRC并将其与收到的CRC代码进行比较,然后可以ACK / NAK整个阵列/传输。
由于关于给定波特率的传输太慢了,它不会使用1个数据包大小?还是有任何其他原因?

如上所述,我不关心(至少现在)速度和效率。也就是说,在没有上述不可预测的活动(参照“现实生活的实现”一节)的情况下,我设法实现了操作传输。
Yaakov.

Yaakov.

加入2019年1月27日
2,809
我忘了提到-我已经设置了工作UART com之前,我开始错误纠正(在那之前,我只是清除错误和继续)。那东西按预期工作了。但是代码也比现在简单得多。

你能解释一下你下面的意思吗?


谢谢!
我的意思是你的计划不能在实践中使用,使频道更加可靠,因为它是朴实的并且效率低下。串行连接上的误码率是众所周知和记录的。要处理它们的计划,它看起来与你的看起来无处不在。即使是一个天真的方案,至少使用的数据块也会更有意义。如果检查错误的错误,则在“纠正”的错误是冗余的。一个简单的校验和,或CRC对包数据的CRC将从可靠的数据传输中开始。

数据包大小可以通过通道的预期BER(误码率)来计算。我真的认为你会发现更多来学习比这更简单但实用的纠错方案。
鲁卡

螺纹启动器

鲁卡

加入2018年1月25日
12.
如果我可以添加,那就像学习驾驶一样。人们可以在购物车上学习,但实际上不会帮助您获得许可证。

单个字节的错误检测和校正是您的购物车。但它不会帮助你在高速公路上开车。
我的意思是,程序是否检查每个字节或每1000字节的数据的有效性,不应该真正难以实现。但是,我没有尝试任何数据验证算法,如CRC,校验和,奇偶校验等。