小言_互联网的博客

48 STM32普通IO模拟usart串口

238人阅读  评论(0)

1.引言

本次实验的原因是因为最近接触到一款单片机,只有一个串口,但项目中要用到至少2个串口,所以一个串口好少啊,没办法,只能另寻出路,通过普通IO来模拟usart串口了。然后,经过一番资料搜索,代码搬运,终于成功,组合出一个io模拟串口的模块,该模块成功实现了9600-8-N的串口数据收发,为方便记忆以及学习,特此记录!

 

2.普通IO模拟串口原理

普通io模拟串口,也需要严格的遵循串口协议规则,具体的规则可百度一下。

当波特率是115200时,发送1bit数据需要 1/115200 = 8.68us;所以,根据协议每一位数据传输电平持续8.68us

当波特率是9600时,发送1bit数据需要 1/9600= 104us;所以,根据协议每一位数据传输电平持续104us

这里要注意,在115200的时候,因为stm32默认的delay_us的不精准,会导致模拟串口的数据收发错误,在此可以建议用定时器延时,以达到精准操作。

在接收的地方,通过中断来触发启动,然后启动一个定时器,根据波特率的电平持续时间,在电平的中间去检测,是高电平还是低电平,从而确定bit的值。所以,检测到下降沿后延时一下,再启动定时器,定时去判断IO的高低。

 

3.代码实现 

本次使用stm32f103c8t6实验板,所用到的硬件资源如下:

1.GPIO两个

2.外部中断1路

3.定时器两个

io模拟串口头文件:iousart.h


  
  1. #ifndef __IOUSART_H_
  2. #define __IOUSART_H_
  3. #include "stm32f10x.h"
  4. //对应波特率的1个电平持续时间
  5. //(1/9600) = 104us
  6. #define IO_USART_SENDDELAY_TIME 104
  7. enum{
  8. COM_START_BIT,
  9. COM_D0_BIT,
  10. COM_D1_BIT,
  11. COM_D2_BIT,
  12. COM_D3_BIT,
  13. COM_D4_BIT,
  14. COM_D5_BIT,
  15. COM_D6_BIT,
  16. COM_D7_BIT,
  17. COM_STOP_BIT,
  18. };
  19. void iouart1_SendByte(uint8_t uByte);
  20. void iouart1_init(void);
  21. #endif /* __LED_H */

io模拟串口源文件:iousart.c


  
  1. #include <stdio.h>
  2. #include "iousart.h"
  3. #include "delay.h"
  4. //
  5. //io模拟串口-9600-8-N
  6. //定时器2+定时器4+外部中断1路+2个GPIO
  7. //
  8. u8 recvData = 0;
  9. u8 recvStat = COM_STOP_BIT;
  10. //
  11. //用以模拟串口延时
  12. void TIM2_Int_Init(void)
  13. {
  14. TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  15. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
  16. TIM_TimeBaseStructure.TIM_Period = 65535 - 1;
  17. TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1; //设置了用来作为 TIM3 时钟频率除数的预分频值
  18. TIM_TimeBaseStructure.TIM_ClockDivision = 0; //时钟分割为0
  19. TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM3 向上计数模式
  20. TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
  21. TIM_Cmd(TIM2,ENABLE);
  22. }
  23. //用以模拟io串口数据接收计时
  24. void TIM4_Int_Init(void)
  25. {
  26. TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  27. NVIC_InitTypeDef NVIC_InitStructure;
  28. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //时钟使能
  29. //定时器TIM4初始化
  30. TIM_TimeBaseStructure.TIM_Period = IO_USART_SENDDELAY_TIME; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
  31. TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1; //设置用来作为TIMx时钟频率除数的预分频值
  32. TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
  33. TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
  34. TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
  35. TIM_ClearITPendingBit(TIM4, TIM_FLAG_Update);
  36. TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断
  37. //中断优先级NVIC设置
  38. NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //TIM4中断
  39. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; //先占优先级1级
  40. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //从优先级1级
  41. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
  42. NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器
  43. }
  44. //模拟串口1初始化
  45. void iouart1_init(void)
  46. {
  47. GPIO_InitTypeDef GPIO_InitStructure;
  48. NVIC_InitTypeDef NVIC_InitStructure;
  49. EXTI_InitTypeDef EXTI_InitStruct;
  50. RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB, ENABLE); //使能PB,PC端口时钟
  51. //SoftWare Serial TXD
  52. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
  53. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
  54. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
  55. GPIO_Init(GPIOB, &GPIO_InitStructure);
  56. GPIO_SetBits(GPIOB, GPIO_Pin_8);
  57. //SoftWare Serial RXD
  58. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  59. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
  60. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  61. GPIO_Init(GPIOB, &GPIO_InitStructure);
  62. GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource9);
  63. EXTI_InitStruct.EXTI_Line = EXTI_Line9;
  64. EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;
  65. EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling; //下降沿触发中断
  66. EXTI_InitStruct.EXTI_LineCmd=ENABLE;
  67. EXTI_Init(&EXTI_InitStruct);
  68. NVIC_InitStructure.NVIC_IRQChannel= EXTI9_5_IRQn ;
  69. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 3;
  70. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  71. NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
  72. NVIC_Init(&NVIC_InitStructure);
  73. TIM2_Int_Init();
  74. TIM4_Int_Init();
  75. }
  76. void iouart1_TXD(uint8_t option)
  77. {
  78. if( 1 == option)
  79. {
  80. GPIO_SetBits(GPIOB, GPIO_Pin_8);
  81. }
  82. else
  83. {
  84. GPIO_ResetBits(GPIOB, GPIO_Pin_8);
  85. }
  86. }
  87. uint8_t iouart1_RXD(void)
  88. {
  89. return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_9);
  90. }
  91. /***************************************
  92. * 函 数 名: Delay_Ms
  93. * 功能说明: 延时
  94. * 形 参:nTime,单位为uS
  95. * 返 回 值: 无
  96. ****************************************/
  97. void iouart1_delayUs(volatile u32 nTime)
  98. {
  99. u16 tmp;
  100. tmp = TIM_GetCounter(TIM2); //获得 TIM3 计数器的值
  101. if(tmp + nTime <= 65535)
  102. while( (TIM_GetCounter(TIM2) - tmp) < nTime );
  103. else
  104. {
  105. TIM_SetCounter(TIM2, 0); //设置 TIM3 计数器寄存器值为0
  106. while( TIM_GetCounter(TIM2) < nTime );
  107. }
  108. }
  109. /*****************************************
  110. * 函 数 名: iouart1_SendByte
  111. * 功能说明: 模拟串口发送一字节数据
  112. * 形 参:无
  113. * 返 回 值: 无
  114. ******************************************/
  115. void iouart1_SendByte(u8 datatoSend)
  116. {
  117. u8 i, tmp;
  118. // 开始位
  119. iouart1_TXD( 0); //将TXD的引脚的电平置低
  120. iouart1_delayUs(IO_USART_SENDDELAY_TIME);
  121. for(i = 0; i < 8; i++)
  122. {
  123. tmp = (datatoSend >> i) & 0x01;
  124. if(tmp == 0)
  125. {
  126. iouart1_TXD( 0);
  127. iouart1_delayUs(IO_USART_SENDDELAY_TIME); //0
  128. }
  129. else
  130. {
  131. iouart1_TXD( 1);
  132. iouart1_delayUs(IO_USART_SENDDELAY_TIME); //1
  133. }
  134. }
  135. // 结束位
  136. iouart1_TXD( 1); //将TXD的引脚的电平置高
  137. iouart1_delayUs(IO_USART_SENDDELAY_TIME);
  138. }
  139. void EXTI9_5_IRQHandler(void)
  140. {
  141. if(EXTI_GetITStatus(EXTI_Line9) != RESET)
  142. {
  143. if(iouart1_RXD() == 0)
  144. {
  145. if(recvStat == COM_STOP_BIT)
  146. {
  147. recvStat = COM_START_BIT;
  148. //这里要延时一下
  149. iouart1_delayUs( 50);
  150. TIM_Cmd(TIM4, ENABLE);
  151. }
  152. }
  153. EXTI_ClearITPendingBit(EXTI_Line9);
  154. }
  155. }
  156. void TIM4_IRQHandler(void)
  157. {
  158. if(TIM_GetITStatus(TIM4, TIM_FLAG_Update) != RESET)
  159. {
  160. TIM_ClearITPendingBit(TIM4, TIM_FLAG_Update);
  161. recvStat++;
  162. if(recvStat == COM_STOP_BIT)
  163. {
  164. TIM_Cmd(TIM4, DISABLE);
  165. //到这里就接收到完整的一个字节数据
  166. recvData = recvData;
  167. return;
  168. }
  169. if(iouart1_RXD())
  170. {
  171. recvData |= ( 1 << (recvStat - 1));
  172. } else{
  173. recvData &= ~( 1 << (recvStat - 1));
  174. }
  175. }
  176. }

以上代码,测试过是可以的。over!

 


转载:https://blog.csdn.net/Chasing_Chasing/article/details/116458804
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场