我们从2011年坚守至今,只想做存粹的技术论坛。  由于网站在外面,点击附件后要很长世间才弹出下载,请耐心等待,勿重复点击不要用Edge和IE浏览器下载,否则提示不安全下载不了

 找回密码
 立即注册
搜索
查看: 797|回复: 0

STM8模拟串口程序

[复制链接]

该用户从未签到

36

主题

121

回帖

0

积分

二级逆天

站在风口上,猪都能飞

积分
0

终身成就奖

发表于 2019-11-6 10:59:13 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区

您需要 登录 才可以下载或查看,没有账号?立即注册

×
STM8s103f3p3单片机只有一个串口,有时候在实际项目中,我们需要用到多个串口来实现项目要求,此时,我们可以通过IO口来模拟串口通信,在实现项目需求的同时,还大大降低了成本。
## 实验原理
默认串口空闲状态为高电平,开始位为0,然后发送8个数据位,然后是奇偶校验位,停止位为高电平。数字电路中只有0、1两种状态,这是我们用IO口可以实现的,我们认为高电平是1,低电平是0。也就是说我们用只到了IO口的输出功能(对于实现TX功能的IO口而言),那么到底发多长时间的高电平呢?这是由TIMx定时器决定的,TIMx定时器通过计数器实现。这个时间取决于什么呢?取决于波特率。也就是说,只要我们初始化设置好了TIMx定时器我们就不需要考虑时间问题了。为方便我们现发送低位,然后逐渐右移来发送高位。如何判断发送完一个字节呢?我们认为10位为一个字节。如何判断发送玩所有的字节呢?我们使用一个缓存区(也就是数组)缓存区的字节发送完了我们就认为发送完了。

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


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

```
#define    VM_UART_TXD_PORT_OUT       PC_DDR|=1<<5 C_CR1|=(1<<5 )C_CR2 &=~(1<<5)//设定为输出
#define    VM_UART_TXD_PORT_IN        PC_DDR&=~(1<<5 )C_CR1|=(1<<5 )C_CR2&=~(1<<5 )//设定为输入

```

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

```
#define    VM_UART_RXD_PORT_IN        PB_DDR&=~(1<<4)B_CR1|=(1<<4)B_CR2&=~(1<<4) //设置只上拉输入 不中断
#define    VM_UART_RXD_PORT_INT_IN    PB_DDR&=~(1<<4)B_CR1|=(1<<4)B_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口模拟串口通线,实习那真正串口,大大降低了成本。以下是串口发送的效果图。
写得不好,多多指教。
工程代码:https://download.csdn.net/download/weixin_43184208/11120674
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

公告:服务器刚移机,
大家请不要下载东西。
会下载失败


Copyright ©2011-2024 NTpcb.com All Right Reserved.  Powered by Discuz! (NTpcb)

本站信息均由会员发表,不代表NTpcb立场,如侵犯了您的权利请发帖投诉

( 闽ICP备2024076463号-1 ) 论坛技术支持QQ群171867948 ,论坛问题,充值问题请联系QQ1308068381

平平安安
TOP
快速回复 返回顶部 返回列表