STM8S003F使用I/O口模拟串口(一)发送数据

来源:互联网 发布:js date获取日期 编辑:程序博客网 时间:2024/05/02 06:13

最近在使用STM8S003F模拟串口发送数据,网上资源很多,但是没有找到我需要的,因此自己写一篇文章,做一个总结,这篇文章主要是不用库函数实现发送简单的过程。

1、串口通信原理和模拟串口发送数据的原理

标准串口数据格式为:起始位(1bit)+数据位(8bit)+校验位(1bit)+停止位(1bit)。其中起始位为低电平,停止位为高电平。

串口通讯需要设置波特率和检查COM口。

思路是这样的,我们使用定时器TIM2来定时,每隔一端时间发送一个位,从而实现模拟串口发送数据。

2、获得定时器ARR自动装载的值

为了简便,我们不要校验位,因此共有10个位的数据。

我以stm8中9600bit/s的波特率计算的过程为例(1秒钟传输9600位)。

可以计算出传输1位所需要的时间 T1 = 1/9600 约为104us。

stm8 内部晶振频率为16M,我采用16分频也就是1M,故MCU震荡周期为 1/1M = 1us

由上面的计算我们可以知道要发送一位数据,定时器中定时的自动装载的值应设为为 104/1 =104。

3、实现过程

在实现过程中,我们在工程文件夹SimUART中共分了4个文件夹(分别为System:存放系统文件;Project:存放项目文件;User:存放main.c和UserApp.c;My_Lib:存放其它常用的文件)。根据我们将用到的单片机的资源,我们在My_Lib中分了三个文件,分别是——Delay:存放与延时函数相关的文件;IO:存放与IO口相关函数的文件;Time:与定时器相关函数的文件。下面我贴出相关函数的.c文件,而.h文件省略不写,有需要的同学可以根据文章后面的网址下载使用。我的编程环境是IAR,需要自己建立IAR工程。MARK,以后介绍。下面详细介绍(Project和System省略不写,其中System只用了stm8s.h)。

3.1、一切从main.c开始

首先,在main.c中我们需要对用到的单片机的资源进行初始化。我们将其写在All_Config()【在UserApp.c中】函数内,代码如下:

//head file #include "UserApp.h"#include "IO.h"#include "User.h"#include "Time.h"//初始化函数void All_Config( void ){    Clock_Config();    IO_Init();      TIM2_Init();}


其中User.h是我将自己常用的宏写在了一个文件里面,对应于main.c。

在没有接外部时钟的时候,STM8S003F在启动时主时钟默认为HSI RC时钟的8分频,我们这里的初始化仅指定为16MHZ高速内部RC振荡器(HSI),也可以省略不写,Clock_Config()【在UserApp.c中】函数代码如下:

//初始化时钟 选择内部16M晶振void Clock_Config(){    CLK->CKDIVR &= ~( BIT(4) | BIT(3) );}


我选择单片机的PD2作为我的模拟串口的数据发送口,IO_Init()【在IO.c中】函数代码如下:


//head file#include "IO.h"#include "User.h"void IO_Init(){    //TXD:TXD位推挽输出  PD2    UART_PORT->ODR |= UART_PIN_TX; //0000 0000    UART_PORT->DDR |= UART_PIN_TX; //0000 1000    UART_PORT->CR1 |= UART_PIN_TX; //0000 1000      UART_PORT->ODR &= ~UART_PIN_TX;    UART_PORT->ODR |= UART_PIN_TX;}

其中在IO.h中的宏定义为:


#define UART_PORT GPIOD#define UART_PIN_TX 0X04 //  PD2#define UART_PIN_RX 0X08 //  PD3

下面是定时器的初始化,在使用定时器的时候我们分为以下几步:

a、选择定时器,我们选择TIM2

b、定时器的时钟分频(注意要看是使用的默认8分频还是有更改),我开始没有分频,需要16分频;

c、填充定时器自动装载的值,我这里是104

d、开启定时器;

注意计数器CNTR上电自动为0,我们还是清零一下,使用的自动装载寄存器后,自动重装载寄存器决定了定时器的上溢时机,当定时器的计数器中数值达到了自动重装载寄存器规定的值,计数器就要归零。也就是说自动重装载寄存器决定了定时器的周期。

在这篇文章介绍的模拟串口发送数据的方法中并没有使用中断。TIM2_Init()【在TIM2.c中】函数代码如下:

//head file#include "TIM2.h"#include "User.h"void TIM2_Init(){    CLK->PCKENR1 |= CLK_PCKENR1_TIM2;   //TIM2 enable      TIM2->PSCR    = 0x04;               //16分频 1MHZ 1us    TIM2->ARRH    = 104 >> 8;           //自动装载 每52us复位一次TIM2    TIM2->ARRL    = 104;                //每1us递减1<pre name="code" class="cpp">    TIM2->CNTRH = 0;    TIM2->CNTRL = 0; 
TIM2->CR1 |= TIM2_CR1_CEN; //开启定时器}

完成初始化以后,我们应该写发送程序,为了简单的发送,我们选择发送0x55,因为它在示波器中显示方波。我们在UserApp.c中写我们的实现我们的模拟串口的发送。

发送数据的规则是:

a、共10位数据(因此需要16为的数据,8位是不行的),由于我们发送的数据是8位,所以有一个转换的过程;

b、先发低位,再发高位;

c、在发送函数中有时间延时代码,保证没法每一次数据都是一个计数时间(即完成一次104计数)

发送函数SimUART_TxByte()【在UserApp.c中】代码如下

void SimUART_TxByte( u8 SendData ){    static u16 Send_All = 0;    static u8  BitNum   = 0;                //位计数    Send_All = SendData;    Send_All = ( Send_All << 1 ) | 0X0200;  //需要发送的10个位的数据    TIM2->CNTRH = 0;    TIM2->CNTRL = 0;      TIM2->SR1 &= ~TIM2_SR1_UIF;    //先发低位,再发高位    for( BitNum = 0;  BitNum<10 ;  BitNum++ )    {        if( Send_All & 0X0001 )                 //如果是高电平,发高电平        {            UART_PORT->ODR |= UART_PIN_TX;        }        else                                    //如果是低电平,发低电平        {             UART_PORT->ODR &= ~UART_PIN_TX;        }        Send_All >>= 1;                         //右移一位                //等待波特率时间         while( (TIM2->SR1 & TIM2_SR1_UIF) == 0 );        TIM2->SR1 &= ~TIM2_SR1_UIF;    }}

其中UART_PIN_TX为IO.c中的宏定义。

3.2、补全main()函数

完成上面的代码之后我们可以测试一下,下面我们完成main()函数,代码如下:

//head file #include "User.h"#include "UserApp.h"#include "Delay.h"int main( void ){    char Test = 0;    //char Test = 0x55;             //测试数据      All_Config();                   //初始化    while(1)                        //发送循环    {        SimUART_TxByte( Test++ );        Delay_50000();              //延时,防止数据过多    }    //return 0;}

在0x55发送正确之后,我们发送0x00到0xFF,若波特率不正确将会出现发送不连贯的现象。为了防止数据过多,我们需要一个延迟函数,Delay_50000()【IO.C中】函数代码如下:

//Head file#include "Delay.h"void Delay_50000(){    int a,b;    for( a=0 ; a<100 ; a++ )    {        for( b=0 ; b<500 ; b++ );    }}

4、结束语

至此我们使用IO口模拟串口(未使用库函数)发送数据的功能已经实现,相关代码可以移步下面的地址下载使用,欢迎大家和我一起学习和交流。

源代码下载地址:

/***************************************************************************************************************************************************************************************/

2、修改时间:2015.06.17

作者:Alan

说明:1、修正错误:串口数据中,起始位为低电平,停止位为高电平(之前错误的以为停止位为低电平)。

            2、调整文章格式。

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

1、修改时间:2015.06.13

作者:Alan

说明:完成文章。

1 0
原创粉丝点击