STM32 freertos 使用软件模拟串口uart

如题,为什么要这样做?

最近做的一个项目上使用了74HC595作为指示灯板使用;

这个灯板与驱动板是通过排线连接,排线约25cm长;

在实验室测试一切正常,发到客户手上使用就出现了某个LED跳动情况;

跳动原因:传输线收到干扰。

这种显示方式抗干扰能力非常差且没有校验功能,满足不了需求;

因为传输线是必须要有的,所以只能通过增加校验的方式来处理干扰。

解决方法:

  1. 指示灯板增加MCU;
  2. 驱动板与灯板改为UART通讯增加校验功能;

因为驱动板与灯板连接的IO口没有UART外设功能,只能通过软件模拟UART使用;

代码

代码来自于:发个软件UART代码吧 (amobbs.com 阿莫电子技术论坛)

#include "SimComIO.h"
#include 



//为了提高接收的准确率和纠错性能,采用3倍采样率,定时中断的频率是波特率的3倍。例如:需要波特率9600bps的话,需要1/(9600*3)=34.72us的定时中断。


//#define SC_RXD 6
//#define SC_RXD_SET_INPUT() 	{DDRD&=(~BIT(SC_RXD));}
//#define SC_RXD_GET() 				((PIND&(BIT(SC_RXD))))
#define SC_RXD_GET() 				(HAL_GPIO_ReadPin(SIM_UART_RX_GPIO_Port, SIM_UART_RX_Pin))


//#define SC_TXD 7
//#define SC_TXD_0() 					{PORTD&=(~BIT(SC_TXD));}
//#define SC_TXD_1() 					{PORTD|=BIT(SC_TXD);}
//#define SC_TXD_SET_OUTPUT() {DDRD|=BIT(SC_TXD);}
#define SC_TXD_0() 					{HAL_GPIO_WritePin(SIM_UART_TX_GPIO_Port, SIM_UART_TX_Pin, GPIO_PIN_RESET);}
#define SC_TXD_1() 					{HAL_GPIO_WritePin(SIM_UART_TX_GPIO_Port, SIM_UART_TX_Pin, GPIO_PIN_SET);}

uint8_t sc1_rxd_scan_ct=0;
uint8_t sc1_rxd_scan_next_time=0;
uint8_t sc1_rxd_scan_step=0;
uint8_t sc1_rxd_dat;
uint8_t sc1_rxd_ready=0;
uint8_t sc1_rxd_tmpdat;


volatile uint8_t sc_txd_ready=0;//模拟串口变量
volatile uint8_t sc_txd_bit_pt=0;
volatile uint8_t sc_txd_data=0;
volatile uint8_t sc_txd_ct=0;



// typedef sim_uart_def{
	// uint8_t sc1_rxd_scan_ct;
	// uint8_t sc1_rxd_scan_next_time;
	// uint8_t sc1_rxd_scan_step;
	// uint8_t sc1_rxd_dat;
	// uint8_t sc1_rxd_ready;
	// uint8_t sc1_rxd_tmpdat;
	// uint8_t sc_txd_ready;//模拟串口变量
	// uint8_t sc_txd_bit_pt;
	// uint8_t sc_txd_data;
	// uint8_t sc_txd_ct;
// }TYPE_SIM_UART_DEF;

// TYPE_SIM_UART_DEF su1,su2,su3,su4,su5,su6;

// void SC_RxdSrv(TYPE_SIM_UART_DEF *pSU);
// void SC_TxdSrv(TYPE_SIM_UART_DEF *pSU);


void SC_Recv_Pro(uint8_t dat){

}

void SC_RxdSrv(void){
       
        if(sc1_rxd_scan_step==0){
                if(!SC_RXD_GET()){
                        sc1_rxd_scan_step=1;
                        return;
                }
        }
        if(sc1_rxd_scan_step==1){
                if(!SC_RXD_GET()){
                        sc1_rxd_scan_step=2;                //rxd start bit ok,goto next step
                        sc1_rxd_scan_ct=0;
                        sc1_rxd_scan_next_time=3;
                        sc1_rxd_tmpdat=0;
                        return;
                }
                else{
                        sc1_rxd_scan_step=0;        //rxd start bit is avalid
                        return;
                }
        }
        if(sc1_rxd_scan_step>=2){
                sc1_rxd_scan_ct++;
                if(sc1_rxd_scan_ct>=1;
                        if(SC_RXD_GET()){
                                sc1_rxd_tmpdat|=0x80;
                        }
                        sc1_rxd_scan_step++;       
                        return;
                }
                if(sc1_rxd_scan_step==10){
                        if(SC_RXD_GET()){
                                sc1_rxd_dat=sc1_rxd_tmpdat;
                                sc1_rxd_ready=1;

                                //Receive a byte OK       
                                #if 0
                                 sc_txd_data=sc1_rxd_dat;
                                 sc_txd_ready=1;
                                 #endif
                                 #if 0
                                 Test_Uart1(sc1_rxd_dat);
                                 #endif

                                 SC_Recv_Pro(sc1_rxd_dat);
                        }
                        sc1_rxd_scan_step=0;
                        return;
                }
               
               
        }

}



void SC_TxdSrv(void){
        sc_txd_ct++;
        if(sc_txd_ct<3) return;
        sc_txd_ct=0;
        if(sc_txd_ready){                                                        //Data Ready
                if(sc_txd_bit_pt<10){
                        if(sc_txd_bit_pt==0){
                                SC_TXD_0();                                //Start BIT
                       
                        }
                        else{
                               
                                if(sc_txd_bit_pt>=9){
                                        SC_TXD_1();                        //End BIT
                                }
                                else{                                                        //数据位
                                        if((sc_txd_data>>(sc_txd_bit_pt-1))&0x01){       
                                                SC_TXD_1();
                                        }
                                        else{
                                                SC_TXD_0();
                                        }
                                }                       
                        }
                }               
                if(sc_txd_bit_pt>10){               
                        sc_txd_bit_pt=0;                        //发送完后延时两个时钟,复位各标志
                        sc_txd_ready=0;               
                }
                else{
                        sc_txd_bit_pt++;                        //位指针自加               
                }
        }
       

}


void SC_send_char(uint8_t b){
         sc_txd_data=b;
         sc_txd_ready=1;
         while(sc_txd_ready==1);

}
void SC_send_str(uint8_t *str){
         while((*str)!=0){
                 SC_send_char(*str);
                str++;
         }

}

void SC_send_arr(uint8_t *arr,uint8_t len){
         while(len--){
                 SC_send_char(*arr);
         }
}


void init_SimComIO(void){
	
//	SC_TXD_SET_OUTPUT();
	SC_TXD_1();
//	SC_RXD_SET_INPUT();
	
}


// #pragma interrupt_handler ISR_T1:8
// void ISR_T1(void)
// {
// //compare occured TCNT1=OCR1A

        // SC_RxdSrv();
        // SC_TxdSrv();
// }

移植

定时器

        波特率9600,3倍采样34.72us,使用定时器2完成

STM32 freertos 使用软件模拟串口uart_第1张图片

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  /* USER CODE BEGIN Callback 0 */

  /* USER CODE END Callback 0 */
  if (htim->Instance == TIM4) {
    HAL_IncTick();
  }
  /* USER CODE BEGIN Callback 1 */
  if (htim->Instance == TIM2) {
//    HAL_GPIO_TogglePin(SIM_UART_TX_GPIO_Port, SIM_UART_TX_Pin);
		SC_TxdSrv();
//		SC_RxdSrv();
  }
  /* USER CODE END Callback 1 */
}

示波器观察IO引脚反转周期,符合需求。 

Freertos

HAL_TIM_Base_Start_IT(&htim2);    

在开启定时器的时候,整个任务调度接近瘫痪。

问题分析:

freertos心跳是1ms,35us这样频繁中断会影响调度的。

现在的问题是:freertos 能用35us这样的中断吗?如果能应该怎么用?

于是chatgpt开始对话,问题主要是问gpt freertos中这种35us 频繁中断应该怎么使用?

没有得到想要的答案;

网络上没得到答案就咨询身边大佬吧;

STM32 freertos 使用软件模拟串口uart_第2张图片

转义:

就是咱们这个发送也不是连续不断发送,实际使用中1S发送1帧就行了。

使用

taskENTER_CRITICAL();
//code
taskEXIT_CRITICAL(); 

    加入代码测试,发现问题:进入临界区之后定时器2中断不进了,被关了。

于是想,那就使用停止调度的呗

vTaskSuspendAll();         /* 开启调度锁 */    
        printf("任务vTaskLed1正在运行\r\n");   
        if(!xTaskResumeAll())      /* 关闭调度锁,如果需要任务切换,此函数返回pdTRUE,否则返回pdFALSE */
        {
            taskYIELD ();
        }    

测试还是不行。

二问大佬:

STM32 freertos 使用软件模拟串口uart_第3张图片

于是查代码,freertos在哪里管理中断的;

taskENTER_CRITICAL();

进这个接口查源码

最后查到了下面那个地方

STM32 freertos 使用软件模拟串口uart_第4张图片

打开cubemx 找修改优先级的地方

STM32 freertos 使用软件模拟串口uart_第5张图片

上面是修改优先级的地方,那定时器2的优先级是多少呢?

STM32 freertos 使用软件模拟串口uart_第6张图片

发现这个优先级是灰色的,是与freertos的 

是绑定的,比如你将 LIBRARY MAX SYSCALL INTERRUPT PRIORITY修改为6,定时器的优先级也会跟着修改为6;

那好办,在代码中手动修改定时器2优先级

HAL_NVIC_SetPriority(TIM2_IRQn, 4, 0);
	HAL_NVIC_GetPriority(TIM2_IRQn, TIM2_PriorityGroup, &TIM2_PreemptPriority, &TIM2_SubPriority);
	printf(" HAL_NVIC_GetPriority TIM2_PriorityGroup:%d, TIM2_PreemptPriority:%d,TIM2_SubPriority:%d  \r\n" ,TIM2_PriorityGroup,TIM2_PreemptPriority,TIM2_SubPriority);

验证修改成功。

验证

SC_send_char(0x55);

发现使用上面代码会卡在这个while里面,单步调试查看 sc_txd_ready 变量已经变为0了,为什么还会卡在这里? 

void SC_send_char(uint8_t b){
         sc_txd_data=b;
         sc_txd_ready=1;
         while(sc_txd_ready==1);

}

解决方法

加入volatile 修饰符;

volatile uint8_t sc_txd_ready=0;

总结

        通过上面学习使用方法与故障排除,STM32 freertos 使用软件模拟串口uart已经正常可以使用了。通过这个测试也对freertos 有了进一步认识。

感谢

babyos 作者 提供的帮助;

你可能感兴趣的:(MCU,stm32,模拟串口,freertos,临界区,中断,us)