我正在裸机 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 。
使用指针时必须非常小心。通过指针调用函数时,您必须 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
存储在数组中。这会导致从未定义的数组索引调用指针。这会自动导致代码崩溃。
要将数字转换为七段表示的值,无论如何都不需要这种令人头疼的程序构造。
程序的其余部分包含许多很多不正确的编程结构,并且可能还有其他原因导致崩溃。