STM8 普通IO配置模拟串口输出

 刚接到上级的需求,由于stm8的串口资源较少,需要在原来工程的基础上加多一个io来输出串口数据。串口在每个学习单片机的人来说都是耳熟能详的东西。没有串口基础的同学建议先去学习串口知识点再来阅读。

 

首先我们知道串口数据配置里面包含:波特率、流控、数据起始位、数据位、奇偶校验位、停止位。针对本人经常选的配置为:

波特率115200 bps
流控
数据位8
奇偶校验位0
停止位1

 

 数据起始位默认都是1。数据实体如下:


 由于本人选用波特率为115200 bps ,通过计算器算出每发送一位需要8.68us。这是一个非常低延时的值,这时候就要考虑写一个高精度的短延时的函数。那么问题来了,现在每次一个语句都是以微秒为单位的,任何写多一个语句和写少一个语句都会影响到这个延时函数,计算起来相当复杂。(去年自己写过高精度延时函数,但是是针对stm8在时钟频率是2mhz,且函数参数单位是1.5us的,但是最小延时只能是21us,无法把延时降到8.68us左右当时写出这个延时函数花费1-2天的时间去测试和验证,测试起来比较繁琐和麻烦)

 为了更快更有效的完成这个需求,本人采取扫描方式来找到自己想要的延时时间。前提要先写好io模拟串口驱动,本人在stm8平台上对PD3做串口输出。

普通io模拟串口驱动分别写在了一个c文件和h文件中:

.C:

#include "analog_uart.h"

uint16_t ANALOG_TICK_N = 1;// 115200bps:18 ,9600bps:270

void analog_uart_init(void) {    //初始化
    GPIO_Init(TXD_PORT, TXD_PIN,GPIO_MODE_OUT_PP_HIGH_FAST);
}

inline void bxxx_delay(void) {   //延时值作为波特率需要的延时时间函数
    for(uint16_t i = 0; i < ANALOG_TICK_N ; ++i) {
        asm("nop");
    }
}

static inline void TXD_Write(uint8_t i) { //io 输出高或低电平
    if(i == 0) {
        TXD_PORT->ODR &= (uint8_t)(~TXD_PIN);
    } else {
        TXD_PORT->ODR |= (uint8_t)TXD_PIN;
    }
}

static void sent_byte(uint8_t byte) { // 模拟串口输出1个字节
    uint8_t i = 8;
    TXD_Write(0);
    bxxx_delay();
    while(i--) {
        TXD_Write((uint8_t)(byte&0x01));
        bxxx_delay();
        byte = byte >> 1;
    }
    TXD_Write(1);
    bxxx_delay();
} 

void analog_uart_sent_data(uint8_t * data,uint16_t size) { // 模拟串口输出字符串
    uint16_t i = size;
    while(i--) {
        sent_byte(*data);
        ++data;
        bxxx_delay();
    }
}

void print_tick_num(void) {  //打印延时值出来,通过应用端对延时值做递增,在串口助手查找延时值
    uint8_t buf[10] = "N:";
    uitoa(ANALOG_TICK_N,(char *)buf+2);
    analog_uart_sent_data(buf,strlen((char *)buf));
}

/***
 * Description : 将无符号整数转为字符串.
 * Arguments   : val     待转换的整数.
 *               str     转换后字符串储存的数组指针.
 * Returns     : 返回转换后的指针.
 * Caller      : Application.
 * Notes       : None.
 *******************************************************************************
 */
static char* uitoa(uint32_t val, char *str)
{
    uint32_t power,j;
    char *p = NULL;
    if (str != NULL)
    {
        p = str;
        j = val;
        for (power = 1; j >= 10; j /= 10)
        {
            power *= 10;
        }
        for (; power > 0; power /= 10)
        {
            *p++ = '0' + val / power;
            val %= power;
        }
        *p = '\0';
        p = str;
    }
    return p;
}

.H:

#ifndef ANALOG_UART_H
#define ANALOG_UART_H
#include "stm8s.h"
#include <string.h>



#define TXD_PORT  GPIOD
#define TXD_PIN   GPIO_PIN_3



extern uint16_t ANALOG_TICK_N ;


void analog_uart_init(void); // 初始化
void bxxx_delay(void);
static void sent_byte(uint8_t byte) ;
void analog_uart_sent_data(uint8_t * data,uint16_t size);  //发送串口数据
static char* uitoa(uint32_t val, char *str);
void print_tick_num(void) ;    // 查看延时时间数

#endif

在主函数完成相关操作

#include .....
#include "analog_uart.h"


....


void main(void) {
    /*相关初始化*/
    .....
    analog_uart_init();
    .....
    while(1) {
        print_tick_num();
        analog_uart_sent_data("fang\r\n",6);
        delay_ms(10);
        ANALOG_TICK_N ++;
    }

}

测试结果如下图:

发现,当N等于16或17时,数据是比较稳定,而N等于18的时候数据已经有部分丢失。此时stm8选择LSI内部时钟16Mhz的,当时测试的时候用的是LSI内部时钟4Mhz,结果没找到自己想要的延时。于是选择使用16MhzLSI,就找到延时值为16或17是存在于115200bps中。随着n不断增加,波特率就越低,最后会到一个无法打印出数据的时候。证明已经找不到自己想要的延时时间了。其实这种情况是不容乐观,因为LSI时钟其实并不稳定,单独测试的没问题,把它注入到较大的应用程序常常会部分乱码,因此建议降低波特率做开发,本人最后选择降低到9600bps进行扫描,9600bps算出来约104us。接下来对9600进行扫描

[2019-08-28_21:19:57:972] .:253&angj)aze
[2019-08-28_21:19:58:088] .:254&angj)aze
[2019-08-28_21:19:58:212] .:255fang猧aze? N:25秄ang猧aze? N:257fang?iaze
[2019-08-28_21:19:58:570] N:25竑angjiaze
[2019-08-28_21:19:58:691] N:259fangjiaz?
[2019-08-28_21:19:58:810] N:260fangjiaze
[2019-08-28_21:19:58:931] N:261fangjiaze
[2019-08-28_21:19:59:050] N:262fangjiaze
[2019-08-28_21:19:59:173] N:263fangjiaze
[2019-08-28_21:19:59:294] N:264fangjiaze
[2019-08-28_21:19:59:415] N:265fangjiaze
[2019-08-28_21:19:59:534] N:266fangjiaze
[2019-08-28_21:19:59:655] N:267fangjiaze
[2019-08-28_21:19:59:776] N:268fangjiaze
[2019-08-28_21:19:59:899] N:269fangjiaze
[2019-08-28_21:20:00:019] N:270fangjiaze
[2019-08-28_21:20:00:140] N:271fangjiaze
[2019-08-28_21:20:00:262] N:272fangjiaze
[2019-08-28_21:20:00:384] N:273fangjiaze
[2019-08-28_21:20:00:503] N:274fangjiaze
[2019-08-28_21:20:00:625] N:275fangjiaze
[2019-08-28_21:20:00:747] N:276fangjiaze
[2019-08-28_21:20:00:871] N:277fangjiaze
[2019-08-28_21:20:00:993] N:278fangjiaze
[2019-08-28_21:20:01:114] N:279fangjiaze
[2019-08-28_21:20:01:237] N:280fangjiaze
[2019-08-28_21:20:01:359] N:281fangjiaze
[2019-08-28_21:20:01:480] N:282fangj?aze
[2019-08-28_21:20:01:603] ?:283f?n?jiaz?
[2019-08-28_21:20:01:726] ?z284f??鏹i狷e

当扫描到257时,数据逐渐稳定,此时是比较乐观的,可以看到不少数据是稳定正确,这个时候选择中间值就是最稳定的。因此本人选择270这个值来延时作为串口波特率9600输出数据,这样加入到应用程序中就比较稳定了。

源码例程:https://download.csdn.net/download/fangjiaze444/11633980

 

 

STM32中,通过普通IO模拟串口通信,需要自己编写相关的发送和接收函数来实现数据的处理和解析。下面是一个示例代码,可以供你参考: ```c #include "stm32f1xx.h" #define UART_TX GPIO_PIN_9 // 发送引脚 #define UART_RX GPIO_PIN_10 // 接收引脚 #define BAUDRATE 9600 // 波特率 GPIO_InitTypeDef GPIO_InitStruct; void delay_us(uint32_t us) { uint32_t tickstart = HAL_GetTick(); uint32_t wait = us * (SystemCoreClock / 1000000U); while ((HAL_GetTick() - tickstart) < wait) { } } void UART_GPIO_Init(void) { __HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Pin = UART_TX; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 配置发送引脚为输出模式 GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pin = UART_RX; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 配置接收引脚为输入模式 } void UART_SendByte(uint8_t data) { uint8_t i; HAL_GPIO_WritePin(GPIOA, UART_TX, GPIO_PIN_RESET); // 发送起始位 for (i = 0; i < 8; i++) { // 按位发送数据 if (data & 0x01) { HAL_GPIO_WritePin(GPIOA, UART_TX, GPIO_PIN_SET); } else { HAL_GPIO_WritePin(GPIOA, UART_TX, GPIO_PIN_RESET); } data >>= 1; delay_us(1000000 / BAUDRATE); } HAL_GPIO_WritePin(GPIOA, UART_TX, GPIO_PIN_SET); // 发送停止位 delay_us(1000000 / BAUDRATE); } uint8_t UART_ReceiveByte(void) { uint8_t data = 0; uint8_t i; while (HAL_GPIO_ReadPin(GPIOA, UART_RX) == GPIO_PIN_RESET) { // 等待接收起始位 } delay_us(1000000 / BAUDRATE / 2); // 延时半个波特率时钟周期,等待数据位 for (i = 0; i < 8; i++) { // 按位接收数据 data >>= 1; if (HAL_GPIO_ReadPin(GPIOA, UART_RX) == GPIO_PIN_SET) { data |= 0x80; } delay_us(1000000 / BAUDRATE); } while (HAL_GPIO_ReadPin(GPIOA, UART_RX) == GPIO_PIN_SET) { // 等待接收停止位 } return data; } int main(void) { HAL_Init(); UART_GPIO_Init(); // 初始化GPIO while (1) { UART_SendByte(0xAA); // 发送数据 uint8_t data = UART_ReceiveByte(); // 接收数据 } } ``` 在这个示例代码中,我们使用GPIOA的Pin9和Pin10分别模拟串口的发送和接收。在发送数据时,我们将发送引脚拉低,并按照位的顺序依次将数据输出到发送引脚上。在接收数据时,我们轮询接收引脚的电平状态,按照位的顺序将数据接收下来。需要注意的是,由于使用IO模拟串口的收发,在高速通讯时容易出现误码和数据丢失等问题,因此需要充分测试和优化。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值