函数指针导致裸机 ATMega328PB 极其奇怪的行为

问题描述 投票:0回答:1

我正在裸机 ATMega328PB 上制作数字手表项目。 uC 的任务之一是使用 B 组和 D 组的引脚作为数字输出来驱动 7 段显示器。我正在使用带有 MiniCore 的 Arduino IDE 和 Arduino UNO 作为 ISP 程序员上传代码。我将所有保险丝保留为出厂默认值(内部振荡器等),并且不使用任何引导加载程序。上传代码工作正常,但在我的代码中,有一些功能可以将正确的引脚驱动为高电平以显示正确的数字。我需要将每个函数的执行分配给相应的整数,所以我使用了函数指针数组。

当我上传使用“display_write”函数的代码时,我的uC停止正确驱动显示引脚,并且即使在复位引脚上有10k上拉电阻的情况下,它也表现得好像在不断重置。我非常确定这是一个软件问题。我将我的项目焊接在自制的 PCB 上,并且我多次检查电源是否开始供电(通过示波器),并且现在任何引脚上都出现了意外事件。在此状态下,ATMega 在某些段的阳极(连接到 PD 引脚)上产生短脉冲,但所有阴极(这是 5 位共阴极显示器)不会被拉低(通过 NPN 晶体管)。

在此状态下与uC没有通信。然而,当我从显示器上断开一些阳极驱动引脚时,uC 开始正常工作,直到我再次连接它们,并且总是有相同的 2 个引脚在断开连接后暂时返回到正常状态。我已经对所有硬件进行了测量(各段消耗的电流小于 1mA),这种配置或其他配置绝对没有什么特别之处。仅当我在程序中执行使用函数指针的函数时才会发生这种情况,当我不使用函数指针时一切正常。

老实说,我不知道是什么导致了这种行为。我修改了代码以不使用计时器中断,但它也没有改变任何内容,直到我使用“display_write”函数。

这是上传后的代码。

/*
Wiring:
PD0 - a
PD1 - b
PD2 - c
PD3 - d
PD4 - e
PD5 - f
PD6 - g
PD7 - dp

PB0 - CA1 - position 0
PB1 - CA2 - position 1
PB2 - CA3 - position 2
PB6 - CA4 - position 3
PB7 - CA5 - position 4
*/

#include <Wire.h> //Library needed for I2C communication with RTC module
#include <Arduino.h>

#define digits_count    5

//DS1337 RTC register addresses
#define rtc_address     0x68
#define second          0x00
#define minute          0x01
#define hour            0x02
#define day             0x03
#define date            0x04
#define month           0x05
#define year            0x06
#define alarm1_minute   0x08
#define alarm1_hour     0x09
#define alarm1_state    0x0A
#define alarm2_minute   0x0B
#define alarm2_hour     0x0C
#define alarm2_state    0x0D

//Curent position for display
volatile int current_position = 0;

//Current digits array with sign number and dot point status
volatile int current_display[digits_count][2];

//Position lookup table for PORT manipulation
volatile int position_LUT[digits_count] = {0b00000001, 0b00000010, 0b00000100, 0b01000000, 0b10000000};

//Pointers assigning intigers to corresponding functions that display digits/signs they represent
volatile void (*fun_ptr[11])(int, bool);

//Interrupt service routine for Timer 1 to regularly update display
ISR(TIMER1_COMPA_vect) {
  display_write(current_position);
}

void setup() {
  //Set Timer 1 to interrupt and refresh screen periodacly
  TCCR1A  = 0b00000000; 
  TCCR1B  = 0b00001001; //No prescaler, CTC for OCR1A
  TCNT1   = 0;          //Initial value
  TIMSK1 |= 0b00000010; //Set interrupt on compare A
  OCR1A   = 5000;       //Display new digit every 5ms
  //Set D and B pins as outputs for driving the display
  DDRD =  0b11111111;
  DDRB =  0b11111111;
  //Default output state
  PORTD = 0b00000000;
  PORTB = 0b00000000;
  //Assigning function pointers to functions
  fun_ptr[0] = d0;
  fun_ptr[1] = d1;
  fun_ptr[2] = d2;
  fun_ptr[3] = d3;
  fun_ptr[4] = d4;
  fun_ptr[5] = d5;
  fun_ptr[6] = d6;
  fun_ptr[7] = d7;
  fun_ptr[8] = d8;
  fun_ptr[9] = d9;
  fun_ptr[10] = null;

  //Initial diplay values
  current_display[0][0] = 4;
  current_display[1][0] = 7;
  current_display[2][0] = 2;
  current_display[3][0] = 9;
  current_display[4][0] = 8;
  current_display[0][1] = 1;
  current_display[1][1] = 1;
  current_display[2][1] = 0;
  current_display[3][1] = 0;
  current_display[4][1] = 1;

  //Initialize I2C communication with RTC
  Wire.begin();
}


void loop() {
  current_display[0][1] = 0;
  current_display[1][1] = 1;
  current_display[2][1] = 0;
  current_display[3][1] = 1;
  current_display[4][1] = 0;
  rtc_read(hour, 0, 1, 1);
  rtc_read(minute, 2, 3, 1);
  rtc_read(second, 4, 4, 0);;
  delay(1);
}

//Read data from RTC and insert digits to positions in display array
void rtc_read(int address, int position1, int position2, bool position2_valid) {
  int data_bcd;
  int data_dec;
  Wire.beginTransmission(rtc_address);
  Wire.write(address);
  Wire.endTransmission();
  Wire.requestFrom(rtc_address,1);
  if (Wire.available()) {
    data_bcd = Wire.read();
  }
  data_dec = (data_bcd / 16 * 10) + (data_bcd % 16);  //Convert RTS's BCD format to decimal
  current_display[position1][0] = data_dec / 10;      //Write most siginificant digit to position 1
  if (position2_valid != 0) {
  current_display[position2][0] = data_dec % 10;      //Write least siginificant digit to position 2
  }
}

//Display array function
void display_write(int position) {
  (*fun_ptr[current_display[position][0]])(position, current_display[position][1]);
  current_position++;
  if (current_position == digits_count) {
    current_position = 0;
  }
}

//Digit/sign display functions

void d0(int position, bool dp_status) {
  PORTD = 0b00111111; // Set segment configuration
  PORTB = position_LUT[position]; //Disable all digits and add bit corresponding to position argument from LUT
  PORTD |= (dp_status << PD7); //Add dot point bit
}

void d1(int position, bool dp_status) {
  PORTD = 0b00000110;
  PORTB = position_LUT[position];
  PORTD |= (dp_status << PD7);
}

void d2(int position, bool dp_status) {
  PORTD = 0b01011011;
  PORTB = position_LUT[position];
  PORTD |= (dp_status << PD7);
}

void d3(int position, bool dp_status) {
  PORTD = 0b01001111;
  PORTB = position_LUT[position];
  PORTD |= (dp_status << PD7);
}

void d4(int position, bool dp_status) {
  PORTD = 0b01100110;
  PORTB = position_LUT[position];
  PORTD |= (dp_status << PD7);
}

void d5(int position, bool dp_status) {
  PORTD = 0b01101101;
  PORTB = position_LUT[position];
  PORTD |= (dp_status << PD7);
}

void d6(int position, bool dp_status) {
  PORTD = 0b01111101;
  PORTB = position_LUT[position];
  PORTD |= (dp_status << PD7);
}

void d7(int position, bool dp_status) {
  PORTD = 0b00000111;
  PORTB = position_LUT[position];
  PORTD |= (dp_status << PD7);
}

void d8(int position, bool dp_status) {
  PORTD = 0b01111111;
  PORTB = position_LUT[position];
  PORTD |= (dp_status << PD7);
}

void d9(int position, bool dp_status) {
  PORTD = 0b01101111;
  PORTB = position_LUT[position];
  PORTD |= (dp_status << PD7);
}

void null(int position, bool dp_status) {
  PORTD = 0b00000000;
  PORTB = position_LUT[position];
  PORTD |= (dp_status << PD7);
}

我尝试了所有可能的代码组合,并且在执行“display_write”函数时总会出现奇怪的行为(不断重置且无通信)。 我已经检查了所有连接和硬件问题,并测量了来自 uC 和电源(USB 5V)的所有信号。

我希望代码定期检查“current_display”数组中每个位置的整数,并通过函数指针数组执行与该整数对应的函数 d0/d1...null 。

c arduino atmega
1个回答
0
投票

使用指针时必须非常小心。通过指针调用函数时,您必须 100% 确定将调用正确的指针。对于您的情况,这是无法保证的。

以下代码允许将大于 10 的值输入到数组中。

  data_dec = (data_bcd / 16 * 10) + (data_bcd % 16);  //Convert RTS's BCD format to decimal
  current_display[position1][0] = data_dec / 10;      //Write most siginificant digit to position 1

例如,变量

data_bcd
包含值
0xFF
就足够了。
data_dec
变量的值为
165
。这会导致值
16
存储在数组中。这会导致从未定义的数组索引调用指针。这会自动导致代码崩溃。

要将数字转换为七段表示的值,无论如何都不需要这种令人头疼的程序构造。

程序的其余部分包含许多很多不正确的编程结构,并且可能还有其他原因导致崩溃。

© www.soinside.com 2019 - 2024. All rights reserved.