[单片机] MCU自定义引脚当串口

串口RS232的数据协议有两种,我们以PC与8位/32位单片机间比较常用的异步模式为例子讲解.
单片机的TXD引脚先发一个低电平0给对方,然后发送5–8个位作为数据,最后发送1个位奇偶校验和1个位高电平1作为停止位.
以下例子为波特率9600bps,1个开始位,8个数据位,1个停止位 [10位数据,无奇偶校验位:9600-8-N-1]
波特率:每秒钟发送的字节位,如果baud=9600bps,也就是说,每发送1位耗时1000ms/9600bps=104uSec

//:常用波特率对应定时周期
2400bps<–>417uS (416.666us)
4800bps<—>208uS (208.333us)
9600bps<—>104uS (104.166us)
19200bps<–>52uS (52.083us)
115200bps<–>8uS (8.68us)

/**************************************
   利用51定时器中断模拟串口信息发送
   优点:可以自定义串口引脚,可以实现多个串口
   缺点:占用1个定时器,定时器中断周期需要稳定不被打断
   格式: 9600-8-N-1
   功能:模拟串口Send
*****************************************/
#include <REG51.H>         
                                 
char   g_nSendInx=0;
char   g_nBuff=0;
char   g_nFlag=0;

#define MY_TXD P2_1 //定义TXD引脚,原生芯片是P3.1


void Timer0Init(void)//104微秒@11.0592MHz
{
 //1000ms/9600=104.166us
 //使用这种方案,由于精确性问题,长时间传输会偶然出现几个误码,而且需要保证定时器不被其他中断打断
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x02;		//设置定时器模式
	TL0 = 0xA0;		//设置定时初值
	TH0 = 0xA0;		//设置定时重载值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时

	ET0=1;
	EA=1;
}

/******************************
  自定义串口发送单字符  
******************************/
void MyUart_Send(char ch){
  g_nFlag=0;
  g_nBuff=ch;
  TR0=1; //Start timer
  while(g_nFlag==0);
}


/******************************
  自定义串口发送字符串
******************************/
void MyUart_String(char* str){
 
  int i=0;
 
  while(*(str+i)){
   g_nFlag=0;
   g_nBuff=*(str+i);
    i++;
   TR0=1; //Start Timer
   while(g_nFlag==0);
  }
}


/*
   定时器0中断服务
*/
void Timer0_ISP() interrupt 1{

  g_nSendInx++;//发送模拟第几位

  if(g_nSendInx==1)
  {
     MY_TXD=0;//起始位拉低

  }else if(g_nSendInx==10)  //格式: 9600-8-N-1,完整10Bit数据
  {
     MY_TXD=1;//停止位拉高
	 TR0=0; //Stop Timer
     g_nSendInx=0;
	 g_nFlag=1;///发送完成标志位
  }else{
   
    MY_TXD=g_nBuff&0x01;  //set sbuffer_data
	g_nBuff>>=1;
  }

   
}
										 
/**********************************
  用Timer0定时中断模拟串口信号
**********************************/
void main (void) {

   Timer0Init();///定时器初始化,周期为 1000ms/9600=104us

   MyUart_String("===Welcome,My Uart===\r\n ");
   MyUart_Send(0x61);
   MyUart_Send(0x62);
   MyUart_Send(0x63);

  while (1);

 }
/************************************
 *  芯片:比亚迪 BF7612CMXX (51内核)
 *  功能: 串口TXD模拟发送,采用Timer1
 *************************************/

//以下头文件可在比亚迪半导体官网下载 [CTK_Library]
#include "BF7612CMXX.h"
#include "BF7612CMXX_API_Timer.h"


typedef unsigned char uchar;
typedef unsigned int uint;
typedef unsigned long ulong;
typedef unsigned char BYTE;

#define SYSCLK_SEL 0 // 系统时钟选择0-12MHz,1-6MHZ,2-4MHz

#if (SYSCLK_SEL == 0)
#define SYSCLK 12 // SYS-12Mhz
#elif (SYSCLK_SEL == 1)
#define SYSCLK 6 // SYS-6Mhz
#elif (SYSCLK_SEL == 2)
#define SYSCLK 4 // SYS-04Mhz
#else

#endif

#define SYS_CLK_SET(x)                \
  {                                   \
    SYS_CLK_CFG &= ~(0x03);           \
    SYS_CLK_CFG |= (0x03 & (x << 0)); \
  } //(0--12Mhz,1--06Mhz,2--04Mhz)


char g_nSendInx = 0;
char g_nTxdBuff = 0;
char g_nTxdFlag = 0;

#define MY_TXD PD3 //=====自定义TXD引脚=====

#define T_UART_START  TR1 = 1
#define T_UART_STOP   TR1 = 0

uchar TH1_Reload = 0;
uchar TL1_Reload = 0;

/******************************
  自定义串口发送单字符
******************************/
void MyUart_Send(char ch)
{
  g_nTxdFlag = 0;
  g_nTxdBuff = ch;
  T_UART_START; // Start timer
  while (g_nTxdFlag == 0)
    ;
}

/******************************
  自定义串口发送字符串
******************************/
void MyUart_String(char *str)
{
  int i = 0;
  while (*(str + i))
  {
    g_nTxdFlag = 0;
    g_nTxdBuff = *(str + i);
    i++;
    T_UART_START; // Start Timer
    while (g_nTxdFlag == 0)
      ;
  }
}

void Print_Num(char *str, int num)
{
  idata char buf[15] = {0}; // max len=15
  int cnt = 0;
  int i = 0;
  int temp = num;
  if (num < 0)
    temp = -num;
  for (i = 0; i < 15; i++)
  {
    cnt++;
    buf[i] = temp % 10 + '0';
    temp /= 10;
    if (temp == 0)
      break;
  }
  MyUart_String(str);
  if (num < 0)
    MyUart_Send('-');

  while (cnt >= 1)
  {
    MyUart_Send(buf[cnt - 1]);
    cnt--;
  }
  MyUart_Send('\n'); // end char
}
void MyTimer1_ISP(void)
{
  g_nSendInx++; // 发送模拟第几位

  if (g_nSendInx == 1)
  {
    MY_TXD = 0; // 起始位拉低
  }
  else if (g_nSendInx == 10) // 格式: 9600-8-N-1,完整10Bit数据
  {
    MY_TXD = 1;  // 停止位拉高
    T_UART_STOP; // Stop Timer
    g_nSendInx = 0;
    g_nTxdFlag = 1; /// 发送完成标志位
  }
  else
  {
    MY_TXD = g_nTxdBuff & 0x01; // set sbuffer_data
    g_nTxdBuff >>= 1;
  }
}

void Timer1_Init(uint Timer1Us)
{
  EA = 0;          // 关总中断;
  T1_IP_SET;       // 根据实际应运设置优先级
  T1_INT_FLAG_CLR; // 清除Timer1中断标志
  T1_CT_MODE(0);   // Timer1定时/计数模式选择0为定时模式,1为计数模式
  T1_MODE_SET(1);  // 设置为16位计算器,tiemr1_clk=(1/12)*sys_clk

  TH1 = (0xFFFF - ((uint)((Timer1Us * 1.0) * ((SYSCLK * 1.0) / 12.0)))) >> 8;
  TL1 = (0xFFFF - ((uint)((Timer1Us * 1.0) * ((SYSCLK * 1.0) / 12.0))));

  TH1_Reload = TH1;
  TL1_Reload = TL1;

  T1_IE_SET; // 开Timer1中断使能
  T1_RUN;    // 开启Timer1
  EA = 1;    // 开总中断
}

// INT3 as timer1 isr fuction
void Timer1_ISR() interrupt 3
{

  TH1 = TH1_Reload;
  TL1 = TL1_Reload;
  T1_INT_FLAG_CLR; // 清除Timer1中断标志
  MyTimer1_ISP();
}

void main(void)
{
  long j = 0;
  int loop_cnt = 0;

  SYS_CLK_SET(SYSCLK_SEL); // Select sysclk as 12Mhz
  WDT_EN = 0X55;           // Close WatchDog
  // PD3 as txd test
  TRISD = ~(1 << 3); // D_Port IOMode  0:Output; 1:input;
  Timer1_Init(104);  // 定时器1初始化,104us=>9600bps

	MyUart_String("===StartTest===\n");
  while (1)
  {
   
    for (j = 0; j < 150000; j++)
      ; //在12Mhz下延时1秒
		Print_Num("#CNT=",loop_cnt++);
   
  }
}

/*********************************
   利用ESP32定时器中断模拟串口信息发送
   优点:可以自定义串口引脚,可以实现多个串口
   格式: 19200-8-N-1
   功能:模拟串口Send
 **********************************/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_timer.h"

#define UART_BAUDRATE 19200 // MAX:19200
#define GPIO_SOFT_TXD (20)  // 软件模拟串口TXD
esp_timer_handle_t g_hUart_handle;
uint16_t g_nTmInterval = 0;
char g_nSendInx = 0;
char g_nBuff = 0;
char g_nFlag = 0;

//实测通过范围 (1200~~19200) bps
void soft_uart_set_baud(uint32_t baud)
{
   g_nTmInterval = 1000000 / baud;
}

void soft_uart_send_byte(uint8_t ch)
{
    //(104.1us->9600bps)(52.0us->19200bps)
    g_nFlag = 0;
    g_nBuff = ch;

    esp_timer_start_periodic(g_hUart_handle, g_nTmInterval); // TimeUnit:1us
    while (g_nFlag == 0)
    {
    }
}

void soft_uart_send_str(char *str)
{
    //(104.1us->9600bps)(52.0us->19200bps)

    int i = 0;
    while (1)
    {
        g_nFlag = 0;
        g_nBuff = str[i++];
        if (g_nBuff == 0)
        {
            break;
        }

        esp_timer_start_periodic(g_hUart_handle, g_nTmInterval); // TimeUnit:1us
        while (g_nFlag == 0)
        {
        }
    }
}

static void soft_uart_send_proc(void *arg)
{
    g_nSendInx++; // 发送模拟第几位

    if (g_nSendInx == 1)
    {
        gpio_set_level(GPIO_SOFT_TXD, 0); 起始位拉低
    }
    else if (g_nSendInx == 10) // 格式: Baud-8-N-1,完整10Bit数据
    {
        gpio_set_level(GPIO_SOFT_TXD, 1); // 停止位拉高
        esp_timer_stop(g_hUart_handle);   // stop tx send timer
        g_nSendInx = 0;
        g_nFlag = 1; /// 发送完成标志位
    }
    else
    {
        gpio_set_level(GPIO_SOFT_TXD, g_nBuff & 0x01);
        g_nBuff >>= 1;
    }
}

static void soft_uart_init()
{

    gpio_config_t io_conf = {};
    // set as output mode
    io_conf.mode = GPIO_MODE_OUTPUT;
    io_conf.pin_bit_mask = (1ULL << GPIO_SOFT_TXD);
    io_conf.pull_down_en = 0;
    io_conf.pull_up_en = 1;
    // disable interrupt
    io_conf.intr_type = GPIO_INTR_DISABLE;
    // configure GPIO with the given settings
    gpio_config(&io_conf);

    soft_uart_set_baud(UART_BAUDRATE);
    const esp_timer_create_args_t periodic_timer_args = {
        .callback = &soft_uart_send_proc,
        .name = "soft_uart_send_proc"};

    esp_err_t err = esp_timer_create(&periodic_timer_args, &g_hUart_handle);
    if (err != ESP_OK)
    {
        // timer create fail
    }
    else
    {
        g_nSendInx = 0;
        g_nBuff = 0;
        g_nFlag = 0;
    }
}

void my_printf(char *fmt, ...)
{

    char *buf = (char *)malloc(1024);
    if (buf == NULL)
    {
        return;
    }
    int len = 0;
    va_list args;
    va_start(args, fmt);
    len += vsprintf(buf, fmt, args);
    va_end(args);
    *(buf + len) = 0;
    soft_uart_send_str(buf);
    free(buf);

    return;
}

void app_main(void)
{

    int cnt = 0;
    uint8_t ch = 0;
    char buffer[50] = {0};

    soft_uart_init();

    while (1)
    {
        cnt++;
        ch = 'A' + cnt % 24;

        soft_uart_send_byte(ch); // 发送单字符

        my_printf("#COUNT=%d.Char=%c(%02x)\n",cnt,ch,ch);//发送字符串
        vTaskDelay(200);
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值