Hardware components | ||||||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
![]() |
| × | 4 | |||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
![]() |
| × | 1 |
INTRODUCTION
This project implements an autonomous obstacle-avoidance robot using the MaaxBoard OSM93 with an NXP i.MX93 Cortex-M33 MCU. The robot uses an ultrasonic sensor for real-time distance measurement and controls DC motors via a motor driver. When an obstacle is detected within a predefined range, the robot stops, reverses, and turns to find a clear path.
Features- Real-time obstacle detection using ultrasonic sensing.
- Autonomous navigation with decision-making logic in firmware.
- Motor control using GPIO & PWM via NXP SDK drivers.
- Optimized for low-latency response.
- Designed for embedded learning & prototyping.
The firmware was written in C using MCUXpresso for VS Code, leveraging the NXP SDK. Key peripherals configured:
- GPIO for trigger and echo pins
- TPM (Timer/PWM Module) for motor control
- UART for debug messages
- Delay functions for sensor timing
The main loop continuously measures distance, compares it to a threshold, and calls movement functions (FORWARD()
, BACKWARD()
, LEFT()
, RIGHT()
, HALT()
).
This project demonstrates that low-power microcontrollers like the i.MX93 Cortex-M33 can handle responsive, real-time robotic navigation. With modular design and open-source code, it’s a great platform for experimenting with autonomous movement and sensor integration.
/*
* Copyright (c) 2015, Freescale Semiconductor, Inc.
* Copyright 2016-2021 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "fsl_debug_console.h"
#include "board.h"
#include "app.h"
#include "fsl_tpm.h"
#include "fsl_rgpio.h"
#include "fsl_lpuart.h"
#include "Navigation.h"
/*******************************************************************************
* Definitions
******************************************************************************/
/*******************************************************************************
* Prototypes
******************************************************************************/
void GPIO_SETUP(void);
void FORWARD(void);
void BACKWARD(void);
void TURN_RIGHT(void);
void TURN_LEFT(void);
void HALT(void);
void OBJECT_AVOID(void);
/*******************************************************************************
* Variables
******************************************************************************/
volatile bool tpmIsrFlag = false;
volatile uint32_t risingTime = 0;
volatile uint32_t fallingTime = 0;
volatile bool gotRisingEdge = false;
/*******************************************************************************
* Code
******************************************************************************/
/*!
* @brief Init_LPUART function
*/
void Init_LPUART(void)
{
lpuart_config_t config;
/* Get default UART config (8N1, no parity, no flow control) */
LPUART_GetDefaultConfig(&config);
/* Modify parameters as needed */
config.baudRate_Bps = 115200U;
config.enableTx = true;
config.enableRx = true;
/* Initialize the UART with the specified parameters */
LPUART_Init(BOARD_LPUART, &config, BOARD_LPUART_CLK_FREQ);
}
/*!
* @brief CalculateDistance function
*/
float CalculateDistance(void)
{
uint32_t ticks;
if (fallingTime >= risingTime)
ticks = fallingTime - risingTime;
else
ticks = (TPM_MAX_COUNTER_VALUE(DEMO_TPM_BASEADDR) - risingTime) + fallingTime; /* Overflow handled */
float tpmClk = CLOCK_GetIpFreq(LPTPM_CLOCK_ROOT); /* You may adjust to TPM clock */
float timeUs = ((ticks * 1.0f) / tpmClk) * 1e6;
PRINTF("TPM Clock : %.2f\r\n",tpmClk);
PRINTF("TIME IN US FROM ECHO : %.2f\r\n",timeUs);
float distanceCm = timeUs / 58.0f; /* Convert Micro second into centimeter(distance) */
return distanceCm;
}
/*!
* @brief SetUp function
*/
void SetUp(void)
{
rgpio_pin_config_t out_config ={
kRGPIO_DigitalOutput,
0,
};
/* Trigger pin */
RGPIO_PinInit(TRIG_RGPIO,TRIG_PIN,&out_config);
RGPIO_PinWrite(TRIG_RGPIO,TRIG_PIN,0);
RGPIO_PinInit(MOTOR_RGPIO, A1, &out_config);
RGPIO_PinInit(MOTOR_RGPIO, A2, &out_config);
RGPIO_PinInit(MOTOR_RGPIO, B1, &out_config);
RGPIO_PinInit(MOTOR_RGPIO, B2, &out_config);
}
/*!
* @brief SendTrigPulse function
*/
void SendTrigPulse(void)
{
RGPIO_PinWrite(TRIG_RGPIO, TRIG_PIN, 0);
SDK_DelayAtLeastUs(2, SystemCoreClock);
RGPIO_PinWrite(TRIG_RGPIO, TRIG_PIN, 1);
SDK_DelayAtLeastUs(10, SystemCoreClock);
RGPIO_PinWrite(TRIG_RGPIO, TRIG_PIN, 0);
}
/*!
* @brief TPM_INPUT_CAPTURE_HANDLER function
*/
void TPM_INPUT_CAPTURE_HANDLER(void)
{
uint32_t status = TPM_GetStatusFlags(DEMO_TPM_BASEADDR);
if (status & TPM_CHANNEL_FLAG)
{
uint32_t capturedValue = TPM_GetChannelValue(DEMO_TPM_BASEADDR, BOARD_TPM_INPUT_CAPTURE_CHANNEL);
if (!gotRisingEdge)
{
risingTime = capturedValue;
gotRisingEdge = true;
/* Switch to falling edge detection */
TPM_SetupInputCapture(DEMO_TPM_BASEADDR,BOARD_TPM_INPUT_CAPTURE_CHANNEL,kTPM_FallingEdge);
}
else
{
fallingTime = capturedValue;
tpmIsrFlag = true;
gotRisingEdge = false;
/* Switch back to rising edge detection for next measurement */
TPM_SetupInputCapture(DEMO_TPM_BASEADDR,BOARD_TPM_INPUT_CAPTURE_CHANNEL,kTPM_RisingEdge);
}
/* Clear interrupt flag for this channel */
TPM_ClearStatusFlags(DEMO_TPM_BASEADDR, TPM_CHANNEL_FLAG);
}
SDK_ISR_EXIT_BARRIER;
}
/*!
* @brief OBJECT_AVOID function
*/
void OBJECT_AVOID(void)
{
float distance = CalculateDistance();
PRINTF("Distance: %.2f cm\r\n", distance);
if (distance >= 20)
{
FORWARD();
}
else if (distance <= 19)
{
HALT();
SDK_DelayAtLeastUs(500000,SystemCoreClock);
BACKWARD();
SDK_DelayAtLeastUs(500000,SystemCoreClock);
HALT();
SDK_DelayAtLeastUs(500000,SystemCoreClock);
TURN_RIGHT();
SDK_DelayAtLeastUs(500000,SystemCoreClock);
}
}
/*!
* @brief Main function
*/
int main(void)
{
tpm_config_t tpmInfo;
SetUp(); /* Set up the GPIO */
/* Board pin, clock, debug console init */
BOARD_InitHardware();
// Init_LPUART(); /* Initialise one more UART */
/* Print a note to terminal */
PRINTF("\r\nUltrasonic sensor demo\r\n");
PRINTF("\n");
TPM_GetDefaultConfig(&tpmInfo);
/* Initialize TPM module */
TPM_Init(DEMO_TPM_BASEADDR, &tpmInfo);
/* Setup input capture on a TPM channel */
TPM_SetupInputCapture(DEMO_TPM_BASEADDR, BOARD_TPM_INPUT_CAPTURE_CHANNEL, kTPM_RisingEdge);
/* Set the timer to be in free-running mode */
TPM_SetTimerPeriod(DEMO_TPM_BASEADDR, TPM_MAX_COUNTER_VALUE(DEMO_TPM_BASEADDR));
/* Enable channel interrupt when the second edge is detected */
TPM_EnableInterrupts(DEMO_TPM_BASEADDR, TPM_CHANNEL_INTERRUPT_ENABLE);
/* Enable at the NVIC */
EnableIRQ(TPM_INTERRUPT_NUMBER);
TPM_StartTimer(DEMO_TPM_BASEADDR, kTPM_SystemClock);
while (1)
{
tpmIsrFlag = false;
SendTrigPulse(); /* Send 10us pulse to the sensor to start measuring */
while (tpmIsrFlag != true) /* When both edge captured flag is true */
{
}
OBJECT_AVOID(); /* Oject detection based on the ultrasonic sensor */
// SDK_DelayAtLeastUs(600000,SystemCoreClock);
}
}
/*
* Copyright (c) 2015, Freescale Semiconductor, Inc.
* Copyright 2016-2017 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "board.h"
#include "fsl_debug_console.h"
#include "fsl_rgpio.h"
#include "app.h"
#include "navigation.h"
/*******************************************************************************
* Definitions
******************************************************************************/
/*******************************************************************************
* Prototypes
******************************************************************************/
/*******************************************************************************
* Variables
******************************************************************************/
/*******************************************************************************
* Code
******************************************************************************/
/*!
* @brief GPIO_SETUP function
*/
void GPIO_SETUP(void)
{
rgpio_pin_config_t out_config = {
kRGPIO_DigitalOutput,
0,
};
// rgpio_pin_config_t inp_config = {
// kRGPIO_DigitalInput,
// 0,
// };
/* Trigger pin */
RGPIO_PinInit(TRIG_RGPIO,TRIG_PIN,&out_config);
RGPIO_PinWrite(TRIG_RGPIO,TRIG_PIN,0);
RGPIO_PinInit(MOTOR_RGPIO, A1, &out_config);
RGPIO_PinInit(MOTOR_RGPIO, A2, &out_config);
RGPIO_PinInit(MOTOR_RGPIO, B1, &out_config);
RGPIO_PinInit(MOTOR_RGPIO, B2, &out_config);
// RGPIO_PinInit(BOARD_RGPIO, S1, &inp_config);
// RGPIO_PinInit(BOARD_RGPIO, S2, &inp_config);
// RGPIO_PinInit(BOARD_RGPIO, S3, &inp_config);
// RGPIO_PinInit(BOARD_RGPIO, S4, &inp_config);
// RGPIO_PinInit(BOARD_RGPIO, S5, &inp_config);
}
/*!
* @brief FORWARD function
*/
void FORWARD(void)
{
RGPIO_PinWrite(MOTOR_RGPIO, A1, 1);
RGPIO_PinWrite(MOTOR_RGPIO, A2, 0);
RGPIO_PinWrite(MOTOR_RGPIO, B1, 1);
RGPIO_PinWrite(MOTOR_RGPIO, B2, 0);
}
/*!
* @brief backward function
*/
void BACKWARD(void)
{
RGPIO_PinWrite(MOTOR_RGPIO, A1, 0);
RGPIO_PinWrite(MOTOR_RGPIO, A2, 1);
RGPIO_PinWrite(MOTOR_RGPIO, B1, 0);
RGPIO_PinWrite(MOTOR_RGPIO, B2, 1);
}
/*!
* @brief TURN_RIGHT function
*/
void TURN_RIGHT(void)
{
RGPIO_PinWrite(MOTOR_RGPIO, A1, 0);
RGPIO_PinWrite(MOTOR_RGPIO, A2, 1);
RGPIO_PinWrite(MOTOR_RGPIO, B1, 1);
RGPIO_PinWrite(MOTOR_RGPIO, B2, 0);
}
/*!
* @brief TURN_LEFT function
*/
void TURN_LEFT(void)
{
RGPIO_PinWrite(MOTOR_RGPIO, A1, 1);
RGPIO_PinWrite(MOTOR_RGPIO, A2, 0);
RGPIO_PinWrite(MOTOR_RGPIO, B1, 0);
RGPIO_PinWrite(MOTOR_RGPIO, B2, 1);
}
/*!
* @brief HALT function
*/
void HALT(void)
{
RGPIO_PinWrite(MOTOR_RGPIO, A1, 0);
RGPIO_PinWrite(MOTOR_RGPIO, A2, 0);
RGPIO_PinWrite(MOTOR_RGPIO, B1, 0);
RGPIO_PinWrite(MOTOR_RGPIO, B2, 0);
}
Comments