AVR UART 接收数据损坏
AVR UART Received Data corruption
我在 AVR (Atmega16) 和 GSM 模块之间有一个 UART 通信。通常,一切似乎都正常工作。问题是当我向 GSM 模块发送 SMS(例如 SMS:"H=0,95")时,在程序中每次收到 SMS 后都会有 SMS 响应。响应 (SMS) 已正确发送到手机 phone,但发生了用其他变量的随机数覆盖的情况。当我尝试使用终端调试它时,我想这是由于在 UART 中接收到大量字节引起的(变量 ser_rec 可能已损坏),但我无法在程序中找到它可能发生的位置。在 ISR 处理过程中,有一个条件可以避免这个问题,当最大字节数写入缓冲区时,缓冲区将被清除。
#include<avr/io.h> // Header file for basic avr input/output
//#define F_CPU 8000000UL
#include<util/delay.h> // header file for delay generation
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <util/atomic.h>
#include "rf22.h"
#include "lcd.h"
# define USART_BAUDRATE 9600
# define BAUD_PRESCALE ((( F_CPU / ( USART_BAUDRATE * 16UL))) - 1)
volatile unsigned char INTERRUPT=0;
volatile unsigned char DECODE_SMS=0;
uint16_t EEMEM HumidityLowRange=0;
uint16_t EEMEM HumidityHighRange=100;
int16_t EEMEM TemperatureLowRange=-40;
int16_t EEMEM TemperatureHighRange=80;
uint8_t EEMEM SMS_tel_number[14]="+421911243160";
unsigned char sms_index=0;
volatile int out=0;
int Temperature_low_range=-40;
int Temperature_high_range=80;
int Humidity_low_range=0;
int Humidity_high_range=100;
char tel_num[14];
//debugging tel num changing
unsigned char SMS_alert=0; //shows if SMS has been already sent
typedef struct
{
int Humidity_value;
int Temperature_value;
} BH;
BH BeeHive[5]; //for all BeeHives, ambient sensor included (considered as BeeHive without Heater)
void UART_Transmit_char( unsigned char data )
{
/* Wait for empty transmit buffer */
while ( !( UCSRA & (1<<UDRE)) );
/* Put data into buffer, sends the data */
UDR = data;
}
void UART_Transmit_string( char string[] )
{
int i=0;
while ( string[i] > 0)
{
UART_Transmit_char(string[i]);
i++;
}
}
unsigned char rx_tmp[40];
volatile char ser_rec[95]="";
volatile unsigned char a=0;
void clear_sec_rec()
{
int i=0;
while(i<sizeof(ser_rec))
{
ser_rec[i]=0;
i++;
}
i=0;
a=0;
}
char sms_index1()
{
int i=0;
char index=0;
while( i<(sizeof(ser_rec)-4))
{
i++; //check if gsm sends SM",
if(ser_rec[i]==((char) 'S'))
{
i++;
if(ser_rec[i]==((char) 'M'))
{
i++;
if(ser_rec[i]==((char) '"'))
{
i++;
if(ser_rec[i]==((char) ','))
{
i++;
index=ser_rec[i]; //return index
}
}
}
}
}//returns it
if(index) clear_sec_rec(); //clears the ser_rec buffer if there was index
return index;
}
void sms_decode()
{
int i=0;
char TemporaryString[95];
int Humidity_low_range_temp;
int Humidity_high_range_temp;
int Temperature_low_range_temp;
int Temperature_high_range_temp;
char SMS_text[80];
//clear buffers
strcpy(SMS_text, "");
strcpy(TemporaryString, "");
while( i<sizeof(ser_rec))
{
if(ser_rec[i]==((char) 'H'))
{
if(i<(sizeof(ser_rec)-4)) i++; //go forward only when there is a place for numbers =x,y, therefore number 4
if(ser_rec[i]==((char) '='))
{
i++;
memcpy(TemporaryString, &ser_rec[i], ((strlen(ser_rec))-i));
TemporaryString[(strlen(ser_rec)-i)]='[=10=]';
if((sscanf(TemporaryString,"%d,%d",&Humidity_low_range_temp,&Humidity_high_range_temp)==2))
{
if(Humidity_low_range_temp<Humidity_high_range_temp)
{
if((Humidity_low_range_temp>=0)&&(Humidity_high_range_temp<=100))
{
Humidity_high_range=Humidity_high_range_temp;
Humidity_low_range=Humidity_low_range_temp;
//and finally send the notification about changed limits
clear_sec_rec();
UART_Transmit_string("AT+CMGS=");
_delay_ms(50);
UART_Transmit_char(34);
_delay_ms(50);
UART_Transmit_string(tel_num);//the phone number
_delay_ms(50);
UART_Transmit_char(34);
_delay_ms(50);
UART_Transmit_string("\r");
_delay_ms(50);
clear_sec_rec(); //clears the buffer
sprintf(SMS_text,"Humidity range is set: Low=%d%% High=%d%%\r",Humidity_low_range, Humidity_high_range);
UART_Transmit_string(SMS_text);
_delay_ms(50);
UART_Transmit_char(0x1A);
_delay_ms(50);
clear_sec_rec();
_delay_ms(1000); //wait until response about credit will come
eeprom_update_word(&HumidityHighRange,Humidity_high_range);
eeprom_update_word(&HumidityLowRange,Humidity_low_range);
}
}
}
}
}
i++;
}
//clear buffers
strcpy(SMS_text, "");
strcpy(TemporaryString, "");
i=0;
//Temperature range set
while( i<(sizeof(ser_rec)-4))
{
if(ser_rec[i]==((char) 'T'))
{
i++;
if(ser_rec[i]==((char) '='))
{
if(i<(sizeof(ser_rec)-1)) i++;
memcpy(TemporaryString, &ser_rec[i], ((strlen(ser_rec))-i));
if((sscanf(TemporaryString,"%d,%d",&Temperature_low_range_temp,&Temperature_high_range_temp))==2)
{
if(Temperature_low_range_temp<Temperature_high_range_temp)
{
if((Temperature_low_range_temp>=-40)&&(Temperature_high_range_temp<=80))
{
Temperature_high_range=Temperature_high_range_temp;
Temperature_low_range=Temperature_low_range_temp;
//and finally send the notification about changed limits
clear_sec_rec();//clears the buffer
UART_Transmit_string("AT+CMGS=");
UART_Transmit_char(34);
_delay_ms(50);
UART_Transmit_string(tel_num);//the phone number
UART_Transmit_char(34);
UART_Transmit_string("\r");
_delay_ms(50);
clear_sec_rec();//clears the buffer
sprintf(SMS_text,"Temperature range is set: Low=%d C High=%d C\r",Temperature_low_range, Temperature_high_range);
UART_Transmit_string(SMS_text);
_delay_ms(50);
UART_Transmit_char(0x1A);
_delay_ms(50);
clear_sec_rec();
_delay_ms(1000); //wait until response about credit will come
eeprom_update_word(&TemperatureHighRange,Temperature_high_range);
eeprom_update_word(&TemperatureLowRange,Temperature_low_range);
}
}
}
}
}
i++;
}
//clear buffers
strcpy(SMS_text, "");
strcpy(TemporaryString, "");
i=0;
//Info SMS
while( i<(sizeof(ser_rec)-2)) //go forward only when there is a place for at least 3 characters
{
if(ser_rec[i]==((char) 'I'))
{
i++;
// b1=1;
if(ser_rec[i]==((char) 'N'))
{
i++;
// b1=2;
if(ser_rec[i]==((char) 'F'))
{
// b1=3;
i++;
if(ser_rec[i]==((char) 'O'))
{
//send info about all sensor values
clear_sec_rec();
_delay_ms(200);//clears the buffer
UART_Transmit_string("AT+CMGS=");
UART_Transmit_char(34);
UART_Transmit_string(tel_num);//the phone number
UART_Transmit_char(34);
UART_Transmit_char('\r');
_delay_ms(500);
clear_sec_rec();
_delay_ms(200);//clear the buffer
//T2=%dC H0=%d%%\nH3=%d%% T3=%dC T0=%dC\nH4=%d%% T4=%dC add , BeeHive[2].Temperature_value,BeeHive[0].Humidity_value, BeeHive[3].Humidity_value,BeeHive[3].Temperature_value, BeeHive[0].Temperature_value,BeeHive[4].Humidity_value, BeeHive[4].Temperature_value
sprintf(SMS_text,"H1=%d%% T1=%dC Amb:\nH2=%d%% T2=%dC H0=%d%%\nH3=%d%% T3=%dC T0=%dC\nH4=%d%% T4=%dC",BeeHive[1].Humidity_value, BeeHive[1].Temperature_value,BeeHive[2].Humidity_value, BeeHive[2].Temperature_value,BeeHive[0].Humidity_value, BeeHive[3].Humidity_value,BeeHive[3].Temperature_value, BeeHive[0].Temperature_value,BeeHive[4].Humidity_value, BeeHive[4].Temperature_value);
// c1=strlen(SMS_text);
UART_Transmit_string(SMS_text);
_delay_ms(50);
UART_Transmit_char(0x1A); //CTRL-Z - required by datasheet
_delay_ms(50);
// UART_Transmit_char('\n');
// d1=strlen(ser_rec);
clear_sec_rec();
_delay_ms(1000); //wait until response about credit will come
}
}
}
}
i++;
}
//Telephone number changing SMS
//clear buffers
i=0;
strcpy(SMS_text, "");
strcpy(TemporaryString, "");
while( i<(sizeof(ser_rec)-15))
{
if(ser_rec[i]==((char) 'T'))
{
i++; //go forward only when there is a place for 16 characters
if(ser_rec[i]==((char) 'E'))
{
i++;
if(ser_rec[i]==((char) 'L'))
{
i++;
if(ser_rec[i]==((char) ':'))
{
i++;
memcpy(TemporaryString, &ser_rec[i], 13);
TemporaryString[13]='[=10=]';
if((strlen(TemporaryString)==strlen(tel_num))&&(strlen(tel_num)==13))
{
memcpy(tel_num,&TemporaryString[0],strlen(TemporaryString));
//send info about changing number
clear_sec_rec();
_delay_ms(200);//clears the buffer
UART_Transmit_string("AT+CMGS=");
UART_Transmit_char(34);
UART_Transmit_string(tel_num);//the phone number
UART_Transmit_char(34);
UART_Transmit_string("\r");
_delay_ms(50);
clear_sec_rec();
_delay_ms(200);//clear the buffer
sprintf(SMS_text,"This is my new telephone number. BeeHiveMonitor");
UART_Transmit_string(SMS_text);
_delay_ms(50);
UART_Transmit_char(0x1A);
_delay_ms(50);
clear_sec_rec();
_delay_ms(1000); //wait until response about credit will come
eeprom_update_block((void*)&tel_num, (const void*)&SMS_tel_number, 13);
}
}
}
}
}
i++;
}
//Alert turn on SMS
//clear buffers
strcpy(SMS_text, "");
strcpy(TemporaryString, "");
i=0;
//Info SMS
while( i<sizeof(ser_rec))
{
if(ser_rec[i]==((char) 'K'))
{
if(i<(sizeof(ser_rec))) i++;
if(ser_rec[i]==((char) 'K'))
{
SMS_alert=0;
//send info alert turning on
clear_sec_rec();
_delay_ms(200);//clears the buffer
UART_Transmit_string("AT+CMGS=");
UART_Transmit_char(34);
UART_Transmit_string(tel_num);//the phone number
UART_Transmit_char(34);
UART_Transmit_string("\r");
_delay_ms(50);
clear_sec_rec();
_delay_ms(200);//clear the buffer
UART_Transmit_string("Alerts are turned on\r");
_delay_ms(50);
UART_Transmit_char(0x1A);
_delay_ms(50);
clear_sec_rec();
_delay_ms(1000); //wait until response about credit will come
}
}
i++;
}
i=0;
clear_sec_rec(); //clears the buffer
if(sms_index!=0)//deleting SMS
{
UART_Transmit_string("AT+CMGD=");
_delay_ms(50);
UART_Transmit_string("1,4\r"); //clear all SMSs in inbox
_delay_ms(50);
}
clear_sec_rec();
}
int main(void)
{
Humidity_high_range = eeprom_read_word(&HumidityHighRange); //get init values of previously set limits both Humidity and Temperature
Humidity_low_range = eeprom_read_word(&HumidityLowRange);
Temperature_high_range = eeprom_read_word(&TemperatureHighRange);
Temperature_low_range = eeprom_read_word(&TemperatureLowRange);
eeprom_read_block((void*)&tel_num, (const void*)&SMS_tel_number, 13);
sei();
char Text[80]; //character array for writing on the display
char SMS_text[80]; // character array for writing via SMS
char ATCommand[20];
int flag=0;
MCUCSR = (1<<JTD); // no -O0, interrupts disabled
MCUCSR = (1<<JTD); // do not use read-modify-write
DDRC=0xFF;
DDRD=0b11111010;
DDRB=0b10111111;
PORTB = PORTB | (1 << PB3); // Reset pin of SIM800L
_delay_ms(200);
DDRA=0xFF;
PORTC = PORTC | (1 << PC7); // turn on LCD backlight
//UART INIT
UCSRB = (1 << RXEN ) | (1 << TXEN );
UCSRC = (1 << URSEL ) | (1 << UCSZ0 ) | (1 << UCSZ1 );
UBRRH = (unsigned char)( BAUD_PRESCALE >> 8);
UBRRL = (unsigned char) BAUD_PRESCALE&0xFF ;
UCSRB |= (1 << RXCIE );
lcd_init(LCD_DISP_ON); // display initialization
rf22_init();
rf22_setfreq(RF22FREQ(869.545));
rf22_rxmode();
//GSM init 3 times AT
UART_Transmit_string("ATE0\r");
_delay_ms(200);
UART_Transmit_string("AT\r");
_delay_ms(200);
UART_Transmit_string("AT\r");
_delay_ms(200);
UART_Transmit_string("AT\r");
_delay_ms(200);
clear_sec_rec();
UART_Transmit_string("AT+CMGF=1\r");//set text mode
// while(!(strstr((char*)ser_rec,"OK")));
_delay_ms(200);
// while(!(strstr((char*)ser_rec,"OK")));
clear_sec_rec();
UART_Transmit_string("AT+CNMI=1,1,0,0,0\r");
// while(!(strstr((char*)ser_rec,"OK")));
_delay_ms(200);
clear_sec_rec();
UART_Transmit_string("AT+CMGD=1,4\r"); //delete all SMS in inbox
_delay_ms(100);
// UART_Transmit_string("1,4\r");
// while(!(strstr((char*)ser_rec,"OK")));
_delay_ms(200);
clear_sec_rec();
while(1)
{
if((INTERRUPT==1)&&(DECODE_SMS==0))
{
sms_index=sms_index1();
INTERRUPT=0;
if(sms_index!=0)
{
//if index exists - ready for decoding SMS
DECODE_SMS=1;
sprintf(ATCommand,"AT+CMGR=%c\r",sms_index);
UART_Transmit_string(ATCommand); //request for reading SMS with sms index
}
}
if((INTERRUPT==1)&&(DECODE_SMS==1))//decodes the sms
{
sms_decode();
DECODE_SMS=0; //finally clear both flags
INTERRUPT=0;
}
for(int x=0;x<5;++x)
{
rf22_getpacket(rx_tmp);
_delay_ms(10);
sscanf(rx_tmp,"_%d_%d_%d_%d_%d_%d_%d_%d_%d_%d",&BeeHive[0].Humidity_value, &BeeHive[0].Temperature_value,&BeeHive[1].Humidity_value, &BeeHive[1].Temperature_value,&BeeHive[2].Humidity_value, &BeeHive[2].Temperature_value,&BeeHive[3].Humidity_value, &BeeHive[3].Temperature_value,&BeeHive[4].Humidity_value, &BeeHive[4].Temperature_value);
_delay_ms(10);
lcd_clrscr();
_delay_ms(10);
lcd_gotoxy(0,0);
sprintf(Text,"H1=%02d T1=%+03d Amb:\nH2=%02d T2=%+03d H0=%02d\nH3=%02d T3=%+03d T0=%+03d\nH4=%02d T4=%+03d",BeeHive[1].Humidity_value, BeeHive[1].Temperature_value,BeeHive[2].Humidity_value, BeeHive[2].Temperature_value,BeeHive[0].Humidity_value, BeeHive[3].Humidity_value,BeeHive[3].Temperature_value, BeeHive[0].Temperature_value,BeeHive[4].Humidity_value, BeeHive[4].Temperature_value);
// sprintf(Text,"a:%d b:%d c:%d d:%d\n tel:%s\n index: %d",a1,b1,c1,d1,tel_num,sms_index);
// sprintf(Text,"HL:%d HH:%d\n tel:%s\n TL:%d TH:%d",Humidity_low_range,Humidity_high_range,tel_num, Temperature_low_range, Temperature_high_range);
// sprintf(Text,"High Limit:%d\nLow Limit:%d",Humidity_high_range, Humidity_low_range);
//UART_Transmit_string("AT+CMGF=1\r");
//_delay_ms(100);
//sprintf(Text,"%s",ser_rec);
//ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
//{
//UART_Transmit_string("AT\r");
//_delay_ms(1000);
//sprintf(Text,"%s",ser_rec);
//}
//
//UART_Transmit_string("AT\r");
//clear_sec_rec();
lcd_puts(Text);
_delay_ms(2000);
}
//SMS alert condition
for(int j=1;j<5;j++)
{
if(!SMS_alert)
{
if(BeeHive[j].Humidity_value>Humidity_high_range || BeeHive[j].Humidity_value<Humidity_low_range || BeeHive[j].Temperature_value>Temperature_high_range ||BeeHive[j].Temperature_value<Temperature_low_range)
{
//sending SMS alert
SMS_alert=1;
UART_Transmit_string("AT+CMGS=");
_delay_ms(100);
UART_Transmit_char(34);
_delay_ms(100);
UART_Transmit_string(tel_num);//the phone number
_delay_ms(100);
UART_Transmit_char(34);
_delay_ms(100);
UART_Transmit_string("\r");
_delay_ms(100);
clear_sec_rec();
sprintf(SMS_text,"Alert: H%d=%d%% T%d=%+d C\r",j,BeeHive[j].Humidity_value,j,BeeHive[j].Temperature_value);
UART_Transmit_string(SMS_text);
_delay_ms(100);
UART_Transmit_char(0x1A);
_delay_ms(100);
clear_sec_rec();
}
}
}
_delay_ms(100);
}
return 0;
}
ISR ( USART_RXC_vect )
{
if(a<(sizeof(ser_rec)))
{
ser_rec[a]=UDR;
if((ser_rec[a]!=0xd)&&(ser_rec[a]!=0xa)) //we do not need these bytes 0xd,0xa
{
a++;
INTERRUPT=1;
}
}
else
{
out=UDR;
clear_sec_rec();
}
}
警告: 这不是一个解决方案,而是清理您的代码以便您找到解决方案。那是因为你的代码有太多不必要的复杂性,很难分析它。
但是,有一些[明显的]错误...
您的 ISR 与您的任务级别赛跑
尽管 volatile
.
,但您的 ISR 与缓冲区任务级别的同步值得怀疑
当您尝试从任务级别访问它时,您需要在任务级别访问期间禁用中断。
您应该将访问权包装成 cli/sti
对。为 任何 任务级别访问 ser_rec
或 a
执行此操作。而且,not 只是围绕着循环中的 单个 字节获取。您应该将 cli
作为任务级函数的第一件事,将 sti
作为函数的最后一件事。
而且,我会将 a
重命名为更具描述性的名称,例如 ser_len
。
仅处理您知道您拥有
的数据
并且,在任务级别(例如 sms_decode
),您的循环限制是 sizeof(ser_rec)
,因此您正在访问 garbage/random 值或来自 的部分陈旧值上一条 条新消息。
而不是:
while (i < sizeof(ser_rec))
你可能想要:
while (i < a)
或者,如果您重命名:
while (i < ser_len)
如果这样做,clear_sec_rec
函数不再需要 将缓冲区清零,而只需重置 ser_len
。归零是为了减轻破环限制的尝试。
任务级别需要"slide"缓冲区
也就是说,如果当前传入的记录有(例如)8 个字符,但 ser_len
是(例如)12,任务级别必须 "slide" 缓冲区以删除已处理的记录:
memmove(&ser_rec[0],&ser_rec[8],ser_len - 8);
ser_len -= 8;
如果缓冲区溢出
,ISR 应该不重置缓冲区
如果输入的字符太多,您的 ISR 就是 "resetting" 缓冲区。这可能会中断任务级处理,因为您可以在处理过程中进行重置。
如果 buffer/ring 填满,ISR 应该设置一个 "overflow" 标志,任务级别可以检查 [并删除字符],而不是从任务级别下拉出地毯随着重置。
使用环形队列
但是,您可能希望将缓冲区转换为环形队列。这有点复杂,但允许从 ISR 到任务的流程是连续的。 (即)这将消除 ISR 重置缓冲区的需要。而且,任务级别将能够处理 多个 条记录而不会丢失任何记录。
设置过大的缓冲区大小可能会有所帮助(例如 char ser_rec[10000]
)。但是,只有在修复同步后才执行此操作。
你 可能 甚至能够使用原子(例如 stdatomic.h
在环形缓冲区索引上)并避免在任务 cli/sti
中的需要 [虽然后者比较慢但是比较简单。
消除不必要的复杂性
您正在使用 N 级 if/else
梯子而不是简单的 memcmp
:
char
sms_index1()
{
int i = 0;
char index = 0;
#if 0
while (i < (sizeof(ser_rec) - 4)) {
i++; // check if gsm sends SM",
if (ser_rec[i] == ((char) 'S')) {
i++;
if (ser_rec[i] == ((char) 'M')) {
i++;
if (ser_rec[i] == ((char) '"')) {
i++;
if (ser_rec[i] == ((char) ',')) {
i++;
index = ser_rec[i]; // return index
}
}
}
}
} // returns it
#else
while (i < (sizeof(ser_rec) - 4)) {
// return index
if (memcmp(&ser_rec[i],"SM\",",4) == 0) {
i += 4;
index = ser_rec[i];
}
i += 1;
}
#endif
// clears the ser_rec buffer if there was index
if (index)
clear_sec_rec();
return index;
}
将您的代码每行 80 个字符
在逻辑点分解长行
更改(例如):
if (BeeHive[j].Humidity_value > Humidity_high_range || BeeHive[j].Humidity_value < Humidity_low_range || BeeHive[j].Temperature_value > Temperature_high_range || BeeHive[j].Temperature_value < Temperature_low_range)
进入:
if ((BeeHive[j].Humidity_value > Humidity_high_range) ||
(BeeHive[j].Humidity_value < Humidity_low_range) ||
(BeeHive[j].Temperature_value > Temperature_high_range) ||
(BeeHive[j].Temperature_value < Temperature_low_range)) {
避免在代码中使用 [long] "sidebar" 注释。而是将它们放在上面的一行上。 (例)
更改(例如):
i++; // go forward only when there is a place for 16 characters
进入:
// go forward only when there is a place for 16 characters
i++;
对特殊值使用#define/enum
您也在做(例如)UART_Transmit_char(34);
34
是一个 "hardwired" 值。 mean/represent 是什么意思?做类似的事情:#define STARTCHAR 34 // framing char
然后做:UART_Transmit_char(STARTCHAR);
用空行分隔 related/unrelated 代码块
添加一些空行来分组。例如,您执行 UART_Transmit_char
,然后执行:_delay_ms(50)
。在 _delay_ms
后添加一个空行。或者,更好的做法是创建一个同时执行这两项操作的函数并调用 that 函数。
修复损坏的评论
您的一些评论是 incorrect/misplaced:
_delay_ms(200); // clear the buffer
编译带有警告的代码启用并修复所有警告
因为我无权访问 AVR 文件(例如 avr/interrupt.h
),所以我无法编译您的代码。但是,您应该使用 -Wall -O2
进行编译以生成警告并修复它们。而且,我怀疑你有一些。
我在 AVR (Atmega16) 和 GSM 模块之间有一个 UART 通信。通常,一切似乎都正常工作。问题是当我向 GSM 模块发送 SMS(例如 SMS:"H=0,95")时,在程序中每次收到 SMS 后都会有 SMS 响应。响应 (SMS) 已正确发送到手机 phone,但发生了用其他变量的随机数覆盖的情况。当我尝试使用终端调试它时,我想这是由于在 UART 中接收到大量字节引起的(变量 ser_rec 可能已损坏),但我无法在程序中找到它可能发生的位置。在 ISR 处理过程中,有一个条件可以避免这个问题,当最大字节数写入缓冲区时,缓冲区将被清除。
#include<avr/io.h> // Header file for basic avr input/output
//#define F_CPU 8000000UL
#include<util/delay.h> // header file for delay generation
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <util/atomic.h>
#include "rf22.h"
#include "lcd.h"
# define USART_BAUDRATE 9600
# define BAUD_PRESCALE ((( F_CPU / ( USART_BAUDRATE * 16UL))) - 1)
volatile unsigned char INTERRUPT=0;
volatile unsigned char DECODE_SMS=0;
uint16_t EEMEM HumidityLowRange=0;
uint16_t EEMEM HumidityHighRange=100;
int16_t EEMEM TemperatureLowRange=-40;
int16_t EEMEM TemperatureHighRange=80;
uint8_t EEMEM SMS_tel_number[14]="+421911243160";
unsigned char sms_index=0;
volatile int out=0;
int Temperature_low_range=-40;
int Temperature_high_range=80;
int Humidity_low_range=0;
int Humidity_high_range=100;
char tel_num[14];
//debugging tel num changing
unsigned char SMS_alert=0; //shows if SMS has been already sent
typedef struct
{
int Humidity_value;
int Temperature_value;
} BH;
BH BeeHive[5]; //for all BeeHives, ambient sensor included (considered as BeeHive without Heater)
void UART_Transmit_char( unsigned char data )
{
/* Wait for empty transmit buffer */
while ( !( UCSRA & (1<<UDRE)) );
/* Put data into buffer, sends the data */
UDR = data;
}
void UART_Transmit_string( char string[] )
{
int i=0;
while ( string[i] > 0)
{
UART_Transmit_char(string[i]);
i++;
}
}
unsigned char rx_tmp[40];
volatile char ser_rec[95]="";
volatile unsigned char a=0;
void clear_sec_rec()
{
int i=0;
while(i<sizeof(ser_rec))
{
ser_rec[i]=0;
i++;
}
i=0;
a=0;
}
char sms_index1()
{
int i=0;
char index=0;
while( i<(sizeof(ser_rec)-4))
{
i++; //check if gsm sends SM",
if(ser_rec[i]==((char) 'S'))
{
i++;
if(ser_rec[i]==((char) 'M'))
{
i++;
if(ser_rec[i]==((char) '"'))
{
i++;
if(ser_rec[i]==((char) ','))
{
i++;
index=ser_rec[i]; //return index
}
}
}
}
}//returns it
if(index) clear_sec_rec(); //clears the ser_rec buffer if there was index
return index;
}
void sms_decode()
{
int i=0;
char TemporaryString[95];
int Humidity_low_range_temp;
int Humidity_high_range_temp;
int Temperature_low_range_temp;
int Temperature_high_range_temp;
char SMS_text[80];
//clear buffers
strcpy(SMS_text, "");
strcpy(TemporaryString, "");
while( i<sizeof(ser_rec))
{
if(ser_rec[i]==((char) 'H'))
{
if(i<(sizeof(ser_rec)-4)) i++; //go forward only when there is a place for numbers =x,y, therefore number 4
if(ser_rec[i]==((char) '='))
{
i++;
memcpy(TemporaryString, &ser_rec[i], ((strlen(ser_rec))-i));
TemporaryString[(strlen(ser_rec)-i)]='[=10=]';
if((sscanf(TemporaryString,"%d,%d",&Humidity_low_range_temp,&Humidity_high_range_temp)==2))
{
if(Humidity_low_range_temp<Humidity_high_range_temp)
{
if((Humidity_low_range_temp>=0)&&(Humidity_high_range_temp<=100))
{
Humidity_high_range=Humidity_high_range_temp;
Humidity_low_range=Humidity_low_range_temp;
//and finally send the notification about changed limits
clear_sec_rec();
UART_Transmit_string("AT+CMGS=");
_delay_ms(50);
UART_Transmit_char(34);
_delay_ms(50);
UART_Transmit_string(tel_num);//the phone number
_delay_ms(50);
UART_Transmit_char(34);
_delay_ms(50);
UART_Transmit_string("\r");
_delay_ms(50);
clear_sec_rec(); //clears the buffer
sprintf(SMS_text,"Humidity range is set: Low=%d%% High=%d%%\r",Humidity_low_range, Humidity_high_range);
UART_Transmit_string(SMS_text);
_delay_ms(50);
UART_Transmit_char(0x1A);
_delay_ms(50);
clear_sec_rec();
_delay_ms(1000); //wait until response about credit will come
eeprom_update_word(&HumidityHighRange,Humidity_high_range);
eeprom_update_word(&HumidityLowRange,Humidity_low_range);
}
}
}
}
}
i++;
}
//clear buffers
strcpy(SMS_text, "");
strcpy(TemporaryString, "");
i=0;
//Temperature range set
while( i<(sizeof(ser_rec)-4))
{
if(ser_rec[i]==((char) 'T'))
{
i++;
if(ser_rec[i]==((char) '='))
{
if(i<(sizeof(ser_rec)-1)) i++;
memcpy(TemporaryString, &ser_rec[i], ((strlen(ser_rec))-i));
if((sscanf(TemporaryString,"%d,%d",&Temperature_low_range_temp,&Temperature_high_range_temp))==2)
{
if(Temperature_low_range_temp<Temperature_high_range_temp)
{
if((Temperature_low_range_temp>=-40)&&(Temperature_high_range_temp<=80))
{
Temperature_high_range=Temperature_high_range_temp;
Temperature_low_range=Temperature_low_range_temp;
//and finally send the notification about changed limits
clear_sec_rec();//clears the buffer
UART_Transmit_string("AT+CMGS=");
UART_Transmit_char(34);
_delay_ms(50);
UART_Transmit_string(tel_num);//the phone number
UART_Transmit_char(34);
UART_Transmit_string("\r");
_delay_ms(50);
clear_sec_rec();//clears the buffer
sprintf(SMS_text,"Temperature range is set: Low=%d C High=%d C\r",Temperature_low_range, Temperature_high_range);
UART_Transmit_string(SMS_text);
_delay_ms(50);
UART_Transmit_char(0x1A);
_delay_ms(50);
clear_sec_rec();
_delay_ms(1000); //wait until response about credit will come
eeprom_update_word(&TemperatureHighRange,Temperature_high_range);
eeprom_update_word(&TemperatureLowRange,Temperature_low_range);
}
}
}
}
}
i++;
}
//clear buffers
strcpy(SMS_text, "");
strcpy(TemporaryString, "");
i=0;
//Info SMS
while( i<(sizeof(ser_rec)-2)) //go forward only when there is a place for at least 3 characters
{
if(ser_rec[i]==((char) 'I'))
{
i++;
// b1=1;
if(ser_rec[i]==((char) 'N'))
{
i++;
// b1=2;
if(ser_rec[i]==((char) 'F'))
{
// b1=3;
i++;
if(ser_rec[i]==((char) 'O'))
{
//send info about all sensor values
clear_sec_rec();
_delay_ms(200);//clears the buffer
UART_Transmit_string("AT+CMGS=");
UART_Transmit_char(34);
UART_Transmit_string(tel_num);//the phone number
UART_Transmit_char(34);
UART_Transmit_char('\r');
_delay_ms(500);
clear_sec_rec();
_delay_ms(200);//clear the buffer
//T2=%dC H0=%d%%\nH3=%d%% T3=%dC T0=%dC\nH4=%d%% T4=%dC add , BeeHive[2].Temperature_value,BeeHive[0].Humidity_value, BeeHive[3].Humidity_value,BeeHive[3].Temperature_value, BeeHive[0].Temperature_value,BeeHive[4].Humidity_value, BeeHive[4].Temperature_value
sprintf(SMS_text,"H1=%d%% T1=%dC Amb:\nH2=%d%% T2=%dC H0=%d%%\nH3=%d%% T3=%dC T0=%dC\nH4=%d%% T4=%dC",BeeHive[1].Humidity_value, BeeHive[1].Temperature_value,BeeHive[2].Humidity_value, BeeHive[2].Temperature_value,BeeHive[0].Humidity_value, BeeHive[3].Humidity_value,BeeHive[3].Temperature_value, BeeHive[0].Temperature_value,BeeHive[4].Humidity_value, BeeHive[4].Temperature_value);
// c1=strlen(SMS_text);
UART_Transmit_string(SMS_text);
_delay_ms(50);
UART_Transmit_char(0x1A); //CTRL-Z - required by datasheet
_delay_ms(50);
// UART_Transmit_char('\n');
// d1=strlen(ser_rec);
clear_sec_rec();
_delay_ms(1000); //wait until response about credit will come
}
}
}
}
i++;
}
//Telephone number changing SMS
//clear buffers
i=0;
strcpy(SMS_text, "");
strcpy(TemporaryString, "");
while( i<(sizeof(ser_rec)-15))
{
if(ser_rec[i]==((char) 'T'))
{
i++; //go forward only when there is a place for 16 characters
if(ser_rec[i]==((char) 'E'))
{
i++;
if(ser_rec[i]==((char) 'L'))
{
i++;
if(ser_rec[i]==((char) ':'))
{
i++;
memcpy(TemporaryString, &ser_rec[i], 13);
TemporaryString[13]='[=10=]';
if((strlen(TemporaryString)==strlen(tel_num))&&(strlen(tel_num)==13))
{
memcpy(tel_num,&TemporaryString[0],strlen(TemporaryString));
//send info about changing number
clear_sec_rec();
_delay_ms(200);//clears the buffer
UART_Transmit_string("AT+CMGS=");
UART_Transmit_char(34);
UART_Transmit_string(tel_num);//the phone number
UART_Transmit_char(34);
UART_Transmit_string("\r");
_delay_ms(50);
clear_sec_rec();
_delay_ms(200);//clear the buffer
sprintf(SMS_text,"This is my new telephone number. BeeHiveMonitor");
UART_Transmit_string(SMS_text);
_delay_ms(50);
UART_Transmit_char(0x1A);
_delay_ms(50);
clear_sec_rec();
_delay_ms(1000); //wait until response about credit will come
eeprom_update_block((void*)&tel_num, (const void*)&SMS_tel_number, 13);
}
}
}
}
}
i++;
}
//Alert turn on SMS
//clear buffers
strcpy(SMS_text, "");
strcpy(TemporaryString, "");
i=0;
//Info SMS
while( i<sizeof(ser_rec))
{
if(ser_rec[i]==((char) 'K'))
{
if(i<(sizeof(ser_rec))) i++;
if(ser_rec[i]==((char) 'K'))
{
SMS_alert=0;
//send info alert turning on
clear_sec_rec();
_delay_ms(200);//clears the buffer
UART_Transmit_string("AT+CMGS=");
UART_Transmit_char(34);
UART_Transmit_string(tel_num);//the phone number
UART_Transmit_char(34);
UART_Transmit_string("\r");
_delay_ms(50);
clear_sec_rec();
_delay_ms(200);//clear the buffer
UART_Transmit_string("Alerts are turned on\r");
_delay_ms(50);
UART_Transmit_char(0x1A);
_delay_ms(50);
clear_sec_rec();
_delay_ms(1000); //wait until response about credit will come
}
}
i++;
}
i=0;
clear_sec_rec(); //clears the buffer
if(sms_index!=0)//deleting SMS
{
UART_Transmit_string("AT+CMGD=");
_delay_ms(50);
UART_Transmit_string("1,4\r"); //clear all SMSs in inbox
_delay_ms(50);
}
clear_sec_rec();
}
int main(void)
{
Humidity_high_range = eeprom_read_word(&HumidityHighRange); //get init values of previously set limits both Humidity and Temperature
Humidity_low_range = eeprom_read_word(&HumidityLowRange);
Temperature_high_range = eeprom_read_word(&TemperatureHighRange);
Temperature_low_range = eeprom_read_word(&TemperatureLowRange);
eeprom_read_block((void*)&tel_num, (const void*)&SMS_tel_number, 13);
sei();
char Text[80]; //character array for writing on the display
char SMS_text[80]; // character array for writing via SMS
char ATCommand[20];
int flag=0;
MCUCSR = (1<<JTD); // no -O0, interrupts disabled
MCUCSR = (1<<JTD); // do not use read-modify-write
DDRC=0xFF;
DDRD=0b11111010;
DDRB=0b10111111;
PORTB = PORTB | (1 << PB3); // Reset pin of SIM800L
_delay_ms(200);
DDRA=0xFF;
PORTC = PORTC | (1 << PC7); // turn on LCD backlight
//UART INIT
UCSRB = (1 << RXEN ) | (1 << TXEN );
UCSRC = (1 << URSEL ) | (1 << UCSZ0 ) | (1 << UCSZ1 );
UBRRH = (unsigned char)( BAUD_PRESCALE >> 8);
UBRRL = (unsigned char) BAUD_PRESCALE&0xFF ;
UCSRB |= (1 << RXCIE );
lcd_init(LCD_DISP_ON); // display initialization
rf22_init();
rf22_setfreq(RF22FREQ(869.545));
rf22_rxmode();
//GSM init 3 times AT
UART_Transmit_string("ATE0\r");
_delay_ms(200);
UART_Transmit_string("AT\r");
_delay_ms(200);
UART_Transmit_string("AT\r");
_delay_ms(200);
UART_Transmit_string("AT\r");
_delay_ms(200);
clear_sec_rec();
UART_Transmit_string("AT+CMGF=1\r");//set text mode
// while(!(strstr((char*)ser_rec,"OK")));
_delay_ms(200);
// while(!(strstr((char*)ser_rec,"OK")));
clear_sec_rec();
UART_Transmit_string("AT+CNMI=1,1,0,0,0\r");
// while(!(strstr((char*)ser_rec,"OK")));
_delay_ms(200);
clear_sec_rec();
UART_Transmit_string("AT+CMGD=1,4\r"); //delete all SMS in inbox
_delay_ms(100);
// UART_Transmit_string("1,4\r");
// while(!(strstr((char*)ser_rec,"OK")));
_delay_ms(200);
clear_sec_rec();
while(1)
{
if((INTERRUPT==1)&&(DECODE_SMS==0))
{
sms_index=sms_index1();
INTERRUPT=0;
if(sms_index!=0)
{
//if index exists - ready for decoding SMS
DECODE_SMS=1;
sprintf(ATCommand,"AT+CMGR=%c\r",sms_index);
UART_Transmit_string(ATCommand); //request for reading SMS with sms index
}
}
if((INTERRUPT==1)&&(DECODE_SMS==1))//decodes the sms
{
sms_decode();
DECODE_SMS=0; //finally clear both flags
INTERRUPT=0;
}
for(int x=0;x<5;++x)
{
rf22_getpacket(rx_tmp);
_delay_ms(10);
sscanf(rx_tmp,"_%d_%d_%d_%d_%d_%d_%d_%d_%d_%d",&BeeHive[0].Humidity_value, &BeeHive[0].Temperature_value,&BeeHive[1].Humidity_value, &BeeHive[1].Temperature_value,&BeeHive[2].Humidity_value, &BeeHive[2].Temperature_value,&BeeHive[3].Humidity_value, &BeeHive[3].Temperature_value,&BeeHive[4].Humidity_value, &BeeHive[4].Temperature_value);
_delay_ms(10);
lcd_clrscr();
_delay_ms(10);
lcd_gotoxy(0,0);
sprintf(Text,"H1=%02d T1=%+03d Amb:\nH2=%02d T2=%+03d H0=%02d\nH3=%02d T3=%+03d T0=%+03d\nH4=%02d T4=%+03d",BeeHive[1].Humidity_value, BeeHive[1].Temperature_value,BeeHive[2].Humidity_value, BeeHive[2].Temperature_value,BeeHive[0].Humidity_value, BeeHive[3].Humidity_value,BeeHive[3].Temperature_value, BeeHive[0].Temperature_value,BeeHive[4].Humidity_value, BeeHive[4].Temperature_value);
// sprintf(Text,"a:%d b:%d c:%d d:%d\n tel:%s\n index: %d",a1,b1,c1,d1,tel_num,sms_index);
// sprintf(Text,"HL:%d HH:%d\n tel:%s\n TL:%d TH:%d",Humidity_low_range,Humidity_high_range,tel_num, Temperature_low_range, Temperature_high_range);
// sprintf(Text,"High Limit:%d\nLow Limit:%d",Humidity_high_range, Humidity_low_range);
//UART_Transmit_string("AT+CMGF=1\r");
//_delay_ms(100);
//sprintf(Text,"%s",ser_rec);
//ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
//{
//UART_Transmit_string("AT\r");
//_delay_ms(1000);
//sprintf(Text,"%s",ser_rec);
//}
//
//UART_Transmit_string("AT\r");
//clear_sec_rec();
lcd_puts(Text);
_delay_ms(2000);
}
//SMS alert condition
for(int j=1;j<5;j++)
{
if(!SMS_alert)
{
if(BeeHive[j].Humidity_value>Humidity_high_range || BeeHive[j].Humidity_value<Humidity_low_range || BeeHive[j].Temperature_value>Temperature_high_range ||BeeHive[j].Temperature_value<Temperature_low_range)
{
//sending SMS alert
SMS_alert=1;
UART_Transmit_string("AT+CMGS=");
_delay_ms(100);
UART_Transmit_char(34);
_delay_ms(100);
UART_Transmit_string(tel_num);//the phone number
_delay_ms(100);
UART_Transmit_char(34);
_delay_ms(100);
UART_Transmit_string("\r");
_delay_ms(100);
clear_sec_rec();
sprintf(SMS_text,"Alert: H%d=%d%% T%d=%+d C\r",j,BeeHive[j].Humidity_value,j,BeeHive[j].Temperature_value);
UART_Transmit_string(SMS_text);
_delay_ms(100);
UART_Transmit_char(0x1A);
_delay_ms(100);
clear_sec_rec();
}
}
}
_delay_ms(100);
}
return 0;
}
ISR ( USART_RXC_vect )
{
if(a<(sizeof(ser_rec)))
{
ser_rec[a]=UDR;
if((ser_rec[a]!=0xd)&&(ser_rec[a]!=0xa)) //we do not need these bytes 0xd,0xa
{
a++;
INTERRUPT=1;
}
}
else
{
out=UDR;
clear_sec_rec();
}
}
警告: 这不是一个解决方案,而是清理您的代码以便您找到解决方案。那是因为你的代码有太多不必要的复杂性,很难分析它。
但是,有一些[明显的]错误...
您的 ISR 与您的任务级别赛跑
尽管 volatile
.
当您尝试从任务级别访问它时,您需要在任务级别访问期间禁用中断。
您应该将访问权包装成 cli/sti
对。为 任何 任务级别访问 ser_rec
或 a
执行此操作。而且,not 只是围绕着循环中的 单个 字节获取。您应该将 cli
作为任务级函数的第一件事,将 sti
作为函数的最后一件事。
而且,我会将 a
重命名为更具描述性的名称,例如 ser_len
。
仅处理您知道您拥有
的数据并且,在任务级别(例如 sms_decode
),您的循环限制是 sizeof(ser_rec)
,因此您正在访问 garbage/random 值或来自 的部分陈旧值上一条 条新消息。
而不是:
while (i < sizeof(ser_rec))
你可能想要:
while (i < a)
或者,如果您重命名:
while (i < ser_len)
如果这样做,clear_sec_rec
函数不再需要 将缓冲区清零,而只需重置 ser_len
。归零是为了减轻破环限制的尝试。
任务级别需要"slide"缓冲区
也就是说,如果当前传入的记录有(例如)8 个字符,但 ser_len
是(例如)12,任务级别必须 "slide" 缓冲区以删除已处理的记录:
memmove(&ser_rec[0],&ser_rec[8],ser_len - 8);
ser_len -= 8;
如果缓冲区溢出
,ISR 应该不重置缓冲区如果输入的字符太多,您的 ISR 就是 "resetting" 缓冲区。这可能会中断任务级处理,因为您可以在处理过程中进行重置。
如果 buffer/ring 填满,ISR 应该设置一个 "overflow" 标志,任务级别可以检查 [并删除字符],而不是从任务级别下拉出地毯随着重置。
使用环形队列
但是,您可能希望将缓冲区转换为环形队列。这有点复杂,但允许从 ISR 到任务的流程是连续的。 (即)这将消除 ISR 重置缓冲区的需要。而且,任务级别将能够处理 多个 条记录而不会丢失任何记录。
设置过大的缓冲区大小可能会有所帮助(例如 char ser_rec[10000]
)。但是,只有在修复同步后才执行此操作。
你 可能 甚至能够使用原子(例如 stdatomic.h
在环形缓冲区索引上)并避免在任务 cli/sti
中的需要 [虽然后者比较慢但是比较简单。
消除不必要的复杂性
您正在使用 N 级 if/else
梯子而不是简单的 memcmp
:
char
sms_index1()
{
int i = 0;
char index = 0;
#if 0
while (i < (sizeof(ser_rec) - 4)) {
i++; // check if gsm sends SM",
if (ser_rec[i] == ((char) 'S')) {
i++;
if (ser_rec[i] == ((char) 'M')) {
i++;
if (ser_rec[i] == ((char) '"')) {
i++;
if (ser_rec[i] == ((char) ',')) {
i++;
index = ser_rec[i]; // return index
}
}
}
}
} // returns it
#else
while (i < (sizeof(ser_rec) - 4)) {
// return index
if (memcmp(&ser_rec[i],"SM\",",4) == 0) {
i += 4;
index = ser_rec[i];
}
i += 1;
}
#endif
// clears the ser_rec buffer if there was index
if (index)
clear_sec_rec();
return index;
}
将您的代码每行 80 个字符
在逻辑点分解长行
更改(例如):
if (BeeHive[j].Humidity_value > Humidity_high_range || BeeHive[j].Humidity_value < Humidity_low_range || BeeHive[j].Temperature_value > Temperature_high_range || BeeHive[j].Temperature_value < Temperature_low_range)
进入:
if ((BeeHive[j].Humidity_value > Humidity_high_range) ||
(BeeHive[j].Humidity_value < Humidity_low_range) ||
(BeeHive[j].Temperature_value > Temperature_high_range) ||
(BeeHive[j].Temperature_value < Temperature_low_range)) {
避免在代码中使用 [long] "sidebar" 注释。而是将它们放在上面的一行上。 (例)
更改(例如):
i++; // go forward only when there is a place for 16 characters
进入:
// go forward only when there is a place for 16 characters
i++;
对特殊值使用#define/enum
您也在做(例如)UART_Transmit_char(34);
34
是一个 "hardwired" 值。 mean/represent 是什么意思?做类似的事情:#define STARTCHAR 34 // framing char
然后做:UART_Transmit_char(STARTCHAR);
用空行分隔 related/unrelated 代码块
添加一些空行来分组。例如,您执行 UART_Transmit_char
,然后执行:_delay_ms(50)
。在 _delay_ms
后添加一个空行。或者,更好的做法是创建一个同时执行这两项操作的函数并调用 that 函数。
修复损坏的评论
您的一些评论是 incorrect/misplaced:
_delay_ms(200); // clear the buffer
编译带有警告的代码启用并修复所有警告
因为我无权访问 AVR 文件(例如 avr/interrupt.h
),所以我无法编译您的代码。但是,您应该使用 -Wall -O2
进行编译以生成警告并修复它们。而且,我怀疑你有一些。