STM32F103ZE单片机只用一个定时器溢出中断就实现GPIO模拟串口收发数据,不用外部中断和定时器输入捕获中断
程序员文章站
2022-03-06 08:01:56
...
本文使用的单片机为STM32F103ZE,使用的单片机引脚为PA9(发送)和PA10(接收)。晶振为8MHz,程序采用HAL库编写。
程序下载地址:https://pan.baidu.com/s/13DEWdpupCG3REGTTAJYuVw
程序功能:一开始先通过串口输出两行字符,然后开始接收串口字符。每收到10个字符,就打印一次收到的内容,然后继续接收。程序能在有外部晶振或无外部晶振条件下运行,模拟出的波特率为9600或38400的串口经测试没有问题(但是115200波特率不行,速度太快反应不过来导致乱码)。由于只用到了一个定时器溢出中断,所以理论上可以模拟出无数个串口。
程序收到无法显示的控制字符,会以十六进制格式显示出来。
程序代码:
#include <ctype.h>
#include <stdio.h>
#include <stm32f1xx.h>
#define DEBUG 0
#define USE_HSI 0
#define MYUART_TX_0 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_RESET)
#define MYUART_TX_1 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_SET)
#define MYUART_RX (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_10) == GPIO_PIN_SET)
TIM_HandleTypeDef htim2;
static uint8_t myuart_txbuf, myuart_txe = 1;
static uint8_t myuart_rxbuf, myuart_rxne;
#if DEBUG
#define DEBUG_SIZE 30
static uint8_t debug_buffer[DEBUG_SIZE][9]; // 记录采样结果 (起始位+8位数据位), 以便调试
static int debug_pos;
#endif
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
while (1);
}
#endif
static void clock_init(void)
{
HAL_StatusTypeDef status;
RCC_ClkInitTypeDef clk = {0};
RCC_OscInitTypeDef osc = {0};
// 启动外部8MHz晶振, 并倍频到72MHz
osc.OscillatorType = RCC_OSCILLATORTYPE_HSE;
osc.HSEState = RCC_HSE_ON;
osc.PLL.PLLMUL = RCC_PLL_MUL9;
osc.PLL.PLLSource = RCC_PLLSOURCE_HSE;
osc.PLL.PLLState = RCC_PLL_ON;
#if USE_HSI
status = HAL_ERROR;
#else
status = HAL_RCC_OscConfig(&osc);
#endif
// 如果外部晶振启动失败, 则使用内部晶振, 并倍频到64MHz
if (status != HAL_OK)
{
osc.HSEState = RCC_HSE_OFF;
osc.PLL.PLLMUL = RCC_PLL_MUL16;
osc.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2;
HAL_RCC_OscConfig(&osc);
}
// 配置外设总线分频系数
__HAL_RCC_ADC_CONFIG(RCC_ADCPCLK2_DIV6);
clk.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
clk.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
clk.AHBCLKDivider = RCC_SYSCLK_DIV1;
clk.APB1CLKDivider = RCC_HCLK_DIV2;
clk.APB2CLKDivider = RCC_HCLK_DIV1;
HAL_RCC_ClockConfig(&clk, FLASH_LATENCY_2);
}
static void myuart_init(int baud_rate)
{
GPIO_InitTypeDef gpio = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_TIM2_CLK_ENABLE();
MYUART_TX_1;
gpio.Mode = GPIO_MODE_OUTPUT_PP;
gpio.Pin = GPIO_PIN_9;
gpio.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &gpio);
htim2.Instance = TIM2;
htim2.Init.Period = SystemCoreClock / baud_rate / 3 - 1;
HAL_TIM_Base_Init(&htim2);
HAL_NVIC_EnableIRQ(TIM2_IRQn);
HAL_TIM_Base_Start_IT(&htim2);
}
static int myuart_recv(uint8_t *data)
{
if (myuart_rxne)
{
*data = myuart_rxbuf;
myuart_rxne = 0;
return 1;
}
return 0;
}
static void myuart_recv_process(void)
{
static int8_t i, j;
static uint8_t bits, data;
uint8_t bit;
bit = MYUART_RX; // 采样
// i=0: 起始位; i=1~8: 数据位; i=9: 停止位
// j=0~2: 每位采样三次, bit为当前采样值
if (i == 0 && j == 0 && bit)
return; // 未收到起始位
else if (i == 9)
{
// 检测停止位
if (bit)
{
// 一检测到停止位, 就立即停止接收, 开始检测下一个起始位
i = 0;
#if DEBUG
if (debug_pos != DEBUG_SIZE)
debug_pos++;
#endif
if (!myuart_rxne)
{
myuart_rxbuf = data;
myuart_rxne = 1;
}
}
return;
}
bits = (bits << 1) | bit; // 将采样结果依次记录到bits变量的低三位中
j++;
if (j == 3)
{
// 三次采样完毕
j = 0;
// 根据三次采样结果, 确定数据的第i位是什么
if (bits == 3 || bits >= 5)
bit = 1; // 如果采到1的次数比0多, 则认为该位为1
else
bit = 0;
data = (data >> 1) | (bit << 7);
#if DEBUG
if (debug_pos != DEBUG_SIZE)
debug_buffer[debug_pos][i] = bits;
#endif
bits = 0;
i++;
}
}
static void myuart_send(uint8_t data)
{
while (!myuart_txe);
myuart_txbuf = data;
myuart_txe = 0;
}
static void myuart_send_process(void)
{
static uint8_t i;
static uint16_t bits;
if (i == 0)
{
if (myuart_txe)
return;
bits = (myuart_txbuf << 1) | 0x200;
}
if (i % 3 == 0)
{
if (bits & 1)
MYUART_TX_1;
else
MYUART_TX_0;
bits >>= 1;
}
i++;
if (i == 30)
{
myuart_txe = 1;
i = 0;
}
}
int main(void)
{
int err, ret;
int i;
uint8_t data[10];
#if DEBUG
int j;
#endif
HAL_Init();
clock_init();
myuart_init(38400);
printf("STM32F103ZE UART\n");
printf("SystemCoreClock=%u\n", SystemCoreClock);
while (1)
{
// 接收串口字符, 直到填满data数组
for (i = 0; i < sizeof(data); i++)
{
do
{
ret = myuart_recv(&data[i]);
} while (!ret);
}
// 打印出每个字符
err = 0;
for (i = 0; i < sizeof(data); i++)
{
if (isprint(data[i]))
printf("%c", data[i]);
else
{
printf("{%#x}", data[i]);
err++;
}
}
printf("\n");
// 如果收到了非打印字符, 则打印出调试信息
#if DEBUG
if (err)
{
for (i = 0; i < debug_pos; i++)
{
for (j = 0; j < 10; j++)
printf("%d%d%d ", (debug_buffer[i][j] >> 2) & 1, (debug_buffer[i][j] >> 1) & 1, debug_buffer[i][j] & 1);
printf("\n");
}
}
debug_pos = 0;
#endif
}
}
void TIM2_IRQHandler(void)
{
HAL_TIM_IRQHandler(&htim2);
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim == &htim2)
{
// 这里理论上可以放置很多对串口收发函数
// 用一个定时器就能模拟出很多个相同波特率的串口
myuart_recv_process();
myuart_send_process();
}
}
void HardFault_Handler(void)
{
while (1);
}
void SysTick_Handler(void)
{
HAL_IncTick();
}
/* 串口绑定printf (不需要勾选Use MicroLIB) */
#pragma import(__use_no_semihosting)
struct __FILE
{
int handle;
} __stdout, __stderr;
int fputc(int ch, FILE *fp)
{
if (fp == stdout || fp == stderr)
{
if (ch == '\n')
myuart_send('\r');
myuart_send(ch);
}
return ch;
}
void _sys_exit(int returncode)
{
while (1);
}