1、流水灯的实现
流水灯的实现分三两步
第一步:初始化GPIO时钟,使用库函数将GPIOx的时钟调为ENABLE(允许)状态。
第二步:初始化GPIO输出模式,有三个需要调节的,第一个是Mode(模式),输出模式分推挽输出、开漏输出、复用推挽输出、复用开漏输出,我们选用推挽输出即可;第三个是输出频率,有2M、10M和50M,这里选50M。
第三步:编写代码控制端口输出,控制LED亮灭。
具体原理可以看上篇寄存器控制LED,这里使用的是标准库,就是封装好的函数使用更方便。
代码如下:
LED.c
#include "stm32f10x.h" // Device header
#include "delay.h"
void LED_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //初始化GPIOA时钟
GPIO_InitTypeDef GPIO_InitStructure; //初始化输出模式
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_ResetBits(GPIOA, GPIO_Pin_0); //设置初始为低电平
}
void LED_ON(void) //灯亮
{
GPIO_SetBits(GPIOA, GPIO_Pin_0);
}
void LED_OFF(void) //灯灭
{
GPIO_ResetBits(GPIOA, GPIO_Pin_0);
}
void LED222(void) //流水灯
{
GPIO_SetBits(GPIOA, GPIO_Pin_0);
Delay_ms(500);
GPIO_ResetBits(GPIOA, GPIO_Pin_0);
GPIO_SetBits(GPIOA, GPIO_Pin_1);
Delay_ms(500);
GPIO_ResetBits(GPIOA, GPIO_Pin_1);
GPIO_SetBits(GPIOA, GPIO_Pin_2);
Delay_ms(500);
GPIO_ResetBits(GPIOA, GPIO_Pin_2);
GPIO_SetBits(GPIOA, GPIO_Pin_3);
Delay_ms(500);
GPIO_ResetBits(GPIOA, GPIO_Pin_3);
GPIO_SetBits(GPIOA, GPIO_Pin_4);
Delay_ms(500);
GPIO_ResetBits(GPIOA, GPIO_Pin_4);
}
LED.h
#ifndef __LED_H
#define __LED_H
void LED_Init(void);
void LED_ON(void);
void LED_OFF(void);
void LED222(void);
#endif
main.c
#include "stm32f10x.h" // Device header
#include "LED.h"
int main(void)
{
LED_Init(); //初始化LED
while (1)
{
LED222(); //流水灯
}
}
上述代码使用的是GPIOA的Pin0、1、2、3、4依次置高低电平。
效果如下:
2、串口通信输出
单片机能够向单片机发送信息,单片机能够向计算机发送信息,计算机也能向单片机发送信息,使用串口通信技术便能实现这些操作。
1.单片机和计算机发送信息是如何实现的?
单片机有TXD和RXD,TXD能够发出一串电平信号,RXD能够接收一段电平信号,所以不同单片机想要相互发送信息,其中一个单片机的TXD要接到另一个单片机的RXD上,这样便能实现信息的传输。
但是实际操作电平容易受到外界环境的干扰,在发送信息时我们又该如何确定什么时候开始、什么时候停止发送。这些问题都是通信技术需要解决的问题。
为了实现判断发送信息的开始与停止,我们采用了起始位和终止位,一般起始位是用来捕捉开始信号,配合终止位用来判断信息是否错误,其中判断错误信息有奇偶校验等。为了降低外界的干扰我们一般除了TXD和RXD端口还有GND,GND在电学上一般代表是0v,在这里更多的是确保两个单片机的GND相同,以减下两个单片机电平差误差。
常使用的有TTL电平,它的电压范围是0~5v,这个电压范围较小,电压在这个范围容易被影响,RS232电平是-25~25v,比起TTL它的高电平低电平所占的电压区间更大,更不容易受到环境影响。除了这些以外还有差动电压,用电压差来发送信号,更加稳定。
2.使用STM32串口通信输出信息到计算机上
这里我们使用STM32发送信息"Hello Windows!"到计算机上,计算机使用串口助手软件去接收它。
除了单片机之外我们还要准备USB TO TTL硬件,TTL连接到单片机上,USB连接在电脑上。
传输信息方式有同步和异步,同步传输信息是发送者发送信号的频率和接收者接收信号的频率一样,我们设置相同的波特率即可实现。异步传输需要判断起始位和终止位。
USART1输出端口TXD在GPIOA_P9上,接收端口RXD在GPIOA_P10上,我们可以查看STM32引脚图。
实现代码如下:
Serial.c
#include "stm32f10x.h" // Device header
#include <stdio.h>
#include <stdarg.h>
void Serial_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);//单片机输出口初始化
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 9600; //波特率为9600
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx; //打开发送
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1, &USART_InitStructure);
USART_Cmd(USART1, ENABLE);
}
void Serial_SendByte(uint8_t Byte)//发送字节
{
USART_SendData(USART1, Byte);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
void Serial_SendString(char *String)//发送字符
{
uint8_t i;
for (i = 0; String[i] != '\0'; i ++)
{
Serial_SendByte(String[i]);
}
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "Serial.h"
int main(void)
{
Serial_Init();
Serial_SendString("Hello Windows!\n");
while (1)
{
}
}
上述代码实现了将Hello Windows传输。
我们打开串口调试软件,效果如下:
3、使用STM32串口通信实现接收判断字符点亮LED
题目:STM32以查询方式接收上位机(win10)串口发来的数据,如果接收到“Y”则点亮链接到stm32上的一个LED灯;接收到“N”则熄灭LED灯。
Serial.c
#include "stm32f10x.h" // Device header
#include <stdio.h>
#include <stdarg.h>
void Serial_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);//单片机输出口初始化
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);//单片机输入口初始化
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1, &USART_InitStructure);
USART_Cmd(USART1, ENABLE);
}
void Serial_SendByte(uint8_t Byte)
{
USART_SendData(USART1, Byte);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
void Serial_SendArray(uint8_t *Array, uint16_t Length)
{
uint16_t i;
for (i = 0; i < Length; i ++)
{
Serial_SendByte(Array[i]);
}
}
void Serial_SendString(char *String)
{
uint8_t i;
for (i = 0; String[i] != '\0'; i ++)
{
Serial_SendByte(String[i]);
}
}
我们在对比上面输出的c文件,发现我们多了初始化了GPIOA_Pin10(也就是RXD)和设置USART_Mode多了RX,这样接受口就打开了。
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "LED.h"
#include "Serial.h"
uint8_t RxData;
int main(void)
{
Serial_Init();
LED_Init();
Serial_SendString("Hello Windows!\n");
while (1)
{
if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==SET)
{
RxData=USART_ReceiveData(USART1);
Serial_SendByte(RxData);
if(RxData==89)
{LED_ON();
Serial_SendByte(89);
}
else if(RxData==78)
{LED_OFF();
Serial_SendByte(78);}
}
}
}
我们只需判断接收位,如果接收位为1,就判断接收字符是否等于Y或者N,LED的源文件和上面流水灯的文件是一样的。
这里计算机依次发送了Y、N、S三个字符,代码的逻辑是,在发送区发送了什么字符,那么单片机接收后就会再传回计算机,如果该字符是Y或者N,那么就会输出两遍Y或者N到计算机,这样我们就能知道LED是否亮灭。
拍摄器材受限,我这里只拍摄了在计算机内发出了Y字符,可以看到发送了Y字符后单片机接收到后LED点亮。