You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Atmega328p按下按键时获取GPS数据问题求助

问题描述

我正在实现按键触发外部中断时获取GPS数据的功能:硬件采用Quectel L80-M39模块连接Atmega328p的RX引脚,按键接PD3引脚。无外部中断时功能正常,但添加中断后无法获取GPS数据,怀疑代码存在问题,以下是我的代码:

#define F_CPU 8000000UL // Defining the CPU Frequency

#include <avr/io.h>     // Contains all the I/O Register Macros
#include <util/delay.h> // Generates a Blocking Delay
#include <avr/interrupt.h>

#define USART_BAUDRATE 9600 // Desired Baud Rate
#define BAUD_PRESCALER (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)

void USART_Init()
{
    // Set Baud Rate
    UBRR0H = (unsigned char)(BAUD_PRESCALER >> 8);
    UBRR0L = (unsigned char)BAUD_PRESCALER;

    // Enable Receiver
    UCSR0B |= (1 << RXEN0);

    // Set data frame format: asynchronous mode,no parity, 1 stop bit, 8 bit size
    UCSR0C |= (1 << UCSZ01) | (1 << UCSZ00);
}

unsigned char USART_ReceivePolling(void)
{
    while (!(UCSR0A & (1 << RXC0)));
        return UDR0;
}


void readgps()
{
    uint8_t lati_value[15];
    unsigned char LocalData;
    int i = 0;
    LocalData = USART_ReceivePolling();
    if (LocalData == '$')
    {
        LocalData = USART_ReceivePolling();
        if (LocalData == 'G')
        {
            LocalData = USART_ReceivePolling();
            if (LocalData == 'P')
            {
                LocalData = USART_ReceivePolling();
                if (LocalData == 'G')
                {
                    LocalData = USART_ReceivePolling();
                    if (LocalData == 'G')
                    {
                        LocalData = USART_ReceivePolling();
                        if (LocalData == 'A')
                        {
                            LocalData = USART_ReceivePolling();
                            if (LocalData == ',')
                            {
                                LocalData = USART_ReceivePolling();
                                while (LocalData != ',')
                                {
                                    LocalData = USART_ReceivePolling();
                                }
                                LocalData = USART_ReceivePolling();
                                while (LocalData != ',')
                                {
                                    lati_value[i] = LocalData;
                                    ++i;
                                    LocalData = USART_ReceivePolling();
                                }
                                PORTD |= (1 << PD2);
                                _delay_ms(100);
                            }
                        }
                    }
                }
            }
        }
    } else 
    {
        PORTD &= ~(1 << PD2);
    }
}

int main(void)
{
    EICRA |= (1 << ISC10) | (1 << ISC11); // interrupt on rising edge of INT1
    EIMSK |= (1 << INT1);                 // external interrupt enable on INT1
    
    sei();
    while (1)
    {

    }
}

ISR(INT1_vect)
{
    if (PIND & (1 << 3))
    {
        DDRD = 0b01110110;
        USART_Init();
        readgps();
    }
} 
问题排查与修复建议

核心问题点

  • 中断内初始化USART:GPS模块持续输出数据,中断触发前USART未初始化,会丢失前期数据;且每次中断才初始化,可能错过当前帧数据,同时浪费资源。
  • 引脚配置错误:中断内直接设置DDRD = 0b01110110,将PD3(按键输入)改为输出,导致后续按键无法触发中断。
  • 中断阻塞风险:readgps()采用阻塞式USART读取,中断服务函数长时间阻塞会导致系统无法响应其他事件,甚至卡死。
  • 按键无消抖:机械按键的抖动会导致多次触发中断,干扰正常逻辑。

修改后的参考代码

#define F_CPU 8000000UL 

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

#define USART_BAUDRATE 9600 
#define BAUD_PRESCALER (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)

volatile uint8_t read_gps_flag = 0; // 读取GPS标志位

void USART_Init()
{
    // 设置波特率
    UBRR0H = (unsigned char)(BAUD_PRESCALER >> 8);
    UBRR0L = (unsigned char)BAUD_PRESCALER;

    // 使能接收器
    UCSR0B |= (1 << RXEN0);

    // 设置帧格式:异步、无校验、1停止位、8位数据
    UCSR0C |= (1 << UCSZ01) | (1 << UCSZ00);
}

unsigned char USART_ReceivePolling(void)
{
    while (!(UCSR0A & (1 << RXC0)));
        return UDR0;
}

void readgps()
{
    uint8_t lati_value[15];
    unsigned char LocalData;
    int i = 0;

    // 清空接收缓冲区残留数据
    while(UCSR0A & (1 << RXC0)) {
        UDR0;
    }

    LocalData = USART_ReceivePolling();
    if (LocalData == '$')
    {
        LocalData = USART_ReceivePolling();
        if (LocalData == 'G')
        {
            LocalData = USART_ReceivePolling();
            if (LocalData == 'P')
            {
                LocalData = USART_ReceivePolling();
                if (LocalData == 'G')
                {
                    LocalData = USART_ReceivePolling();
                    if (LocalData == 'G')
                    {
                        LocalData = USART_ReceivePolling();
                        if (LocalData == 'A')
                        {
                            LocalData = USART_ReceivePolling();
                            if (LocalData == ',')
                            {
                                LocalData = USART_ReceivePolling();
                                while (LocalData != ',')
                                {
                                    LocalData = USART_ReceivePolling();
                                }
                                LocalData = USART_ReceivePolling();
                                // 限制数组索引,防止越界
                                while (LocalData != ',' && i < 14)
                                {
                                    lati_value[i] = LocalData;
                                    ++i;
                                    LocalData = USART_ReceivePolling();
                                }
                                PORTD |= (1 << PD2);
                                _delay_ms(100);
                                PORTD &= ~(1 << PD2);
                            }
                        }
                    }
                }
            }
        }
    } else 
    {
        PORTD &= ~(1 << PD2);
    }
    read_gps_flag = 0; // 重置标志位
}

int main(void)
{
    // 配置引脚:PD3为输入(开启内部上拉),PD2为输出,PD0(RX)保持输入
    DDRD = 0b00000100;
    PORTD |= (1 << PD3);

    USART_Init(); // 提前初始化USART

    // 配置INT1为下降沿触发(适配上拉电阻的按键逻辑)
    EICRA |= (1 << ISC11);
    EICRA &= ~(1 << ISC10);
    EIMSK |= (1 << INT1); // 使能INT1中断
    
    sei(); // 开启全局中断

    while (1)
    {
        // 主循环处理GPS读取,避免中断阻塞
        if(read_gps_flag) {
            readgps();
        }
    }
}

ISR(INT1_vect)
{
    // 软件消抖,确认按键真实按下
    _delay_ms(10);
    if (!(PIND & (1 << PD3))) {
        read_gps_flag = 1;
    }
} 

内容的提问来源于stack exchange,提问作者Jane Malibiran

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 08:30:49