Embedded – Timer and External Interrupt Simulated UART

The following are the general steps and example code for simulating UART (Universal Asynchronous Receiver-Transmitter) communication using timers and external interrupts (taking STM32 as an example, using C language). This method can achieve basic UART communication functionality through software simulation without hardware UART peripherals or when additional UART features are required.

#include “stm32f10x.h”

// Define pins

#define UART_TX_PIN GPIO_Pin_9

#define UART_RX_PIN GPIO_Pin_10

#define UART_PORT GPIOB

// Define baud rate related parameters

#define BAUDRATE 9600

#define SYSTEM_CLOCK 72000000 // Assuming the system clock is 72MHz

#define BIT_TIME (SYSTEM_CLOCK / BAUDRATE) // Transmission time for one data bit

// Define receive buffer and related variables

uint8_t receive_buffer[100];

uint8_t receive_index = 0;

uint8_t receive_complete = 0;

// Timer initialization function

void TIM3_Init(void) {

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

NVIC_InitTypeDef NVIC_InitStructure;

// Enable timer3 clock

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

// Basic configuration of timer 3

TIM_TimeBaseStructure.TIM_Period = BIT_TIME – 1; // Set counting period

TIM_TimeBaseStructure.TIM_Prescaler = 0; // Prescaler is 0

TIM_TimeBaseStructure.TIM_ClockDivision = 0;

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

// Enable timer3 interrupt

TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);

// Configure interrupt priority

NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

// Start timer 3

TIM_Cmd(TIM3, ENABLE);

}

// External interrupt initialization function

void EXTI_Init(void) {

GPIO_InitTypeDef GPIO_InitStructure;

EXTI_InitTypeDef EXTI_InitStructure;

NVIC_InitTypeDef NVIC_InitStructure;

// Enable GPIO clock

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

// Configure RX pin as floating input

GPIO_InitStructure.GPIO_Pin = UART_RX_PIN;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;

GPIO_Init(UART_PORT, &GPIO_InitStructure);

// Configure EXTI line

GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource10);

// Configure EXTI interrupt

EXTI_InitStructure.EXTI_Line = EXTI_Line10;

EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;

EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // Falling edge trigger

EXTI_InitStructure.EXTI_LineCmd = ENABLE;

EXTI_Init(&EXTI_InitStructure);

// Configure interrupt priority

NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

}

// Send a byte of data

void UART_SendByte(uint8_t data) {

// Send start bit

GPIO_ResetBits(UART_PORT, UART_TX_PIN);

for (int i = 0; i < BIT_TIME; i++) {} // Delay for one bit time

// Send data bits

for (int i = 0; i < 8; i++) {

if (data & (1 << i)) {

GPIO_SetBits(UART_PORT, UART_TX_PIN);

} else {

GPIO_ResetBits(UART_PORT, UART_TX_PIN);

}

for (int j = 0; j < BIT_TIME; j++) {} // Delay for one bit time

}

// Send stop bit

GPIO_SetBits(UART_PORT, UART_TX_PIN);

for (int i = 0; i < BIT_TIME; i++) {} // Delay for one bit time

}

// Timer 3 interrupt handler

void TIM3_IRQHandler(void) {

if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) {

// Here you can add logic for processing bits when receiving data, such as shifting storage, etc.

TIM_ClearITPendingBit(TIM3, TIM_IT_Update);

}

}

// External interrupt 15-10 interrupt handler

void EXTI15_10_IRQHandler(void) {

if (EXTI_GetITStatus(EXTI_Line10) != RESET) {

// Start receiving data, enable timer

TIM_SetCounter(TIM3, 0);

TIM_Cmd(TIM3, ENABLE);

// Here add logic for receiving data, gradually reading data bits in the timer interrupt and storing them in the buffer

// After receiving a byte, process the data and store it in the buffer

EXTI_ClearITPendingBit(EXTI_Line10);

}

}

int main(void) {

// Initialize GPIO

GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

// Configure TX pin as push-pull output

GPIO_InitStructure.GPIO_Pin = UART_TX_PIN;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(UART_PORT, &GPIO_InitStructure);

// Initialize timer and external interrupt

TIM3_Init();

EXTI_Init();

while (1) {

// Main loop can handle other tasks, such as checking the receive complete flag and processing received data

if (receive_complete) {

// Process the data in the receive buffer

receive_complete = 0;

receive_index = 0;

}

}

}

The above code implements basic UART simulation functionality, including the initialization of timers and external interrupts, data sending, and the framework for receiving. In practical applications, further refinement of the data receiving logic is needed, including accurately reading data bits, handling parity bits (if any), and determining when reception is complete. Additionally, the code may need to be adjusted according to different microcontroller platforms.

Leave a Comment