STM32F103配置
1-0 串口配置
void uart_init(u32 bound){//GPIO端口设置GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //使能USART1时钟RCC_APB2PeriphResetCmd(RCC_APB2Periph_USART1, ENABLE); //复位串口1RCC_APB2PeriphResetCmd(RCC_APB2Periph_USART1, DISABLE);RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA时钟//USART1_TX GPIOA.9GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //TX PA.9GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9//USART1_RX GPIOA.10初始化GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//RX PA.10GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10 //Usart1 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 3 ;//抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //子优先级1NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器//USART 初始化设置USART_InitStructure.USART_BaudRate = bound;//串口波特率USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式USART_Init(USART1, &USART_InitStructure); //初始化串口1// USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断 ???USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); // 空闲中断,打开接收中断。USART_Cmd(USART1, ENABLE); //使能串口1 usart1_dma_tx_init();usart1_dma_rx_init();
}
1-1 DMA发送模式配置
static void usart1_dma_tx_init(void)
{DMA_InitTypeDef DMA_InitStructure;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);DMA_DeInit(DMA1_Channel4);DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;DMA_InitStructure.DMA_MemoryBaseAddr = (u32)usart1_tx_buff;DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;DMA_InitStructure.DMA_BufferSize = 0;/*避免初始化发送数据,设置为0*/DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;DMA_InitStructure.DMA_Priority = DMA_Priority_High;DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;DMA_Init(DMA1_Channel4, &DMA_InitStructure);USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);DMA_Cmd(DMA1_Channel4, ENABLE);
}
1-2 通过DMA传输数据到USART1的发送寄存器
// 用于通过DMA传输数据到USART1的发送寄存器
uint8_t usart1_dma_tx_data(void* buffer, uint16_t data_lenth)
{if(data_lenth < 1)// 判断长度是否有效{return ERROR;}while (DMA_GetCurrDataCounter(DMA1_Channel4));// 检查DMA发送通道内是否还有数据if(NULL == buffer)//指针判空{return ERROR;}USART1_state.txc = RESET; // 表示发送未完成。// 使用memcpy函数将buffer中的数据复制到usart1_tx_buff中。// 复制的长度为data_lenth和USART1_TX_BUFF_MAX_LENTH中较小的值。这是为了防止数据溢出。memcpy(usart1_tx_buff, buffer,((data_lenth > USART1_TX_BUFF_MAX_LENTH) ? USART1_TX_BUFF_MAX_LENTH:data_lenth));DMA_Cmd(DMA1_Channel4, DISABLE); //DMA发送数据-要先关 ,以确保设置发送长度之前DMA传输已经停止。DMA_SetCurrDataCounter(DMA1_Channel4, data_lenth);// 设置DMA1_Channel4的发送长度为data_lenth。DMA_Cmd(DMA1_Channel4, ENABLE);// 启用DMA1_Channel4,以启动DMA传输return SUCCESS;
}
1-3 串口数据发送
将usart1_dma_tx_data()函数放在main函数中或者中断处理函数中即可,如下所示:
void main()
{uint8_t data_array[8];data_array[0] = 0x12;data_array[1] = 0x34;data_array[2] = (int)Encoder & 0xFF;data_array[3] = ((int)Encoder >> 8) & 0xFF;data_array[4] = (int)Adc & 0xFF;data_array[5] = ((int)Adc >> 8) & 0xFF;data_array[6] = 0x56;data_array[7] = 0x78;usart1_dma_tx_data(data_array, 8);
}
2-1 DMA接收模式配置
#define USART1_RX_BUFF_MAX_LENTH 200 //定义最大接收字节数 200
#define USART1_TX_BUFF_MAX_LENTH 200
static void usart1_dma_rx_init(void)
{DMA_InitTypeDef DMA_InitStructure;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);DMA_DeInit(DMA1_Channel5);DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR; // 初始化外设地址DMA_InitStructure.DMA_MemoryBaseAddr = (u32)usart1_rx_buff; // 缓存地址DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // 外设作为数据来源DMA_InitStructure.DMA_BufferSize = USART1_RX_BUFF_MAX_LENTH; // 缓存容量DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址不递增DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存递增DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;// 外设字节宽度DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 内存字节宽度DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // 正常模式,即满了就不在接收了,而不是循环存储DMA_InitStructure.DMA_Priority = DMA_Priority_High; // 优先级很高DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // 内存与外设通信,而非内存到内存DMA_Init(DMA1_Channel5, &DMA_InitStructure);USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);DMA_Cmd(DMA1_Channel5, ENABLE);
}
2-2 串口结束中断
void USART1_IRQHandler(void)
{if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)/*空闲中断接收*/{u8 data_lenth = 0 ;DMA_Cmd(DMA1_Channel5, DISABLE);// 关闭DMA ,防止干扰USART_ReceiveData( USART1 );data_lenth = USART1_RX_BUFF_MAX_LENTH - DMA_GetCurrDataCounter(DMA1_Channel5);//获得接收到的字节数DMA_SetCurrDataCounter(DMA1_Channel5, USART1_RX_BUFF_MAX_LENTH);// 重新赋值计数值,必须大于等于最大可能接收到的数据帧数目DMA_Cmd(DMA1_Channel5, ENABLE);USART1_state.rxc = SET; //设置接收完成标志my_usmart_scan(usart1_rx_buff,data_lenth); //配置自己的接收处理函数//执行usmart扫描 memset(usart1_rx_buff, 0, USART1_RX_BUFF_MAX_LENTH); //清空接收缓存区 USART_ClearITPendingBit(USART1, USART_IT_IDLE); // Clear IDLE interrupt flag bit}if(USART_GetITStatus(USART1,USART_IT_TC) != RESET) //发送完成标记{DMA_Cmd(DMA1_Channel4, DISABLE); // 关闭DMADMA_SetCurrDataCounter(DMA1_Channel4,RESET); // 清除数据长度USART1_state.txc = SET; // 设置发送完成标志USART_ClearITPendingBit(USART1, USART_IT_TC); // 清除完成标记}
}
2-3 对串口接收的数据进行处理
void my_usmart_scan(uint8_t * data_array,uint8_t data_lenth)
{if(SET == USART1_state.rxc)//串口接收完成标志位{ if(data_array[0] == 0x2D && data_array[1] == 0x01 && data_array[5] == 0x56 && data_array[6] == 0x78) // 判断帧头1和2是否正确、帧尾1和2是否正确 {float value = 0;int16_t sign = 1;if(data_array[2] == 0x45) // 符号位判断{sign = -1;}value = (data_array[4] << 8) + data_array[3]; action = sign * value; // 接收的数据} USART1_state.rxc = RESET;//状态寄存器清空 }
}
python上位机程序
# 从串口接收的数据为:编码器(整型)、角位移传感器(浮点型)
def read_serial_one_data_encoder_adc(ser):global receive_resultBUF_SIZE = 8buf = bytearray([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])c1 = ib = flag = 0while True:R = ser.read(1)# print("data", R)if R == b'':print("Read Fail")ser.close()breakc = int.from_bytes(R, byteorder='big')# print("data", c)if flag > 0:if ib < BUF_SIZE:buf[ib] = cib += 1if ib == 8:if buf[6] == 0x56 and buf[7] == 0x78:Encoder = (buf[3] << 8) + buf[2]Adc= (buf[5] << 8) + buf[4]receive_result = [Encoder, Adc]breakelse:print("CRC Fail")flag = 0if flag == 0:if c1 == 0x12 and c == 0x34:flag = 1ib = 2c1 = creturn receive_resultdef run_play():result = [0, 0]action = bytearray([0x12, 0x34, 0x00, 0x00, 0x00, 0x56, 0x78])ser = serial.Serial( # 下面这些参数根据情况修改port='COM8', # 串口baudrate=921600, # 波特率timeout=None,parity=serial.PARITY_ODD, #stopbits=serial.STOPBITS_ONE,bytesize=8)if ser.isOpen(): # 判断串口是否打开print("open success")for i in range(1000000):result2 = read_serial_one_data_encoder_adc(ser) # /************从串口接收数据函数**************/print("result2=",result)ser.write(action) # 使用DMA需要多个字节一起发送 /************往串口发送数据函数**************/if __name__ == '__main__':run_play()
3 完整程序
#include "sys.h"
#include "usart.h"
#include <string.h>
#include <math.h>
#include <stdio.h>
uint16_t Res;
float action;
uint16_t check_flag;#define USART1_RX_BUFF_MAX_LENTH 200 //定义最大接收字节数 200
#define USART1_TX_BUFF_MAX_LENTH 200uint8_t ReceiveBuff[RECEIVEBUFF_SIZE];
static uint8_t usart1_tx_buff[USART1_TX_BUFF_MAX_LENTH];
uint8_t usart1_rx_buff[USART1_RX_BUFF_MAX_LENTH];typedef struct
{uint8_t rxc;uint8_t txc;
}uart_state_t;uart_state_t USART1_state;//
//如果使用ucos,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS
#include "includes.h" //ucos 使用
#endif //
//加入以下代码,支持printf函数,而不需要选择use MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{ int handle; }; FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{ x = x;
}
重定义fputc函数
//int fputc(int ch, FILE *f)
//{
// while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
// USART1->DR = (u8) ch;
// return ch;
//}
#endif
#if EN_USART1_RX //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误
u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效字节数目
u16 USART_RX_STA=0; //接收状态标记 void uart_init(u32 bound){//GPIO端口设置GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //使能USART1时钟RCC_APB2PeriphResetCmd(RCC_APB2Periph_USART1, ENABLE); //复位串口1RCC_APB2PeriphResetCmd(RCC_APB2Periph_USART1, DISABLE);RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA时钟//USART1_TX GPIOA.9GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //TX PA.9GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9//USART1_RX GPIOA.10初始化GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//RX PA.10GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10 //Usart1 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 3 ;//抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //子优先级1NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器//USART 初始化设置USART_InitStructure.USART_BaudRate = bound;//串口波特率USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式USART_Init(USART1, &USART_InitStructure); //初始化串口1// USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断 ???USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); // 空闲中断,打开接收中断。USART_Cmd(USART1, ENABLE); //使能串口1 usart1_dma_tx_init();usart1_dma_rx_init();
}static void usart1_dma_tx_init(void)
{DMA_InitTypeDef DMA_InitStructure;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);DMA_DeInit(DMA1_Channel4);DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;DMA_InitStructure.DMA_MemoryBaseAddr = (u32)usart1_tx_buff;DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;DMA_InitStructure.DMA_BufferSize = 0;/*避免初始化发送数据,设置为0*/DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;DMA_InitStructure.DMA_Priority = DMA_Priority_High;DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;DMA_Init(DMA1_Channel4, &DMA_InitStructure);USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);DMA_Cmd(DMA1_Channel4, ENABLE);
}
// 用于通过DMA传输数据到USART1的发送寄存器
uint8_t usart1_dma_tx_data(void* buffer, uint16_t data_lenth)
{if(data_lenth < 1)// 判断长度是否有效{return ERROR;}while (DMA_GetCurrDataCounter(DMA1_Channel4));// 检查DMA发送通道内是否还有数据if(NULL == buffer)//指针判空{return ERROR;}USART1_state.txc = RESET; // 表示发送未完成。// 使用memcpy函数将buffer中的数据复制到usart1_tx_buff中。// 复制的长度为data_lenth和USART1_TX_BUFF_MAX_LENTH中较小的值。这是为了防止数据溢出。memcpy(usart1_tx_buff, buffer,((data_lenth > USART1_TX_BUFF_MAX_LENTH) ? USART1_TX_BUFF_MAX_LENTH:data_lenth));DMA_Cmd(DMA1_Channel4, DISABLE); //DMA发送数据-要先关 ,以确保设置发送长度之前DMA传输已经停止。DMA_SetCurrDataCounter(DMA1_Channel4, data_lenth);// 设置DMA1_Channel4的发送长度为data_lenth。DMA_Cmd(DMA1_Channel4, ENABLE);// 启用DMA1_Channel4,以启动DMA传输return SUCCESS;
}static void usart1_dma_rx_init(void)
{DMA_InitTypeDef DMA_InitStructure;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);DMA_DeInit(DMA1_Channel5);DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR; // 初始化外设地址DMA_InitStructure.DMA_MemoryBaseAddr = (u32)usart1_rx_buff; // 缓存地址DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // 外设作为数据来源DMA_InitStructure.DMA_BufferSize = USART1_RX_BUFF_MAX_LENTH; // 缓存容量DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址不递增DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存递增DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;// 外设字节宽度DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 内存字节宽度DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // 正常模式,即满了就不在接收了,而不是循环存储DMA_InitStructure.DMA_Priority = DMA_Priority_High; // 优先级很高DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // 内存与外设通信,而非内存到内存DMA_Init(DMA1_Channel5, &DMA_InitStructure);USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);DMA_Cmd(DMA1_Channel5, ENABLE);
}void USART1_IRQHandler(void)
{if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)/*空闲中断接收*/{u8 data_lenth = 0 ;DMA_Cmd(DMA1_Channel5, DISABLE);// 关闭DMA ,防止干扰USART_ReceiveData( USART1 );data_lenth = USART1_RX_BUFF_MAX_LENTH - DMA_GetCurrDataCounter(DMA1_Channel5);//获得接收到的字节数DMA_SetCurrDataCounter(DMA1_Channel5, USART1_RX_BUFF_MAX_LENTH);// 重新赋值计数值,必须大于等于最大可能接收到的数据帧数目DMA_Cmd(DMA1_Channel5, ENABLE);USART1_state.rxc = SET; //设置接收完成标志my_usmart_scan(usart1_rx_buff,data_lenth); //配置自己的接收处理函数//执行usmart扫描 memset(usart1_rx_buff, 0, USART1_RX_BUFF_MAX_LENTH); //清空接收缓存区 USART_ClearITPendingBit(USART1, USART_IT_IDLE); // Clear IDLE interrupt flag bit}if(USART_GetITStatus(USART1,USART_IT_TC) != RESET) //发送完成标记{DMA_Cmd(DMA1_Channel4, DISABLE); // 关闭DMADMA_SetCurrDataCounter(DMA1_Channel4,RESET); // 清除数据长度USART1_state.txc = SET; // 设置发送完成标志USART_ClearITPendingBit(USART1, USART_IT_TC); // 清除完成标记}
}///********************************************************
//Function: void my_usmart_scan(uint8_t * data_array,uint8_t data_lenth)
//Description:处理接收数据
//Input:
//Output:
//Others:
//*********************************************************/
void my_usmart_scan(uint8_t * data_array,uint8_t data_lenth)
{if(SET == USART1_state.rxc)//串口接收完成标志位{ if(data_array[0] == 0x2D && data_array[1] == 0x01 && data_array[5] == 0x56 && data_array[6] == 0x78) // 判断帧头是否正确、判断奇偶校验位是否正确 || USART_RX_BUF[1] == check_flag{float value = 0;int16_t sign = 1;if(data_array[2] == 0x45){sign = -1;}value = (data_array[4] << 8) + data_array[3]; action = sign * value;} USART1_state.rxc = RESET;//状态寄存器清空 }
}