900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > CubeMX STM32串口1DMA使用IDLE中断接收 串口2DMA接收DMX512信号(标准)

CubeMX STM32串口1DMA使用IDLE中断接收 串口2DMA接收DMX512信号(标准)

时间:2019-09-27 01:02:15

相关推荐

CubeMX STM32串口1DMA使用IDLE中断接收 串口2DMA接收DMX512信号(标准)

CubeMX STM32串口1DMA使用IDLE中断接收、串口2DMA收发DMX512信号(标准)

DMX512协议CubeMX代码部分串口1串口2外部中断定时器1总结

DMX512协议

这是我第一次写文章,请大家多多指教。

最近遇到了一些疑问,还请大家帮忙瞧瞧。

DMX(Digital MultipleX),意为多路数字传输,采用的是单向异步串行传输,是美国舞台灯光协会(usITT)于1990年发布的灯光控制器与灯具设备进行数据传输的工业标准,全称是USITTDMX512(1990)。

DMX512波特率:250Kbps,即每比特宽度是4 us。

数据帧格式:11位(1位开始位,8位数据,2位停止位)。

标准的DMX512信号包括一个MTBP位、一个Break位、一个MAB位、一个SC和512个数据帧。其中Break位不小于88us,MAB位不小于8us。时序图如下:

链接: link.

CubeMX

本实验采用的芯片是STM32F103C8T6。

下面是CubeMX的部分配置:

打开外部时钟,本实验HCLK为64MHZ。

配置uart1,波特率为115200,打开中断,添加DMA。

配置uart2,波特率为250000,打开中断,添加DMA,停止位2位。

配置定时器1,64MHz/(63+1)/(3+1) = 250kHz,即4us,NVIC打开中断,这里我配置优先级为1。

配置完成后,生成Keil工程。

代码部分

串口1

在usart.c/h中,自行添加打开IDLE中断函数和DMA接收。

#define BUFFER_SIZE 100uint8_t aRxBuffer[BUFFER_SIZE];volatile uint8_t rx_len = 0,recv_end_flag = 0;void MX_USART1_UART_Init(void){huart1.Instance = USART1;huart1.Init.BaudRate = 115200;huart1.Init.WordLength = UART_WORDLENGTH_8B;huart1.Init.StopBits = UART_STOPBITS_1;huart1.Init.Parity = UART_PARITY_NONE;huart1.Init.Mode = UART_MODE_TX_RX;huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;huart1.Init.OverSampling = UART_OVERSAMPLING_16;if (HAL_UART_Init(&huart1) != HAL_OK){Error_Handler();}__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);//使能idle中断HAL_UART_Receive_DMA(&huart1,aRxBuffer,BUFFER_SIZE);//打开DMA接收,数据存入rx_buffer数组中。}

在stm32f1xx_it.c中编写串口1的接收中断函数,这里引用了:

链接: link.

void USART1_IRQHandler(void){/* USER CODE BEGIN USART1_IRQn 0 */uint8_t temp;if(USART1 == huart1.Instance){if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) != RESET){recv_end_flag = 1; // 接受完成标志位置1 __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位HAL_UART_DMAStop(&huart1);temp = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);// 获取DMA中未传输的数据个数rx_len = BUFFER_SIZE - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数HAL_UART_Receive_DMA(&huart1,aRxBuffer,BUFFER_SIZE);//重新打开DMA接收}}HAL_UART_IRQHandler(&huart1);}

然后直接在main.c大循环里面加入

if(recv_end_flag == 1){DMA_USART1_Send(aRxBuffer,rx_len);recv_end_flag = 0;rx_len = 0;}

串口2

在usart.c/h中,串口2初始化和发送DMX信号的编写,DMX的break和MAB信号在定时中断中产生。DMX信号发送需要对TX引脚进行普通输出模式和复用输出模式的配置。

#define DMXBUFFER_SIZE 512uint8_t DMXrxbuffer[DMXBUFFER_SIZE];volatile uint8_t DMXrecv_start_flag = 0,DMXrecv_end_flag = 0;void MX_USART2_UART_Init(void){huart2.Instance = USART2;huart2.Init.BaudRate = 250000;huart2.Init.WordLength = UART_WORDLENGTH_8B;huart2.Init.StopBits = UART_STOPBITS_2;huart2.Init.Parity = UART_PARITY_NONE;huart2.Init.Mode = UART_MODE_TX_RX;huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;huart2.Init.OverSampling = UART_OVERSAMPLING_16;if (HAL_UART_Init(&huart2) != HAL_OK){Error_Handler();}__HAL_UART_ENABLE_IT(&huart2, UART_IT_TC);//使能发送完成中断}void TX_gpioConfig(uint8_t tx_select)//普通输出模式和复用输出模式{GPIO_InitTypeDef GPIO_InitStruct = {0}; /* GPIO Ports Clock Enable */// __HAL_RCC_GPIOA_CLK_ENABLE();/*Configure GPIO pin : PA2 */GPIO_InitStruct.Pin = GPIO_PIN_2;if(tx_select == 1) {GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;}if(tx_select == 2) {GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;}GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);}void DMX512_Send(uint8_t *buf,uint16_t len)//DMX信号发送{if(DMXsend_start_flag == 0){DMXsend_start_flag = 1;HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET); //拉低tx引脚,改为普通输出模式TX_gpioConfig(1); //普通输出模式HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET); //使能485发送dmx_mode = 2;tim1_count = 0;HAL_TIM_Base_Start_IT(&htim1);//打开定时器delay_us(100);// 延时us,等待计数结束}if(DMX_break == 1){HAL_UART_Transmit_DMA(&huart2,buf,len);if(DMXsend_end_flag == 1){DMX_break = 0;DMXsend_end_flag = 0;DMXsend_start_flag = 0;dmx_mode = 1;HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET); //发送完成,使能485接收}}}

在stm32f1xx_it.c中,有多处中断,包括串口1、串口2、定时器1、外部中断。

void USART2_IRQHandler(void)//DMX信号接收{/* USER CODE BEGIN USART2_IRQn 0 */static uint8_t clean_idle;if(USART2 == huart2.Instance){DMXsend_end_flag = 1;// 接受完成标志位置1 if(__HAL_UART_GET_FLAG(&huart2,UART_FLAG_ORE) != RESET){clean_idle = USART2->SR;clean_idle = USART2->DR;__HAL_UART_CLEAR_OREFLAG(&huart2);}}HAL_UART_IRQHandler(&huart2);}

在主函数的大循环中,把接收到的数据通过uart1发送。

外部中断

这里接了PA8引脚到串口2的RX引脚上,用来捕捉break和MAB信号。

void EXTI9_5_IRQHandler(void){if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_8) != RESET){__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_8);if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_8)==1)//上升沿{if(tim1_count2>23)//检测到break{break_notic = 1;HAL_UART_DMAStop(&huart2);if(rece_notic == 1) {DMXrecv_end_flag = 1;} }if(break_notic == 2){break_notic = 0;HAL_UART_Receive_DMA(&huart2,DMXrxbuffer,DMXBUFFER_SIZE);//打开DMA接收HAL_TIM_Base_Stop_IT(&htim1);//关闭定时器}tim1_count2 = 0;}else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_8)==0)//下降沿{if(break_notic == 1&&tim1_count2<=4)//检测到MAB{break_notic = 2;rece_notic = 1;}tim1_count2= 0;HAL_TIM_Base_Start_IT(&htim1);//打开定时器}}HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_8);}

定时器1

定时器1主要用做处理DMX信号接收和发送的时间基准,接收时,dmx_mode为1;发送时为2。

void TIM1_UP_IRQHandler(void){if(TIM1 == htim1.Instance){switch(dmx_mode){case 1:{tim1_count2++;if(tim1_count2>60) HAL_TIM_Base_Stop_IT(&htim1);//关闭定时器}break;case 2:{if(tim1_count <= 20)//break信号>88us{HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET); }else if(tim1_count == 21||tim1_count ==22)//MAB信号>8us{HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_SET);}else if(tim1_count >= 23&&tim1_count <=30)//模拟SC信号,此处应该还有更好办法处理SC信号{HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET); }else if(tim1_count >= 31&&tim1_count <=33)//tx引脚转换为复用模式{TX_gpioConfig(2);}else{DMX_break = 1;tim1_count = 0;HAL_TIM_Base_Stop_IT(&htim1);//关闭定时器}tim1_count++;}break;default: break;}}HAL_TIM_IRQHandler(&htim1);}

总结

附上几张测试结果:

这是DMX信号发送的时序图:

以下是数据接收结果。

单片机与单片机直之间通信问题不大,主要问题是接收DMX192控制台或者ART-NET的DMX信号。本文用外部中断+定时器基准处理break和MAB信号,使得程序适用性更强。

最后总结:

___本文内容主要尝试采用STM32单片机实现标准型的DMX512信号的发送和接收,具体思路总结一下:

发送信号:开启定时器1,4us进入一次中断服务函数;配置USART2的TX引脚;开始88us配置为普通输出模式,输出为低;接下来8us为普通输出模式,输出为高;之后配置为复用输出模式,再开启DMA发送数据;发送过程不需要延时:44us*512=22528us(不占用CPU),只需要判断是否进入发送完成中断。

接收信号:STM32单片机打开外部中断(模式为上升沿或者下降沿触发),当检测为下降沿时,打开定时器计数,检测到上升沿时,查看计数值,若计算出大于88us,则为break信号;继续捕捉下降沿,即为MAB信号,随后就可以打开DMA接收。当下一个break信号来临时,也就是说已经接收好第一个DMX信号,同时也是下一个信号的开始。

实现过程中遇到很多坑,如有错误,请指出。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。