基于STM8的模拟串口接收与发送
本帖最后由 电子Inc 于 2015-1-1 15:45 编辑前言:在我们日常生活中,为了减少成本,在通信中往往使用串口通信,而我们使用到的单片机,也集成了USART这一模块,但是, 当需要到多个串口工作时,这单片机集成的1个或者两个串口
却远远不够需求,所以,我们得使用模拟得手段来给单片机实现多个串口。
这里,我们使用STM8来完成这一实验:
1、串口通信的格式:起始位(1)------数据位(8位/7位)-------校验位(0/1)--------停止位(1)
因为我们很少用到校验位,所以,校验位可有可无
2、波特率, 意思是每秒发送多少个bit,单位是 bit/s, 我们常用bps来表示, 例如 9600bps, 4800bps, 2400bps
3、采样频率, 为了保持精度,减少接收数据和发送数据的误差,我们把采样次数增多,利用多次采样次数中的固定位置采样
思路:以STM8位基准,我们设定此时的波特路为9600bps,利用定时器中断产生时间间隔, 1秒传送9600bit相当于 1bit需要104us,而我们这里采样次数取4次,定时时间是26us
PS:因为STM8使用内部晶振,难免出现误差,所以在现实使用中,如果使用26us间隔读取,往往出现数据错误,在实际应用中,我采用24~25us的间隔,数据接收发送正确
实验工具:STM8S103FP6最小系统版, ST-linkV2, PL2303转串口模块
步骤1、算出定时初值, STM8内部晶振为16M, 采用8分频后震荡周期 =8/16 = 0.5us,所以发送或接受以为数据的采样定时器初值应为48~52;
下面为定时器初始化代码:void Tim2_Init(void)
{
TIM2_DeInit();
TIM2_TimeBaseInit(TIM2_PRESCALER_8, 48);
TIM2_ClearFlag(TIM2_FLAG_UPDATE);
TIM2_ITConfig(TIM2_IT_UPDATE, ENABLE);
TIM2_Cmd(ENABLE);
}
步骤2、RXD和TXD初始化,RXD为上拉输入,TXD位推挽输出高电平
#define Uart_RXD_Init() GPIO_Init(GPIOD,GPIO_PIN_6,GPIO_MODE_IN_PU_NO_IT)
#define Uart_RXD_Status() GPIO_ReadInputPin(GPIOD,GPIO_PIN_6)
#define Uart_TXD_Init() GPIO_Init(GPIOD,GPIO_PIN_5,GPIO_MODE_OUT_PP_HIGH_FAST)
#define Uart_TXD_Out_HIGH() GPIO_WriteHigh(GPIOD,GPIO_PIN_5)
#define Uart_TXD_Out_LOW() GPIO_WriteLow(GPIOD,GPIO_PIN_5)步骤3、中断调用函数
void Uart_Interrupt(void)
{
uint8_t i;
if(1 == flag_rxd_finish)
{
flag_rxd_finish = 0;
flag_rxd_txd = 0;
for(i = 0;i < TxXKCnt; i++)
{
UartTxBuff = UartRxBuff;
}
}
if(1 == flag_rxd_txd) // 为1,接收
{
Uart_Rx_Byte();
}
else // 为0,发送
{
Uart_Tx_Byte();
}
}
步骤4、接收函数
static void Uart_Rx_Byte(void)
{
static uint8_t RxBitNum; //接收位计数
static uint8_t OverTime; //接收超时计时器
static uint8_t RxByteBuff; //一字节数据接收缓存
if(1 == Uart_RXD_Status())
{
OverTime++;
}
else
{
OverTime = 0;
}
if(OverTime > 44)
{
OverTime = 0;
RxByteIndex = 0;
RxBitNum = 0;
}
if((1 == Uart_RXD_Status()) && (0 == RxBitNum))
{
RxSampFreq = 0;
}
else
{
RxSampFreq++;
}
if(1 == RxSampFreq)
{
if(0 == RxBitNum)
{
if(0 == Uart_RXD_Status())
{
RxByteBuff = 0;
RxBitNum++;
}
}
else if((RxBitNum > 0) && (RxBitNum < 9))
{
if(1 == Uart_RXD_Status())
{
RxByteBuff = RxByteBuff | (1 << (RxBitNum - 1));
}
RxBitNum++;
}
else if(9 == RxBitNum)
{
if(1 == Uart_RXD_Status())
{
RxBitNum = 0;
if(0x0d != RxByteBuff && 0x0a != RxByteBuff)
{
UartRxBuff = RxByteBuff;
RxByteIndex++;
if(RxByteIndex > 63)
{
RxByteIndex = 0;
}
}
else
{
TxXKCnt = RxByteIndex;
RxByteIndex = 0;
flag_rxd_finish = 1;
}
}
}
else
{
RxBitNum = 0;
}
}
else if(RxSampFreq > 3)
{
RxSampFreq = 0;
}
}步骤5、发送函数
static void Uart_Tx_Byte(void)
{
static bool SendFinish = 1; //发送完成标志
static u8 TxSampFreq = 0; //发送计数 采样4次
static u8 BitNum = 0; //位计数
static u8 ByteLock; //发送字节锁定(防止在发送途中 发送数字被改变)
static u8 TxIndex = 0; //当前发送索引
if(SendFinish)
{
SendFinish = 0;
RxSampFreq = 0;
BitNum = 0;
if(TxIndex < TxXKCnt) //控制发送的字节
{
ByteLock = UartTxBuff;
TxIndex++;
RxByteIndex = 0;
}
else if(TxIndex == TxXKCnt)
{
ByteLock = '\n';
TxIndex++;
RxByteIndex = 0;
}
else
{
flag_rxd_txd = 1;
SendFinish = 0;
TxIndex = 0;
}
}
if(++TxSampFreq > 3)
{
if(BitNum == 0) //起始位
{
Uart_TXD_Out_LOW();
BitNum++;
}
else if((BitNum >0) && (BitNum < 9)) //数据位
{
if(0x01 & (ByteLock >> (BitNum-1))) //先发低位
{
Uart_TXD_Out_HIGH();
}
else
{
Uart_TXD_Out_LOW();
}
BitNum++;
}
else if(BitNum == 9) //结束码
{
Uart_TXD_Out_HIGH();
SendFinish = 1;
BitNum = 0;
}
TxSampFreq = 0;
}
}
非常漂亮。我仔细看了这个思路,利用定时器控制时序应该比delay延时更加精确,果断加精。 jianhong_wu 发表于 2015-1-1 18:01
非常漂亮。我仔细看了这个思路,利用定时器控制时序应该比delay延时更加精确,果断加精。
基本我对delay产生非常大的厌恶感,如非必要,绝对不用,我的裸编程思想都是:前后台
页:
[1]