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

发布者:RadiantSmile最新更新时间:2020-02-24 来源: eefocus关键字:STM8S003F  IO口  模拟串口  发送数据 手机看文章 扫描二维码
随时随地手机看文章

最近在使用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

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

关键字:STM8S003F  IO口  模拟串口  发送数据 引用地址:STM8S003F使用I/O口模拟串口(一)发送数据

上一篇:STM8S单片机串口调试
下一篇:stm8s中UART的用法(四种UART中断)

推荐阅读最新更新时间:2024-11-08 15:52

STM32CubeMx(Keil5)开发之路——3发送USART数据和printf重定向
运行环境 Windows10 STM32CubeMX___Version 5.0.0 Keil5(MDK5)___Version 5.15 简介 本例程主要讲解如何通过串口发送数据和重定向printf STM32CubeMx基本配置 基础配置过程请参考 STM32CubeMx(Keil5)开发之路—1配置第一个项目 STM32CubeMx USART1配置 1——点击USART1进行设置 2——模式选择Asynchronous异步传输 3——可以看到右边自动出现了Tx和Rx 4——可以自行设置波特率,停止位,校验位等参数 代码修改 1——选择main.c文件 2——在USER CODE中添加如下代码,重定向print
[单片机]
STM32CubeMx(Keil5)开发之路——3<font color='red'>发送</font>USART<font color='red'>数据</font>和printf重定向
多余的引脚怎么办?例如单片机不用IO口
输入口不要悬空,尤其是输入阻抗高的,更不能悬空。例如在CMOS电路中,如果输入口悬空,可能会导致输入电平处于非0和非1的中间状态,这将会使输出级的上下两个推动管同时导通,从而产生很大电流。一般的做法是通过一个电阻(例如10K或者1K)上拉到高电平或者下拉到低电平。而对于不用的运放,则可以将输出端直接接回反向输入端,并把同向输入端接至参考电平点(一般单电源使用时(或者干脆将双电源看成一个单电源),用1/2Vcc作为参考电平,因此有些资料上也会写连接至half supply)。 输出口则可以悬空。对于IO口,一般是将其设置为输入口,并像上面的输入口那样处理。如果是IO口内带上拉电阻的,则可使用内部上拉电阻使其电位固定。不设置成输出口
[单片机]
单片机软件实现模拟串口方法介绍
三种单片机模拟串口方法介绍 模拟串口就是利用51的两个输入输出引脚如P1.0和P1.1,置1或0分别代表高低电平,也就是串口通信中所说的位,如起始位用低电平,则将其置0,停止位为高电平,则将其置1,各种数据位和校验位则根据情况置1或置0。至于串口通信的波特率,说到底只是每位电平持续的时间,波特率越高,持续的时间越短。如波特率为9600BPS,即每一位传送时间为1000ms/9600=0.104ms,即位与位之间的延时为为0.104毫秒。单片机的延时是通过执行若干条指令来达到目的的,因为每条指令为1-3个指令周期,可即是通过若干个指令周期来进行延时的,单片机常用11.0592M的的晶振,现在我要告诉你这个奇怪数字的来历。用此频
[单片机]
STM8S103 IO口控制
STM8的IO口控制,说好听点是灵活多变,可以各种定制,可是用起来那真是烦,要定义的好多! void io_init() { PA_DDR |= (1 3); PA_CR1 |= (1 3); PA_CR2 &= ~(1 3);//PA3设置为推挽输出 PD_DDR |= (1 5); PD_CR1 |= (1 5); PD_CR2 &= ~(1 5); //PD5设置为推挽输出 PD_DDR &= ~(1 6); //PD6设置为悬浮输入 PD_DDR |= (1 3); PD_CR1 |= (1 3); PD_CR2 &= ~(1
[单片机]
stm32 的PA13,PA14, PA15做普通IO口的问题
这两天在玩oled屏,想用几个按键控制舵机,oled显示,于是把三个按键接到了PA13,14,15上发现没有任何反应后来一查手册发现有问题 可以看到PA13口的Main function是JTMS-SWDIO,不是PA13,所以要想使用PA13的普通IO口能力,就要先把IO口的复用功能打开,再把JTMS-SWDIO功能关掉就可以。 做输入,输出口都可以 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable , ENABLE); 再后来又发现了一个问题:就是单步调试的时候不能调试,
[单片机]
51单片机串口接收和发送数据
第一种方法:在中断中处理 typedef unsigned char UINT8; volatile UINT8 u8Uart_Data; void InitialUART0_Timer1() { SCON = 0x50; //2015-05-04 TMOD = 0x20; TH1 = 0XFD; // 9600BPS 2015-05-04 TL1 = 0XFD; TR1 = 1; // start timer0 ES = 1; // Enable serial interrup
[单片机]
udp协议的数据接收与发送的代码
于lwIP协议中的UDP协议,用单片机做一个服务器,接受电脑的指令然后返回数据。以下是我的代码 /**************************************************** *函数功能:初始化udp,选定通信端口,建立连接机制 ****************************************************/ void Udp_Api_init(void) { err_t err; struct udp_pcb *UDPpcb; /* create a new UDP PCB structure */ UDPpcb = udp_new(); if (!UDPpcb)
[单片机]
stm32 串口发送数据第一字节丢失
使用stm32f10x调试串口通讯时,发现一个出错的现象,硬件复位重启之后,发送测试数据0x01 0x02 0x03 0x04..接收端收到的数据为:0x02 0x03 0x04,第一个数据丢失。 查阅stm32f10x参考手册,找到这样一句话: TC:发送完成 当包含有数据的一帧发送完成后,由硬件将该位置位。如果USART_CR1中的TCIE为1,则产生中断。由软件序列清除该位(先读USART_SR,然后写入USART_DR)。TC位也可以通过写入0来清除,只有在多缓存通讯中才推荐这种清除程序。 0:发送还未完成; 1:发送完成。 注意到这一句:由软件序列清除该位(先读USART_SR,然后写入USART_DR)。 也就
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved