写在前面
我们的购物机器人要完成识别,控制,抓取等动作的话,仅靠一块嵌入式芯片是远远不够的,因此我们的购物机器人还搭载了树莓派。我将在这篇博客里面分享树莓派和嵌入式芯片(以STM32F427IIH6为例)通过DMA+USART进行通信的方法。
树莓派
树莓派端我们需要找到USB端口设备,我的树莓派上是CHASSIS_SERIAL_PORT = '/dev/ttyUSB0'
,获得了端口设备之后就可以开始调库了,这也是python语言的拿手好戏。这次我们用的库是serial
,我们需要这个库里的函数
- 串口初始化:
self.port_chassis = serial.Serial(CHASSIS_SERIAL_PORT, 9600, timeout=1)
- 发送数据:
self.port_chassis.write((direction + str(distance)).encode())
- 读取数据:
self.port_chassis.readline().decode()
- 流初始化:
self.port_chassis.flushInput();self.port_chassis.flushOutput()
有了上面几个函数之后,就可以开始写python脚本啦,结合代码来进行分析
首先是类的初始化函数,目的主要是初始化USART(UART)的波特率
def __init__(self):
print("move init finish")
self.time1 = time.time()
self.time2 = time.time()
self.flag = False
self.port_chassis = serial.Serial(CHASSIS_SERIAL_PORT, 9600, timeout=1)
# print('port_chassis is :' + self.port_chassis.is_Open())
self.port_chassis.flushInput()
self.port_chassis.flushOutput()
接下来就是发送数据,我们的购物机器人是通过树莓派做为整个的 “大脑”,是由树莓派里的python脚本给底盘发送指令,所以这个函数就是python让底盘移动distance
个格子的函数,这个函数里面也是通过USART(UART)给底盘发送指令的,所用函数就是self.port_chassis.write((direction + str(distance)).encode())
def move_by_grid(self, direction, distance=''):
# print('move by grid')
self.port_chassis.write((direction + str(distance)).encode())
self.time1 = time.time()
print(direction + str(distance))
self.wait_for_act_end_signal_chassis(direction, distance)
最后是接收数据,既然树莓派要控制底盘,树莓派自然也要接收到底盘回传的数据,这里用到的USART(UART)接收数据函数是self.port_chassis.readline().decode()
,sig就是树莓派接收到的USART(UART)数据,用户就可以对其做处理啦
def wait_for_act_end_signal_chassis(self, ret=None, distance=0):
print('waiting chassis...')
while True:
sig = self.port_chassis.readline().decode()
# print(self.time2 - self.time1)
sig = sig[0:2]
print(sig)
#
if sig == 'HI':
self.time2 = time.time()
print(self.time2 - self.time1)
if self.time2 - self.time1 < 1.0 and self.flag == False and distance == 1:
print('1 step deal error')
self.flag = True
if (ret == dir_up or ret == dir_back):
self.move_by_grid(ret, 1)
else:
STM32F427IIH6
在嵌入式芯片STM32F427IIH6里,我们首先要打开芯片的DMA功能,然后进行USART(UART)的初始化,最后设置USART(UART)的回调函数即可。
首先进行DMA的初始化,在这些初始化里面完成的就是DMA时钟的使能和DMA中断的使能
void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
__HAL_RCC_DMA2_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Stream0_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);
/* DMA1_Stream1_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
/* DMA1_Stream3_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Stream3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream3_IRQn);
/* DMA1_Stream6_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Stream6_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream6_IRQn);
/* DMA2_Stream1_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA2_Stream1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA2_Stream1_IRQn);
/* DMA2_Stream6_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn);
}
DMA使能完成之后就要进行USART(UART)的基本配置了,这里我设置的就是波特率9600(各个USART(UART)波特率初始化大同小异,因此在这里只放了usart6的初始化,其他类似,不做赘述)
void MX_USART6_UART_Init(void)
{
huart6.Instance = USART6;
huart6.Init.BaudRate = 9600;
huart6.Init.WordLength = UART_WORDLENGTH_8B;
huart6.Init.StopBits = UART_STOPBITS_1;
huart6.Init.Parity = UART_PARITY_NONE;
huart6.Init.Mode = UART_MODE_TX_RX;
huart6.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart6.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart6) != HAL_OK)
{
Error_Handler();
}
}
完成了芯片级的配置之后,就要开始配置用户自己需求的USART(UART)功能了,简单点来说就是就是设置回调函数,并且编写回调函数内容
在void USR_UartInit(void)
中主要是打开USART(UART)的DMA接收功能,并且开启USART(UART)接收中断,HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart)
就是我们的回调函数,uart6Rx[1024],uart7Rx[1024],uart8Rx[1024]这3个数组就是STM32F427IIH6的3个(USART6,UART7,UART8)USART(UART)接收到的数组数据 啦,接下来用户就可以对其做处理啦。
void USR_UartInit(void)
{
test1++;
uart8RxLength = 0;
HAL_UART_Receive_DMA(&huart8, uart8Rx, buffer_size);
uart7RxLength = 0;
HAL_UART_Receive_DMA(&huart7, uart7Rx, buffer_size);
uart6RxLength = 0;
HAL_UART_Receive_DMA(&huart6, uart6Rx, buffer_size);
__HAL_UART_ENABLE_IT(&huart8, UART_IT_IDLE);
__HAL_UART_ENABLE_IT(&huart7, UART_IT_IDLE);
__HAL_UART_ENABLE_IT(&huart6, UART_IT_IDLE);
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
HAL_UART_Receive_DMA(&huart8, uart8Rx, buffer_size);
HAL_UART_Receive_DMA(&huart7, uart7Rx, buffer_size);
HAL_UART_Receive_DMA(&huart6, uart6Rx, buffer_size);
__HAL_DMA_ENABLE(&hdma_uart8_rx);
__HAL_DMA_ENABLE(&hdma_uart7_rx);
__HAL_DMA_ENABLE(&hdma_usart6_rx);
test2++;
}
uint8_t RxLenHi, RxLenlo;
void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart)
{
__HAL_UART_CLEAR_IDLEFLAG(huart);
if( huart == &huart8)
{
test3++;
HAL_UART_Receive_DMA(&huart8, uart8Rx, buffer_size);
uart8RxLength = buffer_size-__HAL_DMA_GET_COUNTER(&hdma_uart8_rx);
if(uart8Rx[0]=='@'
&&uart8Rx[1]=='c'
&&uart8Rx[2]=='m'
&&uart8Rx[3]=='d')
{
if(!SERDEB_CmdValid())
SERDEB_PushCmd(uart8Rx, uart8RxLength);
}
__HAL_DMA_DISABLE(&hdma_uart8_rx);
HAL_GPIO_TogglePin(GPIOG,GPIO_PIN_8);
}
if( huart == &huart7)
{
__HAL_DMA_DISABLE(&hdma_uart7_rx);
HAL_UART_Receive_DMA(&huart7, uart7Rx, buffer_size);
__HAL_DMA_DISABLE(&hdma_uart7_rx);
}
if( huart == &huart6)
{
__HAL_DMA_DISABLE(&hdma_usart6_rx);
HAL_UART_Receive_DMA(&huart6, uart6Rx, buffer_size);
__HAL_DMA_DISABLE(&hdma_usart6_rx);
}
}
至此完成了双工通信的基本配置,接下来我会在另外一篇博客中具体讲解如何处理接收到的数据,敬请期待~
(づ ̄3 ̄)づ╭❤~一键三连,这次一定(๑•̀ㅂ•́)و✧