STM32F1C8-UART-9600BAUD-1S-软件模拟

在用滴答定时器的延时模拟串口通信时序是发现老是接收错误,也不能说是全错,就是断断续续错误,加了校验还是会出错。然后去网上检索以下,发现了采用定时器溢出中断形式来做数据接收,我觉得很不错。把两部分代码整合了一下,放在这里

原文地址:STM32模拟串口(UART)使用-电子发烧友网

 实测是没有什么大问题,反正是比我原来用延时一位一位的发送强。

 main函数代码

#include "stm32f10x.h"
#include "Delay.h"
// 定义串口参数
#define BAUD_RATE 9600
#define START_BIT_DURATION (1.0 / BAUD_RATE)
#define BIT_DURATION (1.0 / BAUD_RATE)

// 定义发送和接收的 GPIO 口
#define TX_PIN GPIO_Pin_9 // PA9
#define RX_PIN GPIO_Pin_10 // PA10

// 定义 BIT_DURATION 的微秒表示
#define BIT_DURATION_US (int)(BIT_DURATION * 1000000)
	
#define Recive2_Byte 10//接收缓冲器的个数
u8 len2=0;//接收计数
u8 USART2_buf[Recive2_Byte];//接收缓冲区

enum{
COM_START_BIT,
COM_D0_BIT,
COM_D1_BIT,
COM_D2_BIT,
COM_D3_BIT,
COM_D4_BIT,
COM_D5_BIT,
COM_D6_BIT,
COM_D7_BIT,
COM_STOP_BIT,
};

u8 recvStat2= COM_STOP_BIT;
u8 recvData2=0;
uint8_t receivedByte = 0;
void SystemclockInit()
{
	// 启动外部时钟
    RCC->CR |= RCC_CR_HSEON;
    while (!(RCC->CR & RCC_CR_HSERDY));

    // 配置PLL
    RCC->CFGR &= ~RCC_CFGR_PLLSRC; // 外部时钟作为PLL输入
    RCC->CFGR |= RCC_CFGR_PLLMULL9; // PLL倍频因子,得到72M主频
    RCC->CFGR |= RCC_CFGR_PPRE1_DIV2; // APB1分频,可根据实际情况调整

    // 启动PLL
    RCC->CR |= RCC_CR_PLLON;
    while (!(RCC->CR & RCC_CR_PLLRDY));

    // 设置系统时钟为PLL
    RCC->CFGR &= ~RCC_CFGR_SW;
    RCC->CFGR |= RCC_CFGR_SW_PLL;
    while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);

    // 调整FLASH等待周期
    FLASH->ACR |= FLASH_ACR_LATENCY_2; // 根据主频调整,可能需要其他设置	
	
}
void GPIOxInit() {
	
    GPIO_InitTypeDef GPIO_InitStruct;
	EXTI_InitTypeDef EXTI_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
	
    // 使能 GPIOA 时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);

    // 配置 TX_PIN 为推挽输出
    GPIO_InitStruct.GPIO_Pin = TX_PIN;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
	GPIO_SetBits(GPIOA,GPIO_Pin_9);//TXD默认电平拉高

    // 配置 RX_PIN 为上拉输入
    GPIO_InitStruct.GPIO_Pin = RX_PIN;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	 // 配置外部中断线
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource10);

    // 配置外部中断
    EXTI_InitStructure.EXTI_Line = EXTI_Line10;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // 外部中断触发方式为下降沿
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);

    // 配置中断优先级
    NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}



void sendBit(uint8_t bit) {
    // 模拟发送位的时序
    GPIOA->BSRR = bit ? TX_PIN : (TX_PIN << 16);

    // 精确延时
    Delay_us(BIT_DURATION_US);
}
void sendByte(uint8_t byte) {
    // 发送起始位
    sendBit(0);

    // 发送数据位
    for (int i = 0; i < 8; ++i) {
        sendBit((byte >> i) & 1);
    }

    // 发送停止位
    sendBit(1);

}
void USART2_Send(u8* buf,u8 len2)
{
	u8 t;
	for(t=0;t< len2; t++)
	{
		sendByte(buf[t]);
	}
}

void TIM2_Int_Init(u16 arr,u16 psc)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//时钟使能

	//定时器TIM2初始化
	TIM_TimeBaseStructure.TIM_Period=arr;//设置在下一个更新事件装入活动的自动重装载寄存器周期的值
	TIM_TimeBaseStructure.TIM_Prescaler=psc;//设置用来作为TIMx时钟频率除数的预分频值
	TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;//设置时钟分割:TDTS=Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;//TIM向上计数模式
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);//根据指定的参数初始化TIMx的时间基数单位
	
	TIM_ClearITPendingBit(TIM2,TIM_FLAG_Update);
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//使能指定的TIM5中断,允许更新中断

	//中断优先级NVIC设置
	NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;//TIM2中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//先占优先级1级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;//从优先级1级
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);//初始化NVIC寄存器
}

int main(void) {
    //初始化时钟源
	SystemclockInit();
    // 初始化 GPIO
    GPIOxInit();
	//配置定时器2,溢出时间104us,对应9600 baud
	TIM2_Int_Init(104-1,72-1);
	 // 配置中断优先级分组
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 这里选择中断分组为2

   // 示例:一帧一帧发送数据
	uint8_t buff[]={0x01,0x65,0x66,0x67,0x42,0x41};
	USART2_Send(buff,sizeof(buff));

	
    while (1) {
	
    }
}

void EXTI15_10_IRQHandler(void) {
    if (EXTI_GetITStatus(EXTI_Line10) != RESET) {
        // 在这里添加你需要执行的中断处理代码,每bit定时器溢出一次,用停止位和起始位来判断是否启动定时器
		//如果是 EXTI_Line10 引发的中断,检查 PA10 引脚是否为低电平,如果是则表示检测到串口数据的起始位(START_BIT):
			if((GPIOA->IDR & GPIO_Pin_10 )==0)
		{
			if(recvStat2==COM_STOP_BIT)//如果当前状态 recvStat2 为停止位 COM_STOP_BIT,则将状态置为开始位 COM_START_BIT,并启动定时器:
			{
				recvStat2=COM_START_BIT;//将当前的状态设置为开始位
				TIM_Cmd(TIM2,ENABLE);//
			}
		}
        EXTI_ClearITPendingBit(EXTI_Line10); // 清除中断标志
    }
}
void TIM2_IRQHandler(void)
{  
    if(TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) != RESET)//1bit数据接收完毕,定时器溢出
    {
        TIM_ClearITPendingBit(TIM2, TIM_FLAG_Update); 
        recvStat2++;//RX状态变成数据位
        if(recvStat2 == COM_STOP_BIT)//顺延到停止位,表示1帧接收完毕
        {
            TIM_Cmd(TIM2, DISABLE);//关闭计数器
            USART2_buf[len2++] = recvData2;//把数据存入BUFF
			if(len2 >Recive2_Byte-1)//数据最多存入Recive2_Byte,多出的覆盖
			{
				len2=0;
				USART2_Send(USART2_buf,Recive2_Byte);//接收完毕的数据发出
			}
				return;//退出中断,等待下一次的起始位触发EXTI10
			}
			if((GPIOA->IDR & GPIO_Pin_10 ))//读取RX电平
			{
				recvData2|=(1<< (recvStat2 - 1));
			}else{
				recvData2 &= ~(1 << (recvStat2 - 1));
		} 
	}  
}

延时函数的代码

#include "stm32f10x.h"


/**
  * @brief  微秒级延时
  * @param  xus 延时时长,范围:0~233015
  * @retval 无
  */
void Delay_us(uint32_t xus)
{
	SysTick->LOAD = 72 * xus - 1;				//设置定时器重装值
	SysTick->VAL = 0x00;					//清空当前计数值
	SysTick->CTRL = 0x00000005;				//设置时钟源为HCLK,启动定时器
	while(!(SysTick->CTRL & 0x00010000));	//等待计数到0
	SysTick->CTRL = 0x00000004;				//关闭定时器
}

/**
  * @brief  毫秒级延时
  * @param  xms 延时时长,范围:0~4294967295
  * @retval 无
  */
void Delay_ms(uint32_t xms)
{
	while(xms--)
	{
		Delay_us(1000);
	}
}
 
/**
  * @brief  秒级延时
  * @param  xs 延时时长,范围:0~4294967295
  * @retval 无
  */
void Delay_s(uint32_t xs)
{
	while(xs--)
	{
		Delay_ms(1000);
	}
} 
#ifndef __DELAY_H
#define __DELAY_H

void Delay_us(uint32_t us);
void Delay_ms(uint32_t ms);
void Delay_s(uint32_t s);

#endif

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值