我在AVR(Atmega16)和GSM模块之间有一个UART通信。正常情况下,一切似乎都能正常工作。问题是当我向GSM模块发送SMS(例如SMS: "H=0,95")时,在程序中,每次收到SMS后都有SMS响应。响应(SMS)被正确地发送到手机上,但会被其他变量的随机数覆盖。当我试着用终端调试时,我想这是由于在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)]='\0';
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]='\0';
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是 赛车 与您的任务级别
你的ISR与你的缓冲区的任务级同步是值得怀疑的,尽管你的缓冲区有了 volatile
.
当你试图从任务级访问时,你需要在任务级访问期间禁用中断。
你应该将访问包裹在 cli/sti
对。这样做是为了 任何 任务级访问 ser_rec
或 a
. 还有: 不 左右 单一 循环中的字节获取。你应该把 cli
作为任务级函数的第一件事,并把 sti
作为函数的最后一件事。
而且,我会把 a
到更多的描述性的东西,如 ser_len
.
只处理以下数据 知道 你有
而且,在任务级(例如 sms_decode
),你的循环极限是 sizeof(ser_rec)
,所以你是在从一个叫做 以前 收到的信息。
而不是:
while (i < sizeof(ser_rec))
你可能想。
while (i < a)
或者,如果你做重命名,
while (i < ser_len)
如果你这样做, clear_sec_rec
不灵了 需要 将缓冲区清零,但只需重置 ser_len
. 清零是为了缓解断环限制。
任务级需要 "滑动 "缓冲区。
也就是说,如果当前传入的记录有(例如)8个chars,而 ser_len
是(例如)12,任务级必须 "滑动 "缓冲区以删除处理过的记录。
memmove(&ser_rec[0],&ser_rec[8],ser_len - 8);
ser_len -= 8;
ISR应该 不 如果缓冲区溢出,则重置缓冲区
如果有太多的字符进入,你的ISR正在 "重置 "缓冲区。这可能会破坏任务级的处理,因为你可以在处理过程中进行重置。
如果缓冲区填满了,ISR应该设置一个 "溢出 "标志,任务级可以检查[并直接丢弃char],而不是用重置把任务级的地毯拽出来。
使用环形队列
但是,你可能想把缓冲区转换为环形队列。这比较复杂,但可以让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)) {
避免在代码中使用[长]"边栏 "注释。而是把它们放在上面的一行。(e.g.)
更改(e.g.):Into:避免在代码中使用[长]"侧栏 "注释。
i++; // go forward only when there is a place for 16 characters
Into: 使用:
// go forward only when there is a place for 16 characters
i++;
使用... #define/enum
对于特殊值
你也在做(如 UART_Transmit_char(34);
的 34
是一个 "硬连接 "的值。它代表什么意思呢?做这样的事情。#define STARTCHAR 34 // framing char
然后再做。UART_Transmit_char(STARTCHAR);
用空行分开相关的代码块,不相关的代码块
增加一些空行来分组的东西。例如,你可以做 UART_Transmit_char
然后做。_delay_ms(50)
. 后面加一个空行 _delay_ms
. 或者,更好的办法是创建一个同时做这两件事的函数,然后调用 该 功能。
修复破损的注释
你的一些评论是不正确的错位。
_delay_ms(200); // clear the buffer
编译带有警告的代码时,可以启用并修复所有的警告。
因为我无法访问AVR文件(如 avr/interrupt.h
),我不能编译你的代码。但是,你应该用 -Wall -O2
生成警告并修复它们。而且,我怀疑你有一些。