板载资源已经使用完,但是还需要一组额外的串口。只能使用IO口来自己写时序,模拟串口收发。
考虑过使用DMA的方式来进行接收/发送,但是已经没有硬件资源了。
单片机:STM32F103RET6
系统:RT-Thread
模拟收发的引脚:TX—PB14 RX—PB15
接收定时器:TIM6
发送定时器:TIM5
在裸机上已经测试过收发功能,很稳定,基本没有误码。
移植到RT-Thread下之后发现误码率较高。
最初是怀疑两个定时器的中断优先级比较低,被系统干扰导致,就全局搜索中断设置,把所有的(0,0)组都改为了(0,1),只保留我的两个定时器为(0,0)。测试波形发现还是进入定时器中断的时间不稳定,时快时慢。
下图是用示波器测试的中断进入状况,我是每次进入定时器中断就反转一个引脚的状态。
可以很清晰的看到进入中断的时间确实不是那么准时。
求教到底是什么原因导致了我的中断会延时进入?或者说如何把我的自己的中断优先级调到最高,不被系统干扰到?
附上我的代码。
/**
* @file emul.c
* @author roger (mrshaorong@gmail.com)
* @brief
* @version 0.1
* @date 2020-07-02
*
* @copyright Copyright (c) 2020
*
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
#include "stm32f1xx_hal.h"
#include "drv_usart.h"
#define BuadRate_9600
#ifdef BuadRate_9600
#define BUADRATE_PRESCALER 71
#define BUADRATE_PERIOD 103
#endif
#define RX_PIN GET_PIN(B, 15)
#define TX_PIN GET_PIN(B, 14)
#define TX_HIGH (GPIOB->BSRR = GPIO_PIN_14)
#define TX_LOW (GPIOB->BRR = GPIO_PIN_14)
#define OI_RXD rt_pin_read(RX_PIN)
#define RECV_BUF_MAX 0xFF
TIM_HandleTypeDef htim6;
TIM_HandleTypeDef htim5;
#define DBG_TAG "OPENMV"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
uint8_t SW_Usart_Recv_Len = 0;
uint64_t SW_Usart_Recv_Write = 0;
uint64_t SW_Usart_Recv_Read = 0;
uint8_t SW_Usart_Recv_Buf[RECV_BUF_MAX] = { 0 };
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,
};
uint8_t recvStat = COM_STOP_BIT;
uint8_t recvData = 0;
uint8_t SendBitBuf[10] = { 0 };
uint8_t SendPos = 0;
uint8_t SendOverFlag = 0;
/* 指向互斥量的指针 */
static rt_mutex_t sw_uart_write_mutex = RT_NULL;
void IO_TXD(uint8_t Data)
{
static uint16_t wait_send_times = 0;
rt_mutex_take(sw_uart_write_mutex, RT_WAITING_FOREVER);
TX_HIGH;
// SendBitBuf[0] = 0;
for (size_t i = 1; i < 9; i++) {
SendBitBuf[i] = (Data >> (i - 1)) & 0x01;
}
SendBitBuf[COM_STOP_BIT] = 1;
// 开始发送
SendOverFlag = 0;
SendPos = 1;
wait_send_times = 2400; // 500:208.9us;1000:417.3us;2000:843.08us
HAL_TIM_Base_Start_IT(&htim5);
TX_LOW;
// 最多等待9600波特率的发送1byte间隔
while (!SendOverFlag && wait_send_times--) {
}
rt_mutex_release(sw_uart_write_mutex);
}
void USART_Send(uint8_t *buf, uint8_t data_len)
{
for (uint8_t t = 0; t < data_len; t++) {
IO_TXD(buf[t]);
}
}
/**
* @brief TIM6 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM6_Init(void)
{
TIM_MasterConfigTypeDef sMasterConfig = { 0 };
htim6.Instance = TIM6;
htim6.Init.Prescaler = BUADRATE_PRESCALER;
htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
htim6.Init.Period = BUADRATE_PERIOD;
htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim6) != HAL_OK) {
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) !=
HAL_OK) {
Error_Handler();
}
HAL_NVIC_SetPriority(TIM6_IRQn, 0, 0);
}
/**
* @brief TIM5 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM5_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = { 0 };
TIM_MasterConfigTypeDef sMasterConfig = { 0 };
htim5.Instance = TIM5;
htim5.Init.Prescaler = BUADRATE_PRESCALER;
htim5.Init.CounterMode = TIM_COUNTERMODE_UP;
htim5.Init.Period = BUADRATE_PERIOD;
htim5.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim5.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim5) != HAL_OK) {
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim5, &sClockSourceConfig) != HAL_OK) {
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim5, &sMasterConfig) !=
HAL_OK) {
Error_Handler();
}
HAL_NVIC_SetPriority(TIM5_IRQn, 0, 0);
}
#define TEST_PIN GET_PIN(B, 12)
#define TEST_PIN1 GET_PIN(B, 13)
/**
* @brief This function handles TIM7 global interrupt.
*/
void TIM5_IRQHandler(void)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_13);
if (HAL_TIM_Base_GetState(&htim5) != HAL_TIM_STATE_RESET) {
__HAL_TIM_CLEAR_IT(&htim5, TIM_IT_UPDATE);
if (SendBitBuf[SendPos]) {
TX_HIGH;
} else {
TX_LOW;
}
SendPos++;
if (SendPos > COM_STOP_BIT) {
TX_HIGH;
SendOverFlag = 1;
HAL_TIM_Base_Stop_IT(&htim5);
return;
}
}
}
/**
* @brief This function handles TIM6 global interrupt.
*/
void TIM6_IRQHandler(void)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_12);
// rt_uint32_t level;
// level = rt_hw_interrupt_disable();
recvStat++;
if (HAL_TIM_Base_GetState(&htim6) != HAL_TIM_STATE_RESET) {
__HAL_TIM_CLEAR_IT(&htim6, TIM_IT_UPDATE);
if (recvStat == COM_STOP_BIT) {
HAL_TIM_Base_Stop_IT(&htim6);
rt_pin_irq_enable(RX_PIN, PIN_IRQ_ENABLE);
// EXTI->IMR |= GPIO_PIN_15;
if (SW_Usart_Recv_Len >= RECV_BUF_MAX) {
// LOG_D("SW_Usart_Recv_Len > %d", RECV_BUF_MAX);
return;
}
SW_Usart_Recv_Buf[(SW_Usart_Recv_Write++) % RECV_BUF_MAX] =
recvData;
SW_Usart_Recv_Len++;
// rt_hw_serial_isr(&uart_sw_obj.serial, RT_SERIAL_EVENT_RX_IND);
}
if (OI_RXD) {
recvData |= (1 << (recvStat - 1));
} else {
recvData &= ~(1 << (recvStat - 1));
}
}
}
// /**
// * @brief This function handles TIM6 global interrupt.
// */
// void TIM6_IRQHandler(void)
// {
// HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_12);
// // rt_uint32_t level;
// // level = rt_hw_interrupt_disable();
// if (HAL_TIM_Base_GetState(&htim6) != HAL_TIM_STATE_RESET) {
// __HAL_TIM_CLEAR_IT(&htim6, TIM_IT_UPDATE);
// if (recvStat == COM_START_BIT) {
// // __HAL_TIM_SET_AUTORELOAD(&htim6, BUADRATE_PERIOD);
// }
// // else if (recvStat == COM_STOP_BIT) {
// // rt_pin_irq_enable(RX_PIN, PIN_IRQ_ENABLE);
// // HAL_TIM_Base_Stop_IT(&htim6);
// // // EXTI->IMR |= GPIO_PIN_15;
// // if (SW_Usart_Recv_Len >= RECV_BUF_MAX) {
// // // LOG_D("SW_Usart_Recv_Len > %d", RECV_BUF_MAX);
// // return;
// // }
// // SW_Usart_Recv_Buf[(SW_Usart_Recv_Write++) % RECV_BUF_MAX] =
// // recvData;
// // SW_Usart_Recv_Len++;
// // // rt_hw_serial_isr(&uart_sw_obj.serial, RT_SERIAL_EVENT_RX_IND);
// // return;
// // }
// else {
// if (OI_RXD) {
// recvData |= (1 << (recvStat - 1));
// } else {
// recvData &= ~(1 << (recvStat - 1));
// }
// }
// recvStat++;
// if (recvStat == COM_STOP_BIT) {
// rt_pin_irq_enable(RX_PIN, PIN_IRQ_ENABLE);
// HAL_TIM_Base_Stop_IT(&htim6);
// // EXTI->IMR |= GPIO_PIN_15;
// if (SW_Usart_Recv_Len >= RECV_BUF_MAX) {
// // LOG_D("SW_Usart_Recv_Len > %d", RECV_BUF_MAX);
// return;
// }
// SW_Usart_Recv_Buf[(SW_Usart_Recv_Write++) % RECV_BUF_MAX] =
// recvData;
// SW_Usart_Recv_Len++;
// // rt_hw_serial_isr(&uart_sw_obj.serial, RT_SERIAL_EVENT_RX_IND);
// }
// }
// }
// void EXTI15_10_IRQHandler(void)
// {
// if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_15) != 0x00u) {
// __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_15);
// if (OI_RXD == 0) {
// if (recvStat == COM_STOP_BIT) {
// recvStat = COM_START_BIT;
// HAL_TIM_Base_Start_IT(&htim6);
// // EXTI->IMR &= ~GPIO_PIN_15;
// // rt_pin_irq_enable(RX_PIN, PIN_IRQ_DISABLE);
// }
// }
// }
// }
/* 中断回调函数 */
void beep_on(void *args)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_12);
if (OI_RXD == 0) {
if (recvStat == COM_STOP_BIT) {
recvStat = COM_START_BIT;
// __HAL_TIM_SET_AUTORELOAD(&htim6, BUADRATE_PERIOD / 2);
// __HAL_TIM_SET_COUNTER(&htim6, BUADRATE_PERIOD / 2);
HAL_TIM_Base_Start_IT(&htim6);
rt_pin_irq_enable(RX_PIN, PIN_IRQ_DISABLE);
}
}
}
void IOConfig(void)
{
rt_pin_mode(TX_PIN, PIN_MODE_OUTPUT);
rt_pin_mode(TEST_PIN, PIN_MODE_OUTPUT);
rt_pin_mode(TEST_PIN1, PIN_MODE_OUTPUT);
/* 按键0引脚为输入模式 */
rt_pin_mode(RX_PIN, PIN_MODE_INPUT_PULLUP);
/* 绑定中断,上升沿模式,回调函数名为beep_on */
rt_pin_attach_irq(RX_PIN, PIN_IRQ_MODE_FALLING, beep_on, RT_NULL);
/* 使能中断 */
rt_pin_irq_enable(RX_PIN, PIN_IRQ_ENABLE);
}
/**
* @brief
*
* @param param
*/
static void sw_uart_thread(void *param)
{
rt_err_t res;
TX_HIGH;
rt_thread_mdelay(500);
char ch = 0x00;
IO_TXD(ch);
while (1) {
rt_thread_mdelay(1);
if (SW_Usart_Recv_Len <= 10) {
continue;
}
while (SW_Usart_Recv_Len) {
SW_Usart_Recv_Len--;
ch = SW_Usart_Recv_Buf[(SW_Usart_Recv_Read++) % RECV_BUF_MAX];
// LOG_D("RECV = %02X", ch);
// ch++;
IO_TXD(ch);
rt_thread_mdelay(2);
}
}
}
static int sw_uart_init(void)
{
rt_thread_t tid;
// 硬件设备初始化
IOConfig();
MX_TIM6_Init();
MX_TIM5_Init();
/* 创建一个动态互斥量 */
sw_uart_write_mutex = rt_mutex_create("sw_uart_mutex", RT_IPC_FLAG_FIFO);
// tid = rt_thread_create("sw_uart", sw_uart_thread, RT_NULL, 1024,
// (RT_THREAD_PRIORITY_MAX / 3), 10);
tid = rt_thread_create("sw_uart", sw_uart_thread, RT_NULL, 1024,
(5), 10);
if (tid != RT_NULL)
rt_thread_startup(tid);
return RT_EOK;
}
INIT_APP_EXPORT(sw_uart_init);
操作系统会在处理临界区数据时关中断,单纯依靠在中断处理中收发数据是不行的,我以前使用定时器的输入捕获和比较输出功能做过模拟串口,9600速率是没问题的,看看你的收发引脚在不在定时器通道上。
提供一段红外口通信的驱动代码,是通过定时器模拟串口功能实现的,很久以前的代码了,使用的还是以前的stm32标准库,不过原理相同的,希望对你有所帮助.
//--------------------------------------------------------------------
//文件名 : IRComPort.c
//创建人 : 齐永忠
//创建日期: 2009.10.29
//--------------------------------------------------------------------
#include "IRComPort.h"
#include "Debug.h"
#include "QQueue.h"
//--------------------------------------------------------------------
#define IR_RX_PORT GPIOB
#define IR_TX_PORT GPIOB
#define IR_RX_PIN GPIO_Pin_1
#define IR_TX_PIN GPIO_Pin_0
#define IR_TX_NO 0 //红发发送脚对应脚号
#define IR_RX_TIMER TIM3//接收定时器定义
#define IR_TX_TIMER TIM2//发送定时器定义
#define IR_PWM_TIMER TIM3//PWM定时器定义
#define IR_RX_CHNNL TIM_IT_CC4//中断源
#define IR_TX_CHNNL TIM_IT_CC1//中断源
#define IR_PWM_CHNNL TIM_IT_CC3//中断源
#define IR_RX_CAP_CHNNL TIM_Channel_4//定时器信道,需与中断源一致
#define IR_PWM_CMD_CHNNL TIM_Channel_3//定时器信道,需与中断源一致
#define IRCOM_RX_IRQCHANNEL TIM3_IRQChannel//中断信道,需与定时器一致
#define IRCOM_TX_IRQCHANNEL TIM2_IRQChannel//中断信道,需与定时器一致
#define TIMER_PRESCALER 6// 定时器预分频值
#define IRCOM_BYTE_TIMEOUT 20 //红外口字节超时时间Ticks
#define IRCOM_QUEUE_LEN 64//红外口队列长度
#define IRCOM_RX_QUEUE_PTR ((PQQUEUE)IRComRxQueue) //红外口队列指针
#define IRCOM_TX_QUEUE_PTR ((PQQUEUE)IRComTxQueue) //红外口队列指针
//--------------------------------------------------------------------
typedef struct{
u16 BitTClkNum;
u16 BaudRate;
u8 DataBit;
u8 Parity:2;
u8 StopBit:2;
u8 ParityErr:1;
u8 TxParity:1;
u8 RxComMode:1;
u8 RXStep:4;
u8 TXStep:4;
u8 RXChar;
u8 TXChar;
}IRCOMDATA;
//--------------------------------------------------------------------
const u8 BitMask[] = {1<<0,1<<1,1<<2,1<<3,1<<4,1<<5,1<<6,1<<7};
const GPIO_InitTypeDef IRComRx = {IR_RX_PIN, GPIO_Speed_2MHz, GPIO_Mode_IN_FLOATING};//接收脚输入浮空
const GPIO_InitTypeDef IRComTx = {IR_TX_PIN, GPIO_Speed_2MHz, GPIO_Mode_IN_FLOATING};//发送脚复用功能开漏输出
const NVIC_InitTypeDef IRComRx_NVIC_InitStruct={IRCOM_RX_IRQCHANNEL, 0, 2, ENABLE};
const NVIC_InitTypeDef IRComTx_NVIC_InitStruct={IRCOM_TX_IRQCHANNEL, 0, 2, ENABLE};
const TIM_TimeBaseInitTypeDef IRTimerBaseCfg = {TIMER_PRESCALER-1, TIM_CounterMode_Up, 0xffff, TIM_CKD_DIV1, 0};
const TIM_ICInitTypeDef IRRxICCfg = {IR_RX_CAP_CHNNL, TIM_ICPolarity_Falling, TIM_ICSelection_DirectTI, TIM_ICPSC_DIV1, 0};
const TIM_OCInitTypeDef IRTROCCfg = {TIM_OCMode_Timing, TIM_OutputState_Disable, 0, 0, 0, 0, 0, 0};
const TIM_OCInitTypeDef IRTxPWMCfg= {TIM_OCMode_PWM1, TIM_OutputState_Enable, 0, 0, 0, 0, 0, 0};
//--------------------------------------------------------------------
static IRCOMDATA IRComData;
static u8 IRComRxQueue[sizeof(QQUEUE)+IRCOM_QUEUE_LEN];//接收队列数据定义
static u8 IRComTxQueue[sizeof(QQUEUE)+IRCOM_QUEUE_LEN];//发送队列数据定义
//--------------------------------------------------------------------
static void SetPWMOutput(bool IsEnabled)
{
register u32 Cr;
#if IR_TX_NO <= 7
Cr = IR_TX_PORT->CRL;
#else
Cr = IR_TX_PORT->CRH;
#endif
Cr &= ~(0x0000000F<<((IR_TX_NO%8)<<2));
if(IsEnabled)
Cr |= (0x0000000E<<((IR_TX_NO%8)<<2));
else
Cr |= (0x00000004<<((IR_TX_NO%8)<<2));
#if IR_TX_NO <= 7
IR_TX_PORT->CRL = Cr;
#else
IR_TX_PORT->CRH = Cr;
#endif
}
//--------------------------------------------------------------------
static void TIM_OCxInit(TIM_TypeDef * TIMx, u16 TIM_Channel, TIM_OCInitTypeDef * TIM_OCInitStruct)
{
switch(TIM_Channel)
{
case TIM_IT_CC1:
TIM_OC1Init(TIMx, TIM_OCInitStruct);
break;
case TIM_IT_CC2:
TIM_OC2Init(TIMx, TIM_OCInitStruct);
break;
case TIM_IT_CC3:
TIM_OC3Init(TIMx, TIM_OCInitStruct);
break;
case TIM_IT_CC4:
TIM_OC4Init(TIMx, TIM_OCInitStruct);
break;
}
}
//--------------------------------------------------------------------
static void TIM_SetCompare(TIM_TypeDef * TIMx, u16 TIM_Channel, u16 Compare)
{
switch(TIM_Channel)
{
case TIM_IT_CC1:
TIM_SetCompare1(TIMx, Compare);
break;
case TIM_IT_CC2:
TIM_SetCompare2(TIMx, Compare);
break;
case TIM_IT_CC3:
TIM_SetCompare3(TIMx, Compare);
break;
case TIM_IT_CC4:
TIM_SetCompare4(TIMx, Compare);
break;
}
}
//--------------------------------------------------------------------
static u16 TIM_GetCapture(TIM_TypeDef * TIMx, u16 TIM_Channel)
{
switch(TIM_Channel)
{
case TIM_IT_CC1:
return(TIM_GetCapture1(TIMx));
case TIM_IT_CC2:
return(TIM_GetCapture2(TIMx));
case TIM_IT_CC3:
return(TIM_GetCapture3(TIMx));
case TIM_IT_CC4:
return(TIM_GetCapture4(TIMx));
}
return(0);
}
//--------------------------------------------------------------------
static bool CalParity(u8 ch)//奇数个1返回true
{
ch^=(ch>>4);
ch^=(ch>>2);
ch^=(ch>>1);
ch&=0x01;
return((bool)(ch!=0));
}
//--------------------------------------------------------------------
static void IRComPortSetRxModeCap(void)//设置为接收捕获模式
{
TIM_ITConfig(IR_TX_TIMER,IR_TX_CHNNL,DISABLE);//禁止发送比较中断
TIM_Cmd(IR_TX_TIMER, DISABLE);//禁止发送定时器
TIM_Cmd(IR_PWM_TIMER, DISABLE);//禁能PWM定时器
IRComData.RxComMode = 0;
TIM_ICInit(IR_RX_TIMER, (TIM_ICInitTypeDef *)&IRRxICCfg);//配置接收输入为下降沿捕获模式
TIM_SetAutoreload(IR_RX_TIMER, 0xffff);//设置自动重装寄存器
TIM_ClearITPendingBit(IR_RX_TIMER, IR_RX_CHNNL);//清中断标志
TIM_ITConfig(IR_RX_TIMER,IR_RX_CHNNL,ENABLE);//使能接收捕获中断
TIM_Cmd(IR_RX_TIMER, ENABLE);//使能接收定时器
}
//--------------------------------------------------------------------
static void IRComPortRxCapToComp(void)//由捕获模式转换到比较模式
{
static u16 CapVol;
IRComData.RxComMode = 1;
CapVol = TIM_GetCapture(IR_RX_TIMER, IR_RX_CHNNL)+IRComData.BitTClkNum/2;
TIM_OCxInit(IR_RX_TIMER, IR_RX_CHNNL, (TIM_OCInitTypeDef *)&IRTROCCfg);//配置接收比较模式
TIM_SetCompare(IR_RX_TIMER, IR_RX_CHNNL, CapVol);//设置比较值
TIM_ClearITPendingBit(IR_RX_TIMER, IR_RX_CHNNL);//清中断标志
}
//--------------------------------------------------------------------
static void IRComPortRxCompToCap(void)//由接收比较转换到接收捕获模式
{
IRComData.RxComMode = 0;
TIM_ICInit(IR_RX_TIMER, (TIM_ICInitTypeDef *)&IRRxICCfg);//配置接收输入为下降沿捕获模式
TIM_ClearITPendingBit(IR_RX_TIMER, IR_RX_CHNNL);//清中断标志
}
//--------------------------------------------------------------------
static void IRComPortStartupSend(void)//红外口启动发送
{
RCC_ClocksTypeDef RCC_Clocks;
u32 TIM_Clocks;
RCC_GetClocksFreq(&RCC_Clocks);//取时钟频率
if(RCC_Clocks.HCLK_Frequency == RCC_Clocks.PCLK1_Frequency)
TIM_Clocks = RCC_Clocks.PCLK1_Frequency;
else
TIM_Clocks = RCC_Clocks.PCLK1_Frequency * 2;
TIM_ITConfig(IR_RX_TIMER,IR_RX_CHNNL,DISABLE);//禁止接收捕获中断
TIM_Cmd(IR_PWM_TIMER, DISABLE);//禁能PWM定时器
TIM_OCxInit(IR_PWM_TIMER, IR_PWM_CHNNL, (TIM_OCInitTypeDef *)&IRTxPWMCfg);//配置PWM 定时器为PWM 模式
TIM_SetAutoreload(IR_PWM_TIMER, TIM_Clocks/38000/TIMER_PRESCALER);//设置自动重装寄存器,周期值
TIM_SetCompare(IR_PWM_TIMER, IR_PWM_CHNNL, TIM_Clocks/38000/TIMER_PRESCALER/2);//占空比50%
TIM_SetCounter(IR_PWM_TIMER, 0);//设置计数器值
TIM_Cmd(IR_PWM_TIMER, ENABLE);//使能PWM定时器
IRComData.TXStep = 0;
TIM_OCxInit(IR_TX_TIMER, IR_TX_CHNNL, (TIM_OCInitTypeDef *)&IRTROCCfg);//配置发送定时器为输出比较模式
TIM_SetCompare(IR_TX_TIMER, IR_TX_CHNNL, TIM_GetCounter(IR_TX_TIMER)+IRComData.BitTClkNum);//设置发送比较值
TIM_ClearITPendingBit(IR_TX_TIMER, IR_TX_CHNNL);//清中断标志
TIM_ITConfig(IR_TX_TIMER,IR_TX_CHNNL,ENABLE);//使能发送比较中断
TIM_Cmd(IR_TX_TIMER, ENABLE);//使能发送定时器
}
//--------------------------------------------------------------------
void IRComPortInit(u32 Baudrate, VERIFYBIT Verifybit, STOPBIT Stopbit)
{
RCC_ClocksTypeDef RCC_Clocks;
u32 TIM_Clocks;
RCC_GetClocksFreq(&RCC_Clocks);//取时钟频率
if(RCC_Clocks.HCLK_Frequency == RCC_Clocks.PCLK1_Frequency)
TIM_Clocks = RCC_Clocks.PCLK1_Frequency;
else
TIM_Clocks = RCC_Clocks.PCLK1_Frequency * 2;
IRComData.BaudRate = Baudrate;
IRComData.DataBit = 8;
IRComData.Parity = Verifybit;
IRComData.StopBit = Stopbit;
IRComData.ParityErr = 0;
IRComData.RXStep = 0;
IRComData.TXStep = 0;
IRComData.BitTClkNum = TIM_Clocks/TIMER_PRESCALER/IRComData.BaudRate;
QQInit(IRCOM_RX_QUEUE_PTR,sizeof(IRComRxQueue));//接收队列初 始化
QQInit(IRCOM_TX_QUEUE_PTR,sizeof(IRComTxQueue));//发送队列初 始化
//IO口配置
GPIO_Init(IR_RX_PORT, (GPIO_InitTypeDef *)&IRComRx);
GPIO_Init(IR_TX_PORT, (GPIO_InitTypeDef *)&IRComTx);
NVIC_Init((NVIC_InitTypeDef *)&IRComRx_NVIC_InitStruct);//中断向量配置
NVIC_Init((NVIC_InitTypeDef *)&IRComTx_NVIC_InitStruct);//中断向量配置
TIM_InternalClockConfig(IR_TX_TIMER);//设置使用内部时钟
TIM_InternalClockConfig(IR_PWM_TIMER);//设置使用内部时钟
TIM_TimeBaseInit(IR_TX_TIMER, (TIM_TimeBaseInitTypeDef *)&IRTimerBaseCfg );
TIM_TimeBaseInit(IR_PWM_TIMER, (TIM_TimeBaseInitTypeDef *)&IRTimerBaseCfg );
IRComPortSetRxModeCap();//设置为接收捕获模式
}
//--------------------------------------------------------------------
u32 IRComPortRead(u8 *pBuf, u32 MaxLen, u32 Timeout)
{
register u32 i;
register u32 Time;
register bool Ack;
ASSERT(pBuf != NULL);
if(MaxLen == 0)
return(0);
Ack = false;
i=0;
Time = OSTimeGet();
while(i < MaxLen)
{
u8 c;
if(QQGetChar(IRCOM_RX_QUEUE_PTR,&c))//队列不空,取到数据
{
Ack = true;
*(pBuf+i) = c;
i++;
Time = OSTimeGet();
}
else//队列空,未取到数
{
if(Ack)
{
if(OSTimeGet() - Time > IRCOM_BYTE_TIMEOUT)
break;
}
else
{
if((Timeout > 0)&&(OSTimeGet() - Time > Timeout))
break;
}
OSTimeDly(1);
}
}
return(i);
}
//--------------------------------------------------------------------
u32 IRComPortWrite(u8 *pBuf, u32 Len, u32 Timeout)
{
register u32 i;
register u32 Time;
register bool SendStart;
ASSERT(pBuf != NULL);
if(Len == 0)
return(0);
i = 0;
SendStart = false;
Time = OSTimeGet();
while(i<Len)
{
if(QQSetChar(IRCOM_TX_QUEUE_PTR, *(pBuf+i)))//队列不满,放数据成功
{
i++;
}
else
{
if(!SendStart)//如果未启动发送
{
SendStart=true;
IRComPortStartupSend();//启动发送
}
if((Timeout>0)&&((OSTimeGet()-Time)>Timeout))
break;
OSTimeDly(1);
}
}
if(!SendStart)//如果未启动发送
{
SendStart=true;
IRComPortStartupSend();//启动发送
}
while(!QQChkEmpty(IRCOM_TX_QUEUE_PTR))//等待发送队列空
{
if((Timeout>0)&&((OSTimeGet()-Time)>Timeout))
break;
OSTimeDly(1);
}
OSTimeDly(1);//等待最后一字节发完
return(i);
}
//--------------------------------------------------------------------
void IRComPortRxIntDeal(void)//接收捕获比较中断处理
{
if (TIM_GetITStatus(IR_RX_TIMER, IR_RX_CHNNL) != RESET)//有捕获或比较 中断
{
TIM_ClearITPendingBit(IR_RX_TIMER, IR_RX_CHNNL);//清中断标志
if(IRComData.RxComMode == 0)//捕获模式
{
IRComPortRxCapToComp();//由捕获模式转换到比较模式
IRComData.RXStep = 0;
}
else//比较模式
{
u8 Status = GPIO_ReadInputDataBit(IR_RX_PORT, IR_RX_PIN);
if(IRComData.RXStep == 0)//开始位处理
{
if(Status != RESET)//是高电平,开始位错误
{
IRComPortSetRxModeCap();//设置到捕获模式
}
else//是低电平,开始位正确
{
TIM_SetCompare(IR_RX_TIMER, IR_RX_CHNNL, TIM_GetCapture(IR_RX_TIMER, IR_RX_CHNNL)+IRComData.BitTClkNum);//设置比较值
IRComData.RXStep++;
IRComData.RXChar = 0;
}
}
else if(IRComData.RXStep <= IRComData.DataBit)//数据位处理
{
TIM_SetCompare(IR_RX_TIMER, IR_RX_CHNNL, TIM_GetCapture(IR_RX_TIMER, IR_RX_CHNNL)+IRComData.BitTClkNum);//设置比较值
if(Status != RESET)
IRComData.RXChar |= *(BitMask+IRComData.RXStep-1);
IRComData.RXStep++;
}
else if(IRComData.RXStep == IRComData.DataBit+1)//校验或停止位处理
{
QQSetChar(IRCOM_RX_QUEUE_PTR, IRComData.RXChar);//放接收数据入接收队列
if(IRComData.Parity != VB_NONE)//有校验位
{
bool Parity = CalParity(IRComData.RXChar);//奇数个1返回true
if(IRComData.Parity == VB_ODD)
{
if(Parity == Status)//奇校验错误
IRComData.ParityErr = 1;
else
IRComData.ParityErr = 0;
}
else
{
if(Parity == Status)
IRComData.ParityErr = 0;
else
IRComData.ParityErr = 1;
}
}
IRComPortRxCompToCap();//由接收比较转换到接收捕获模式
}
}
}
}
//--------------------------------------------------------------------
void IRComPortTxIntDeal(void)
{
TIM_ClearITPendingBit(IR_TX_TIMER, IR_TX_CHNNL);//清中断标志
if(IRComData.TXStep == 0)
{
if(QQGetChar(IRCOM_TX_QUEUE_PTR, &(IRComData.TXChar)))//如果队列不空,取数据成功
{
TIM_SetCompare(IR_TX_TIMER, IR_TX_CHNNL, TIM_GetCapture(IR_TX_TIMER, IR_TX_CHNNL)+IRComData.BitTClkNum);//设置发送比较值
SetPWMOutput(true);//使能PWM 输出
IRComData.TXStep++;
}
else//队列空,数据发送结束
{
IRComPortSetRxModeCap();//设置到接收捕获模式
}
}
else if(IRComData.TXStep <= IRComData.DataBit)//处理数据位
{
TIM_SetCompare(IR_TX_TIMER, IR_TX_CHNNL, TIM_GetCapture(IR_TX_TIMER, IR_TX_CHNNL)+IRComData.BitTClkNum);//设置发送比较值
if((IRComData.TXChar & (*(BitMask + IRComData.TXStep -1))) == RESET)
SetPWMOutput(true);//使能PWM 输出
else
SetPWMOutput(false);//禁能PWM 输出
IRComData.TXStep++;
}
else if(IRComData.TXStep == IRComData.DataBit+1)//处理数据位后一位
{
if(IRComData.Parity == VB_NONE)//无校验位,则发送停止位
{
TIM_SetCompare(IR_TX_TIMER, IR_TX_CHNNL, TIM_GetCapture(IR_TX_TIMER, IR_TX_CHNNL)+IRComData.BitTClkNum/2*(IRComData.StopBit+1));//设置发送比较值
SetPWMOutput(false);//禁能PWM 输出
IRComData.TXStep = 0;
}
else
{
bool Parity;
TIM_SetCompare(IR_TX_TIMER, IR_TX_CHNNL, TIM_GetCapture(IR_TX_TIMER, IR_TX_CHNNL)+IRComData.BitTClkNum);//设置发送比较值
Parity = CalParity(IRComData.TXChar);//奇数个1返回true
if(IRComData.Parity == VB_ODD)//奇校验
{
if(Parity)//是奇数个1
SetPWMOutput(true);//使能PWM 输出
else
SetPWMOutput(false);//禁能PWM 输出
}
else if(IRComData.Parity == VB_EVEN)
{
if(Parity)//是奇数个1
SetPWMOutput(false);//禁能PWM 输出
else
SetPWMOutput(true);//使能PWM 输出
}
IRComData.TXStep++;
}
}
else if(IRComData.TXStep == IRComData.DataBit + 2)//处理停止位
{
TIM_SetCompare(IR_TX_TIMER, IR_TX_CHNNL, TIM_GetCapture(IR_TX_TIMER, IR_TX_CHNNL)+IRComData.BitTClkNum/2*(IRComData.StopBit+1));//设置发送比较值
SetPWMOutput(false);//禁能PWM 输出
IRComData.TXStep = 0;
}
}
//--------------------------------------------------------------------
#ifdef DEBUG
#include "Com1Port.h"
void IRComPortTest(void)
{
Com1PortInit(1200, VB_EVEN, SB_1);
IRComPortInit(1200, VB_EVEN, SB_1);
while(1)
{
u8 Buf[128];
u32 Len;
Len = Com1PortRead(Buf, sizeof(Buf), OS_TICKS_PER_SEC/2);
if(Len > 0)
{
IRComPortWrite(Buf,Len,OS_TICKS_PER_SEC*2);
Len = IRComPortRead(Buf,sizeof(Buf),OS_TICKS_PER_SEC*2);
if(Len > 0)
Com1PortWrite(Buf, Len, OS_TICKS_PER_SEC*2);
}
}
}
#endif
//--------------------------------------------------------------------
能提供一下输入捕获的代码给看一看吗
提供一段红外口通信的驱动代码,是通过定时器模拟串口功能实现的,很久以前的代码了,使用的还是以前的stm32标准库,不过原理相同的,希望对你有所帮助.
@JQRR_7669 谢谢大佬,我研究下。
@JQRR_7669
我仔细看了您的接收数据帧的部分,就是下面这一段,还是有点问题想问一下。
我的理解是:捕获到下降沿 —> 如果是接收到起始帧 —> 设置为输出比较模式 —> 触发定时器中断 —> 接收一个bit —> 把比较数值累加下一个bit需要采样的数值 —> 循环读取8个bit之后触发接收标志 —> 重置为输入捕获下降沿。
这里的接收方式也是靠反复进入定时器中断来实现的,这样写的话会不会也被系统中断干扰到?相比于定时器的溢出中断,输出比较模式的中断有什么优势?
使用比较输出模式下,输出是直接由硬件控制完成,中断处理可以允许1个bit的延迟;在接收模式下,开始位的下降沿时间由硬件捕获获得,不受中断处理延迟影响,接收数据时是基于捕获的开始沿时间产生比较中断,在中断处理中采样数据位,允许半个bit的中断处理延迟。
你的驱动方式的缺点,开始位中断处理有延迟的话,接下来所有位的采样都收影响,并且前边的前面的中断处理延迟回累积到后边,数据位的采样时间不够准确。
是红外线接收头吗?为什么要模拟成串口呢