单片机学习小组
直播中

史晓明

7年用户 874经验值
私信 关注

请问STM8单片机IO口模拟串口通信如何实现?

请问STM8单片机IO口模拟串口通信如何实现?

回帖(1)

张建国

2022-2-21 09:41:04

IO口配置
串口通信需要发送(TX)和接收(RX)两根通信线(半双工模式)。因此,我们需要将两个IO端口分别配置成TX和RX。


1.IO_TX发送配置:IO基本模式配置(具体请看前几期IO口配置博客),开启外部中断,触发模式设置为下降沿触发;
代码如下:


#define        VM_UART_TXD_PORT_OUT       PC_DDR|=1<<5 ;PC_CR1|=(1<<5 );PC_CR2 &=~(1<<5)//设定为输出
#define        VM_UART_TXD_PORT_IN        PC_DDR&=~(1<<5 );PC_CR1|=(1<<5 );PC_CR2&=~(1<<5 )//设定为输入


2.IO_RX发送配置:IO基本模式配置(具体请看前几期IO口配置博客),开启外部中断,触发模式设置为上升沿和下降沿触发
代码如下:


#define        VM_UART_RXD_PORT_IN        PB_DDR&=~(1<<4);PB_CR1|=(1<<4);PB_CR2&=~(1<<4) //设置只上拉输入 不中断
#define        VM_UART_RXD_PORT_INT_IN    PB_DDR&=~(1<<4);PB_CR1|=(1<<4);PB_CR2|=(1<<4) //设置只上拉输入 中断


3.初始化IO口,并配置中断及编写中断服务函数
代码如下:


/**************************************************************************
                                                                                                全局变量
**************************************************************************/
u8 vm_UART_RX_P;//接收缓存指针
u8 vm_UART_RX_BUF[vm_UART_RX_BUF_L];//接收缓存


u8 vm_UART_RX_byte;//扩展串口 字节缓冲区
u8 vm_UART_RX_bit;//扩展串口 计算位数


u8 vm_UART_TX_byte;//扩展串口 字节缓冲区
u8 vm_UART_TX_bit;//扩展串口 计算位数


u8 vm_uart_tx_flag;//正在发送标志
u8 vm_uart_rx_flag;//正在接收标志
/**************************************************************************/
void IO_EXTI_init(void)
{
    VM_UART_TXD_PORT_IN;//发送初始化
   //外部中断
    EXTI_CR1 &=~(3<<4);//清零
    EXTI_CR1 |=2<<4;//下降沿触发
    VM_UART_RXD_PORT_INT_IN;//接收初始化
   //外部中断
    EXTI_CR1 &=~(3<<2);//清零
    EXTI_CR1 |=3<<2;//上升沿和下降沿触发
}
//中断服务函数
#pragma vector=7
__interrupt void EXTI_PORTC_IRQHandler(void)
{
  //外中断一次收一个字节,只识别起始位
     if((PB_IDR &0x10) == 0)
    {
        TIM1_START;//启动定时器
        vm_UART_RX_byte = 0;
        vm_UART_RX_bit = 0;
        VM_UART_RXD_PORT_IN; //只上拉输入 不中断
        vm_uart_rx_flag = 1;//开启发送
    }
}


波特率配置
由实验原理我们可以知道波特率可以通过配置定时器来设置,具体计算公式如下所示:


stm8波特率计算:主时钟频率/分频系数/波特率=装载值
例:波特率9600 主时钟频率16MHz 分频系数1
初值=16x10^6/9600=1667


根据公式,我们可以求得初值,然后配置定时器自动装载值,分频系数,计数器模式,开启中断!开启中断!开启中断!重要事情说三遍!


发送
定时器配置(此处初始化TIM2定时器实现发送功能),中断服务函数及串口发送函数
代码部分:


//1.定时器2初始化
void TIM2_Configuration(u16 time, u8 en)
{
    TIM2_CR1|=1<<7;允许自动装载值
    TIM2_PSCR = 0;//预分频系数


    TIM2_ARRH = (uint8_t)(time >> 8);
    TIM2_ARRL = (uint8_t)(time & 0xff); //自动装载值


    TIM2_IER = 1;//中断使能
    if(en != 0)
    {
        TIM2_CR1 |= 1<<0;//使能计数器
    }
}
//2.定时器2中断服务函数
#pragma vector=0xF
__interrupt void TIM2_UPD_OVF_BRK_IRQHandler(void)
{
     TIM2_SR1&=~(1<<0);//清空标志位


    if(vm_UART_TX_bit < 8)//判断数据位数

    {
        if((vm_UART_TX_byte & 0x1) == 0x1)
        {
            VM_UART_TXD_PORT_WriteHigh;
        }
        else
        {
            VM_UART_TXD_PORT_WriteLow;
        }
        vm_UART_TX_byte /= 2;
    }
    else
    {
        VM_UART_TXD_PORT_WriteHigh;
        if(vm_UART_TX_bit > 8)//判断数据位数
        {
            vm_uart_tx_flag = 0; //设置为发送完毕
            VM_UART_TXD_PORT_IN;
            TIM2_STOP;//
        }
    }
    vm_UART_TX_bit++;
}
//3.模拟串口发送函数,此处编写了三个发送函数,根据自己需求调用
void vm_UARTsend_byte(u8 byte)
{
    while(vm_uart_tx_flag == 1); //检查是否发送完毕


    vm_uart_tx_flag = 1;
    VM_UART_TXD_PORT_OUT;
    VM_UART_TXD_PORT_WriteLow;
    vm_UART_TX_bit = 0;
    vm_UART_TX_byte = byte;
    TIM2_START; //计数器置零,启动定时器
}
void vm_UART_SendString(u8 *Data, u16 len)
{
    u16 i;
    for(i=0; i < len; i++)
        vm_UARTsend_byte(Data);


}
void vm_UART_SendStr(u8 *str)
{
    u16 i = 0;
    while((*(str+i)) != 0)
    {
        vm_UARTsend_byte(*(str+i));
        i++;
    }
}


接收
配置定时器1(TIM1),中断服务函数(包含接收部分)
代码如下:


//定时器1初始化
//通过设计算定时器初值来设置波特率
/*stm8波特率计算:主时钟频率/分频系数/波特率=装载值*/
void TIM1_Configuration(u16 time, u8 en)
{
    TIM1_CR1 |= 1<<7;//允许自动装载值
    /*设置自动装载值*/
    TIM1_ARRH = (uint8_t)(time >> 8);//取高八位
    TIM1_ARRL = (uint8_t)(time)&0x00ff;//取低八位,高八位清零
  
    /* 设置分频系数*/
    TIM1_PSCRH = (uint8_t)(0x0000 >> 8);
    TIM1_PSCRL = (uint8_t)(0x0000)&0x00ff;
    /* 选择计数器模式 */
    TIM1_CR1&= ~(1<<4);//向上计数
    TIM1_CR1&=~(1<<3);//循环计数
    TIM1_CR1&=~(1<<2);//更新请求源
    TIM1_CR1 = 0;//清零
    /* 设置重复计数模式 */
    TIM1_RCR = 0x00;
    /* 设置允许中断 */
    TIM1_IER = 1;//中断使能
    if(en != 0)
    {
        TIM1_CR1 |= 1<<0;//使能计数器
    }
}
//2.定时器1中断服务函数
#pragma vector=0xD
__interrupt void TIM1_UPD_OVF_TRG_BRK_IRQHandler(void)
{
    TIM1_SR1&=~(1<<0);//清空标志位
    if((PB_IDR &0x10) == 0)
    {
        vm_UART_RX_byte /= 2;
    }
    else
    {
        vm_UART_RX_byte /= 2;
        vm_UART_RX_byte |= 0X80;
    }
    vm_UART_RX_bit++;
    if(vm_UART_RX_bit >= 8)
    {
        (uint8_t)PC_ODR_ODR4>0?0:1;//高取反,低取高,必须配置为输出
        TIM1_STOP;//停止定时器
        VM_UART_RXD_PORT_INT_IN;
        vm_uart_rx_flag = 0;
        vm_UART_RX_bit = 0;//
        vm_UART_RX_BUF[vm_UART_RX_P] = vm_UART_RX_byte; //一个字节时接收完毕
        vm_UART_RX_P++;
        if(vm_UART_RX_P >= vm_UART_RX_BUF_L)vm_UART_RX_P = 0; //接收指针
    }
}


主函数部分
1.时钟及所有外设初始化
2.开启全局中断
3自行添加代码部分


总结
本教程详细讲解了STM8单片机IO口模拟串口通线,实习那真正串口,大大降低了成本。以下是串口发送的效果图。
写得不好,多多指教。


举报

更多回帖

×
20
完善资料,
赚取积分