如何使用atmega32使多个超声波传感器同时工作

时间:2018-02-07 12:08:30

标签: c sensor avr

我正在研究一个自动停车的汽车机器人,我正在使用8个(hc-sr04)超声波传感器(每侧2个),但问题是我使用的是atmega32,资源有限只有3个外部中断和3个定时器(即使使用中断以某种方式工作,我可能会遇到同时触发两个中断的风险)。

我正在使用此传感器:http://ram-e-shop.com/oscmax/catalog/product_info.php?products_id=907

我尝试过使用带有轮询程序的数字I / O引脚,但它没有用。 这是轮询程序的代码:

unsigned int read_sonar(){
int dist_in_cm = 0;
init_sonar();                       // Setup pins and ports
trigger_sonar();                    // send a 10us high pulse


while(!(ECHO_PIN & (1<<ECHO_BIT))){   // while echo pin is still low
    USART_Message("echo pin low\r\n");
    trig_counter++;
     uint32_t max_response_time = SONAR_TIMEOUT;
    if (trig_counter > max_response_time){   // SONAR_TIMEOUT
        return TRIG_ERROR;
    }
}

TCNT1=0;                            // reset timer
TCCR1B |= (1<<CS10);              // start 16 bit timer with no prescaler
TIMSK |= (1<<TOIE1);             // enable overflow interrupt on timer1
overFlowCounter=0;                  // reset overflow counter
sei();                              // enable global interrupts

while((ECHO_PIN & (1<<ECHO_BIT))){    // while echo pin is still high
    USART_Message("echo pin high\r\n");
    if (((overFlowCounter*TIMER_MAX)+TCNT1) > SONAR_TIMEOUT){
        USART_Message("timeout");
        return ECHO_ERROR;          // No echo within sonar range

    }
};

TCCR1B = 0x00;                      // stop 16 bit timer with no prescaler
cli();                              // disable global interrupts
no_of_ticks = ((overFlowCounter*TIMER_MAX)+TCNT1);  // counter count

dist_in_cm = (no_of_ticks/(CONVERT_TO_CM*CYCLES_PER_US));   // distance in cm
return (dist_in_cm );}

如果我想同时读取所有传感器,这种方法不起作用,因为它会在循环中卡住一段时间。

我还尝试使用freeRTOS构建一个任务,检查每1毫秒的引脚状态,但这不会是准确的时间。

任何帮助?

1 个答案:

答案 0 :(得分:2)

假设您使用8MHz的内部时钟,我会尝试处理这个内部定时器溢出中断,并使用整个端口连接传感器。

  1. 在正常模式或CTC模式下使用Timer(我觉得非常直观)以确保定期中断。设置适当的时间段。请记住,时钟频率非常低,所以不要夸大(我认为0.25毫秒适合)。

  2. 将传感器连接到一个端口,例如PORTB。这是一个很好的情况,因为ATmega32有4个端口,引脚编号为0-7,你使用8个传感器,因此特定端口的寄存器可以覆盖所有引脚,你可以使用一个读取来获得所有引脚的状态。

  3. 实施逻辑:

    volatile uint8_t sensors_states;
    volatile uint8_t read_flag = 0;
    
    ISR(TIMER0_OVF_vect)
    {
        sensors_states = PORTB;
        read_flag = 1;
    }
    
    int main()
    {
        // Initialize peripherals ...
    
        // You must assume on your own how much time could the pin be held
        // in the same state. This is important because the number must not 
        // be bigger than max value for the type of the array
        uint8_t states_time[8] = {0, 0, 0, 0, 0, 0, 0, 0};
        uint8_t prev_sensors_states = PORTB;
        while(1)
        {
            // Wait until the flag will be set in the ISR
            if(read_flag)
            {
                for(uint8_t i = 0, mask = 0x80 ; i < 8 ; i ++, mask >>= 1)
                {
                    states_time[i]++;
                    // Compare the previous state and present state on each pin
                    uint8_t state = mask & sensors_states;
                    if((mask & prev_sensors_states) != state)
                    {
                        // Here you can use the state of the pin and the duration of that state.
                        // Remember that when 'state' is > 0 it means that previous state of the
                        // pin was '0' and if if 'state' is == 0 then the previous pin state
                        // was '1' (negation).
                        do_something_with_pin_change(states_time[i], state);
    
                        states_time[i] = 0;
                    }
                }
                // Save the previous states of the pins
                prev_sensors_states = sensors_states;
                // Clear the flag to await next data update
                read_flag = 0;
            }
        }
    }
    
  4. 如果您将尝试使用FreeRTOS您可以使用ulTaskNotifyTakevTaskNotifyGiveFromISR而不是使用read_flag来实现一个简单的机制,该机制将从中断通知任务端口已被阅读。处理器将进入空闲状态一段时间,然后您可以调用睡眠功能以最小化功耗。

    我不知道您想对这些数据做什么,所以我调用了do_something_with_pin_change函数来指示您可以使用数据的位置。

    总结这个解决方案你只能使用一个中断,当然还有8个引脚。