从学软件转过来学stm32,看了原子哥的视频,想着自己写一下代码玩一下串口通信。
代码可能有点冗余,请大家多多指教,我用的是HAL库进行编程,并且传输定长数据
python端收发数据:
用的是双线程进行收发利用,线程分别监听键盘和串口接收,当按键按下‘a’键就发送开灯的命令,当按键按下‘s’键就发送关灯命令。
import serial#导入串口通信库
import threading
import keyboard
ser = serial.Serial()
FRAME_HEAD = 0xFF # 帧头
FRAME_SOURCE = 0xF1 # 源地址
FRAME_DEST = 0xF2 # 目的地址
running = True
def port_open_recv():#对串口的参数进行配置
ser.port='com9'
ser.baudrate=115200
ser.bytesize=8
ser.stopbits=1
ser.parity="N"#奇偶校验位
ser.open()
if(ser.isOpen()):
print("串口打开成功!")
else:
print("串口打开失败!")
#isOpen()函数来查看串口的开闭状态
def check_frame(frame):
frame_hex = ''.join([format(byte,'02x') for byte in frame]) # 将接收到的字节流转换为16进制字符串
frame_list = [frame_hex[i:i+2] for i in range(0,len(frame_hex),2)] # 将16进制字符串转换为16进制列表
frame_dom = list(map(lambda x: int(x,16), frame_list)) # 将16进制列表转换为10进制列表
sum_check = 0
if frame_dom[0] == FRAME_HEAD and frame_dom[1] == FRAME_SOURCE and frame_dom[2] == FRAME_DEST: # 判断帧头、源地址、目的地址是否正确
for i in range(len(frame_dom)-2): # 计算校验和
sum_check += frame_dom[i]
if (sum_check&0xff) == frame_dom[-2]: # 判断校验和是否正确
print(frame_hex)
return frame_list[3:-2],True # 返回数据域和校验结果
else:
return None,False
def port_close():
ser.close()
if(ser.isOpen()):
print("串口关闭失败!")
else:
print("串口关闭成功!")
def generate_frame(data):
frame = [FRAME_HEAD, FRAME_SOURCE, FRAME_DEST] # 帧头、源地址、目的地址
frame.extend(list(data)) # 数据域
sum_check = sum(frame) & 0xFF # 校验和取低8位
frame.append(sum_check) # 校验和
frame.append(0xDD) # 帧尾
return frame
def send(send_data):
if(ser.isOpen()):
if(send_data == "open"):
ser.write(bytes(generate_frame(b'0openLED1'))) # 串口发送数据,用bytes类型
elif(send_data == "close"):
ser.write(bytes(generate_frame(b'closeLED1')))
print("发送成功",send_data)
else:
print("发送失败!")
# 创建一个线程来接收串口数据
def recv():
while running: # 如果不用while True,只能接收一次数据
frame = ser.read_until(expected=b'\xDD')
print(frame)
data,flag = check_frame(frame)
data = ''.join(data)
if flag:
result = int(data, 16)
print("接收到的数据为:", data)
print(result)
else:
print("接收到的数据有误!")
def keypress():
def on_press(event): # 定义监听函数
if event.name == 'a':
print("按下了a键")
send("open") # 发送数据
elif event.name == 's':
print("按下了s键")
send("close")
elif event.name == 'q':
port_close() # 关闭串口
exit()
keyboard.on_press(on_press) # 监听键盘输入
keyboard.wait('q') # 等待按键q
# 开两个线程,一个用来接收串口数据,一个用来监听键盘输入
if __name__ == "__main__":
port_open_recv()
t1 = threading.Thread(target=recv)
t2 = threading.Thread(target=keypress)
t2.start()
t1.start()
t1.join()
t2.join()
STM32端:
收发都采用异步中断,用的是USART1,在精英板上就是那个usb口,波特率:115200,8位长度,奇偶校验位,一位停止位,没有硬件流,具体可以在cubeMX上面配置。
usart.c的关键代码:
void SumCheck(uint8_t *frame_send_buff) // 数据和
{
sum_check = 0;
for(uint8_t i=0; i<CHECK_BEGIN;i++)
{
sum_check += frame_send_buff[i];
}
}
void Uint32toByte(uint32_t* data,uint8_t* buffer,uint8_t lenth) // 32位uint转字节
{
uint8_t *byte_arr; // 取数据的首地址,数据以字节存贮,看成一个字节数组
byte_arr = (uint8_t*)data;
for(uint8_t i=0; i<4; i++)
{
buffer[lenth+i] = byte_arr[3-i];
}
}
void Generate_Frame(uint32_t data,uint8_t* buffer) // 生成帧
{
buffer[0] = FRAME_HEAD;
buffer[1] = FRAME_SORCE;
buffer[2] = FRAME_DEST;
Uint32toByte(&data,frame_send_buff,DATA_BEGIN);
SumCheck(frame_send_buff);
buffer[CHECK_BEGIN] = sum_check;
buffer[CHECK_BEGIN+1] = 0xDD;
}
void SendFrame(uint32_t data) // 发送,中断发送
{
Generate_Frame(data,frame_send_buff);
HAL_UART_Transmit_IT(&huart1,frame_send_buff,SEND_LEN);
}
// 中断服务函数
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&huart1); // 调用HAL库的串口处理函数,内部会失能中断,接收数据后不会再次进入中断
HAL_UART_Receive_IT(&huart1,(uint8_t*)g_rx_buffer,14); // 继续调用异步接收函数
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) // 回调函数
{
if(huart->Instance == USART1) // 是否USART1
{
for (uint8_t i=0;i<14;i++)
{
frame[i] = g_rx_buffer[i];
}
receive_flag = 1;
}
}
然后在usart.h里面声明:
extern uint8_t frame[14];
extern uint8_t receive_flag;
/* USER CODE END Includes */
extern UART_HandleTypeDef huart1;
/* USER CODE BEGIN Private defines */
#define FRAME_HEAD 0xFF // 帧头
#define FRAME_SORCE 0xF1 // 帧源地址
#define FRAME_DEST 0xF2 // 帧目的地址
#define DATA_BEGIN 3
#define CHECK_BEGIN (DATA_BEGIN+4)
#define SEND_LEN (CHECK_BEGIN+2)
void SendFrame();
在main.c中:
uint32_t F = 1000000;
const uint8_t order1[9] = "0openLED1";
const uint8_t order2[9] = "closeLED1";
void distinct(uint8_t *arr) // 这个函数就是接收之后用来干嘛干嘛的,写的有点冗余
{
uint8_t open = 0;
uint8_t ok = 1;
if (arr[0] == order1[0])
{
for(uint8_t i=0;i<9;i++)
{
if(arr[i] != order1[i])
ok = 0;
open = 1;
}
}
if (arr[0] == order2[0])
{
for(uint8_t i=0;i<9;i++)
{
if(arr[i] != order2[i])
ok = 0;
open = 0;
}
}
if(ok && open)
{
HAL_GPIO_WritePin(LED0_GPIO_Port,LED0_Pin,GPIO_PIN_RESET); // 开灯
}
else if(ok && !open)
{
HAL_GPIO_WritePin(LED0_GPIO_Port,LED0_Pin,GPIO_PIN_SET); // 关灯
}
}
uint8_t FrameSum() // 对接收的数据进行求和,取低八位
{
uint8_t sum = 0;
for(uint8_t i=0;i<12;i++)
{
sum += frame[i];
}
return sum;
}
void frameTodata(uint8_t *data) // 将接收的帧转为数据
{
uint8_t head = 0xff;
uint8_t source = 0xF1;
uint8_t dist = 0xF2;
uint8_t lenth = 0;
if(frame[1]==source && frame[2]==dist && frame[0]==head) // 判断帧头,源地址,目的地址
{
if (FrameSum() == frame[12]) // 判断校验和是否正确
{
for(uint8_t i=3;i<12;i++) // 取出数据域
{
data[i-3] = frame[i];
}
}
}
}
int main()
{
uint8_t data[14];
uint8_t s = 0;
while (1)
{
s = KEY_Scan(0);
if(s==1)
{
HAL_GPIO_TogglePin(LED1_GPIO_Port,LED0_Pin);
SendFrame(F); // 按下按键发送数据
}
else if(s == 2)
{
if(F <30000000)
F += 1000000;
}
if (receive_flag) // 数据接收完成
{
frameTodata(data); // 提取数据域
distinct(data); // 对数据进行分析
}
}
}
上面的都是一些关键的代码,key.c可以自己配置,LED0和LDE1的相关配置都是用CubeMX自动生成的。
总的来说定长数据的发送并不难,STM32我才入门一个星期,代码是没有那么老练的,希望大家包容指正