python与STM32F103串口通信

从学软件转过来学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我才入门一个星期,代码是没有那么老练的,希望大家包容指正

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值