【问题标题】:Array in Proteus using WinAVR C size issue使用 WinAVR C 大小问题的 Proteus 中的数组
【发布时间】:2016-03-19 00:02:13
【问题描述】:

我有一个实验室任务,需要我使用 Atmega328P 进行 ADC,并使用 USART 将数字值传输到 MILFORD-4X20-BKP LCD 显示器。 LCD 需要在第一行以 2 字节格式(十进制,0-255)显示值,在第三行以字格式(4 字节,0-1023)显示值。

我成功地做到了这一点,但由于我不确定数组的大小,所以我最初将它们都设置得足够大,不会出现问题。当我将其更改为我认为必要的内容时,我遇到了一个奇怪的错误。这是下面显示的奇怪符号(或者我猜在底部)。该位置的符号取决于电位器的值。

这是我的想法。 我为 buff 分配了 36 个(位置 0 为 +1)位置,这些位置被发送到 LCD。我将 3 分配给 buff2 作为单词值(4 n 个位置) 最后是 4 用于 buff1 的 2 字节值(5 n 个位置)

buff[36]; buff1[4]; buff2[3];

单词值的 3n 个位置有效,但是当我为 2byte 值放置 4n 时,出现了错误。请看第一张图片。

根据buff和buff1的不同数组值,bug也以0-255值的一部分出现在第3行末尾的形式出现。第二张照片有 buff[37], buff1[2], buff2[3]

最后一点,如果我将值更改为buff1[5],错误就会消失..但是为什么呢? 2 字节的数组大小应该小于 4 字节的数组大小。

The weird bug

The LCD

我正在尽力解释我的问题,但不知道我是否足够清楚。我知道我正在让我的数组交叉到彼此的内存地址,但我不知道如何以及在哪里。

/*
 * Serial Lcd.c
 * 
 * Use's a 4x20 serial LCD display.
 *
 * Adapted by Phil J to suit Atmega328P: 15/2/2015 (corrected Usart_Rx Int Vector address ref. for 328)
 *
 * Editted by Tomi Fodor
 *
 */ 

#define F_CPU 16000000UL
#define BAUDRATE 9600 - change to External 16MHz crystal on MCU

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "stdlib.h"
#include "USART.h"

// Global Variables
// Note the use of the volatile keyword to ensure that the compiler knows that these variables can be changed at 
// any time, including by the ISR

volatile int i=0; 
volatile uint16_t buffer[]; // 20 place array
volatile char buff[36];     // var sent out value
volatile char buff1[4];     // var for the pot value / 4 ***** HAS TO BE AT LEAST 4 FOR SOME REASON (5 w/o bug), SHOULD BE FINE AT 2
volatile char buff2[3];     // var for the actual pot value
volatile uint16_t StrRxFlag=0;
volatile int Ana, Bell;     // pot value

int main(void)
{
   buff[4]=' ';buff[5]='P';buff[6]='o';buff[7]='t';buff[8]=' ';buff[9]='V';buff[10]='a';buff[11]='l';buff[12]='(';buff[13]='D';buff[14]=')'; // constants to be displayed
   _delay_ms(500);

   ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1);    // Enables the ADC, sets the ADC to use the division factor 64 for the ADC clock 
   USART_interrupt_init();
   USART_putstring("Ready ");           // Send String to the LCD
// USART_putstring(buff3);                  
   USART_send('\r');                // Send carriage return
// USART_send('\n');                // Send linefeed
   _delay_ms(500);              // Allows for the LCD module to initialize

   while(1)
      {
     USART_send(254);       // LCD control mode
     USART_send(0);         // LCD HOME command
     USART_send(254);
     USART_send(1);         // LCD CLEAR SCREEN
     buff[0] = ' ';         // Required for offset of display
     buff[4] = ' ';         // Signifies terminator of pot
     ADCSRA |= (1<<ADSC);       // Starts A-D conversion
     while (ADCSRA & (1<<ADSC));    // Wait till A-D conversion is complete
     Ana = ADCW/4;          // Get A-D result
     Bell = ADCW;           // Get actual A-D result
     itoa(Ana,buff1,10);        // Creats the dec value of the Analogue value [stdlib.h]
     itoa(Bell,buff2,10);       // actual

     if (buff1[1] == '\0')      // If only 1 digit
        {
           buff[1] = ' ';       // Not hundreds
           buff[2] = ' ';       // Not tens
           buff[3] = buff1[0];  // Place in single digit
        }
     else if(buff1[2] == '\0')  // If only 2 digits
        {
           buff[1] = ' ';       // Not hundreds
           buff[2] = buff1[0];  // Shift
           buff[3] = buff1[1];  // Shift
        }
     else
        {
           buff[1] = buff1[0];  // Shift
           buff[2] = buff1[1];  // Shift
           buff[3] = buff1[2];  // Shift
        }

     for(i=0;i<25;i++)
        {
           buff[i+15] = ' ';
        }
buff[25]=' ';buff[26]='P';buff[27]='o';buff[28]='t';buff[29]=' ';buff[31]='V';buff[32]='a';buff[33]='l';buff[34]='(';buff[35]='D';buff[36]=')'; // constants to be displayed

     if (buff2[1] == '\0')      // If only 1 digit
        {
           buff[21] = ' ';      // Not thousands
           buff[22] = ' ';      // Not hundreds
           buff[23] = ' ';      // Not tens
           buff[24] = buff2[0]; // Place in single digit
        }
     else if(buff2[2] == '\0')  // If only 2 digits
        {
           buff[21] = ' ';      // Not thousands
           buff[22] = ' ';      // Not hundreds
           buff[23] = buff2[0]; // Shift
           buff[24] = buff2[1]; // Shift
        }
     else if(buff2[3] == '\0')  // If only 3 digits
        {
           buff[21] = ' ';      // Not thousands
           buff[22] = buff2[0]; // Shift
           buff[23] = buff2[1]; // Shift
           buff[24] = buff2[2]; // Shift
        }
     else
        {
           buff[21] = buff2[0]; // Shift
           buff[22] = buff2[1]; // Shift
           buff[23] = buff2[2]; // Shift
           buff[24] = buff2[3]; // Shift
        }

          USART_putstring(buff);
          USART_send('\r'); 
         _delay_ms(500);
    }
}

//ISR(USART0_RX_vect) - not for 328
ISR(USART_RX_vect)              //this is the right vector ref, not above
{   
   buffer[i]=UDR0;              //Read USART data register
   if(buffer[i++]=='\r')            //check for carriage return terminator and increment buffer index
      { 
     // if terminator detected
     StrRxFlag=1;           //Set String received flag 
     buffer[i-1]=0x00;      //Set string terminator to 0x00
     i=0;               //Reset buffer index
      }
}

【问题讨论】:

  • 您帖子中显示的代码是否 == 您编译、运行并观察到您的错误的代码?
  • 我发布的主要代码是我运行的代码是的。它应该是导致首先描述的问题的相同代码,并显示在第一张图片中。我确实在我的问题中重复了相同代码的一部分,这可能会导致一些混乱。 (buff[36],buff1[4],buff2[3])。

标签: c arrays memory-management winavr


【解决方案1】:

您引用的问题可能是由于调用USART_putstring(buff); 时出现非空终止字符串所致。 C 字符串 by definition 要求最后一个字符是 \0 ( NULL )。示例:

给定char string[5];

|h|e|r|e|\0| 是合法的
|h|e|r|e|s| |a| |b|u|g| 填充缓冲区,但不是字符串

在您的示例中有些地方您将非 NULL 字符写入缓冲区的最后一个元素。例如 buff 被创建为一个包含 36 个元素的数组:

volatile char buff[36];     // var sent out value

行内:

buff[25]=' ';buff[26]='P';buff[27]='o';buff[28]='t';buff[29]=' ';buff[31]='V';buff[32]='a';buff[33]='l';buff[34]='(';buff[35]='D';buff[36]=')'; // constants to be displayed

索引 35(最后一个合法索引)填充有 D 字符。索引 36(不合法)填充有 ),至少应该导致运行时错误。那么根据定义,因为它不是以 NULL 结尾的,所以它不是一个字符串,并且将其用作一个字符串将导致 Undefined Behavior

这里也用 3 个元素创建 buf2:

volatile char buff2[3];     // var for the actual pot value 

但是在这一行,使用了索引3:(只有0-2有效)

else
        {
           buff[21] = buff2[0]; // Shift
           buff[22] = buff2[1]; // Shift
           buff[23] = buff2[2]; // Shift
           buff[24] = buff2[3]; // Shift <<< only buff2[0] - buff2[2] are legal
        }

这些错误会编译,但会导致运行时指针越界。

此变量的大小未定义,应标记错误:

volatile uint16_t buffer[];  // 20 place array

假设是您希望它是:

volatile uint16_t buffer[20];  // 20 place array

稍后,你在这里使用它:

ISR(USART_RX_vect)              //this is the right vector ref, not above
{   
   buffer[i]=UDR0;   

因为我不确定您正在使用什么 C 标准(即,如果它不是 ANSI C)我不知道您的环境是否会在编译时为 int 数组标记未定义的大小。但由于你的数组大小的其余部分是定义的,这个看起来很可疑。

另外,我看到你将我定义为:

volatile int i=0;

i 的边界是否众所周知? i 是否有可能超过 19 的值? (假设数组在某个时候被初始化)

【讨论】:

  • 对不起,我相信我包含了标签,但没有在问题中提及。这是 Proteus 上的 WinAVR。 TBH 我不确定它遵循的规则。我认为未定义的数组会采用它需要的大小,但也许这是一个愚蠢的错误。我首先从 Arduino 学习编程。 Null 字符很有意义。我不记得如果我将其留空,编译器是否会自动将它们添加到字符串中,但我会在明天早上修复这些。奇怪的错误更有意义,因为所有 3 个数组都有这个缺点,所以我会尽快更新。谢谢。
  • @TomiFodor - 不,未定义的数组不会自动调整大小。但是,这种初始化方法会起作用: char array[]={"string"};像这样初始化,字符串数组将自动以 NULL 结束,当然数组的大小将为 7,可见字符为 6,NULL 为 1。在字符串中为 NULL 添加空间(并应用)可能会解决您所说的问题。
  • 好的,我做到了,它消除了这个错误,但这是令人讨厌的事情。 buff2,一个似乎需要一个包含 5 个元素的数组的值,只需要 3 个元素就可以很好地工作。这似乎没有任何意义。
  • 易失性 char buff2[3]; // var 用于实际底池值,稍后我会这样做:buff[24] = buff2[3]; // Shift ...但它有效!?即使 buff2 的位置只有 0,1 和 2 .. 元素 3 甚至不存在.. 更不用说 '\0'
  • 好的,我想我明白了。它仍然有效,因为它在 buff2 之后的某个地方使用内存。它仍然正确地指向它。只是如果我有另一个阵列,我会有交叉。这是我之前的问题。正确的?我会假设我是正确的。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-22
相关资源
最近更新 更多