day31:Java和STM32的串口通信

Java向串口发送数据,STM32读取串口的数据,由此来控制LED灯的亮灭,蜂鸣器的启闭。

STM32代码:

工程结构:

bsp_usart.h

#ifndef __USART_H
#define	__USART_H


#include "stm32f10x.h"
#include <stdio.h>

/** 
  * 串口宏定义,不同的串口挂载的总线和IO不一样,移植时需要修改这几个宏
	* 1-修改总线时钟的宏,uart1挂载到apb2总线,其他uart挂载到apb1总线
	* 2-修改GPIO的宏
  */
	
// 串口1-USART1
#define  DEBUG_USARTx                   USART1
#define  DEBUG_USART_CLK                RCC_APB2Periph_USART1
#define  DEBUG_USART_APBxClkCmd         RCC_APB2PeriphClockCmd
#define  DEBUG_USART_BAUDRATE           115200

// USART GPIO 引脚宏定义
#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)
#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
    
#define  DEBUG_USART_TX_GPIO_PORT       GPIOA   
#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_9
#define  DEBUG_USART_RX_GPIO_PORT       GPIOA
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_10

#define  DEBUG_USART_IRQ                USART1_IRQn
#define  DEBUG_USART_IRQHandler         USART1_IRQHandler


// 串口2-USART2
//#define  DEBUG_USARTx                   USART2
//#define  DEBUG_USART_CLK                RCC_APB1Periph_USART2
//#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd
//#define  DEBUG_USART_BAUDRATE           115200

//// USART GPIO 引脚宏定义
//#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)
//#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
//    
//#define  DEBUG_USART_TX_GPIO_PORT       GPIOA   
//#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_2
//#define  DEBUG_USART_RX_GPIO_PORT       GPIOA
//#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_3

//#define  DEBUG_USART_IRQ                USART2_IRQn
//#define  DEBUG_USART_IRQHandler         USART2_IRQHandler

// 串口3-USART3
//#define  DEBUG_USARTx                   USART3
//#define  DEBUG_USART_CLK                RCC_APB1Periph_USART3
//#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd
//#define  DEBUG_USART_BAUDRATE           115200

//// USART GPIO 引脚宏定义
//#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOB)
//#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
//    
//#define  DEBUG_USART_TX_GPIO_PORT       GPIOB   
//#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_10
//#define  DEBUG_USART_RX_GPIO_PORT       GPIOB
//#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_11

//#define  DEBUG_USART_IRQ                USART3_IRQn
//#define  DEBUG_USART_IRQHandler         USART3_IRQHandler

// 串口4-UART4
//#define  DEBUG_USARTx                   UART4
//#define  DEBUG_USART_CLK                RCC_APB1Periph_UART4
//#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd
//#define  DEBUG_USART_BAUDRATE           115200

//// USART GPIO 引脚宏定义
//#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOC)
//#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
//    
//#define  DEBUG_USART_TX_GPIO_PORT       GPIOC   
//#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_10
//#define  DEBUG_USART_RX_GPIO_PORT       GPIOC
//#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_11

//#define  DEBUG_USART_IRQ                UART4_IRQn
//#define  DEBUG_USART_IRQHandler         UART4_IRQHandler


// 串口5-UART5
//#define  DEBUG_USARTx                   UART5
//#define  DEBUG_USART_CLK                RCC_APB1Periph_UART5
//#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd
//#define  DEBUG_USART_BAUDRATE           115200

//// USART GPIO 引脚宏定义
//#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD)
//#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
//    
//#define  DEBUG_USART_TX_GPIO_PORT       GPIOC   
//#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_12
//#define  DEBUG_USART_RX_GPIO_PORT       GPIOD
//#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_2

//#define  DEBUG_USART_IRQ                UART5_IRQn
//#define  DEBUG_USART_IRQHandler         UART5_IRQHandler


void USART_Config(void);
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch);
void Usart_SendString( USART_TypeDef * pUSARTx, char *str);
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch);

#endif /* __USART_H */

 bsp_usart.c

/**
  ******************************************************************************
  * @file    bsp_usart.c
  * @author  fire
  * @version V1.0
  * @date    2013-xx-xx
  * @brief   重定向c库printf函数到usart端口
  ******************************************************************************
  * @attention
  *
  * 实验平台:秉火STM32 F103-MINI 开发板  
  * 论坛    :http://www.firebbs.cn
  * 淘宝    :https://fire-stm32.taobao.com
  *
  ******************************************************************************
  */ 
	
#include "bsp_usart.h"

 /**
  * @brief  配置嵌套向量中断控制器NVIC
  * @param  无
  * @retval 无
  */
static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
  /* 嵌套向量中断控制器组选择 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  
  /* 配置USART为中断源 */
  NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
  /* 抢断优先级*/
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  /* 子优先级 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  /* 使能中断 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  /* 初始化配置NVIC */
  NVIC_Init(&NVIC_InitStructure);
}

 /**
  * @brief  USART GPIO 配置,工作参数配置
  * @param  无
  * @retval 无
  */
void USART_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;

	// 打开串口GPIO的时钟
	DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
	
	// 打开串口外设的时钟
	DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);

	// 将USART Tx的GPIO配置为推挽复用模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

  // 将USART Rx的GPIO配置为浮空输入模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
	
	// 配置串口的工作参数
	// 配置波特率
	USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
	// 配置 针数据字长
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	// 配置停止位
	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(DEBUG_USARTx, &USART_InitStructure);
	
	// 串口中断优先级配置
	NVIC_Configuration();
	
	// 使能串口接收中断
	USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);	
	USART_ITConfig ( DEBUG_USARTx, USART_IT_IDLE, ENABLE ); //使能串口总线空闲中断 	

	
	// 使能串口
	USART_Cmd(DEBUG_USARTx, ENABLE);	    
}

/*****************  发送一个字节 **********************/
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
	/* 发送一个字节数据到USART */
	USART_SendData(pUSARTx,ch);
		
	/* 等待发送数据寄存器为空 */
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}

/****************** 发送8位的数组 ************************/
void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num)
{
  uint8_t i;
	
	for(i=0; i<num; i++)
  {
	    /* 发送一个字节数据到USART */
	    Usart_SendByte(pUSARTx,array[i]);	
  
  }
	/* 等待发送完成 */
	while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);
}

/*****************  发送字符串 **********************/
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{
	unsigned int k=0;
  do 
  {
      Usart_SendByte( pUSARTx, *(str + k) );
      k++;
  } while(*(str + k)!='\0');
  
  /* 等待发送完成 */
  while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET)
  {}
}

/*****************  发送一个16位数 **********************/
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch)
{
	uint8_t temp_h, temp_l;
	
	/* 取出高八位 */
	temp_h = (ch&0XFF00)>>8;
	/* 取出低八位 */
	temp_l = ch&0XFF;
	
	/* 发送高八位 */
	USART_SendData(pUSARTx,temp_h);	
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
	
	/* 发送低八位 */
	USART_SendData(pUSARTx,temp_l);	
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}

///重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
		/* 发送一个字节数据到串口 */
		USART_SendData(DEBUG_USARTx, (uint8_t) ch);
		
		/* 等待发送完毕 */
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);		
	
		return (ch);
}

///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
		/* 等待串口输入数据 */
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);

		return (int)USART_ReceiveData(DEBUG_USARTx);
}

 rx_data_queue.h

#ifndef __ESP_DATA_QUEUE_H_
#define __ESP_DATA_QUEUE_H_

#include "stm32f10x.h"

#include <string.h>
#include <stdio.h>

//缓冲队列的个数需要为2的幂
#define QUEUE_NODE_NUM        (2)			//缓冲队列的大小(有多少个缓冲区)
#define QUEUE_NODE_DATA_LEN   (2*1024 )		//单个接收缓冲区大小

// 队列的主体数据类型接口
#define QUEUE_DATA_TYPE  				ESP_USART_FRAME
// 队列的调试输出接口
#define DATA_QUEUE_LOG  				QUEUE_DEBUG
#define DATA_QUEUE_LOG_ARRAY 	QUEUE_DEBUG_ARRAY


// 数据主体
typedef struct 
{
	char  *head; 	// 缓冲区头指针	
	uint16_t len; 	// 接收到的数据长度

}ESP_USART_FRAME;


// 队列结构
typedef struct {
	int         size;  		/* 缓冲区大小 */
	int         read; 		/* 读指针 */
	int         write;   	/* 写指针 */
	int read_using;			/* 正在读取的缓冲区指针 */
	int write_using;		/* 正在写入的缓冲区指针 */
	QUEUE_DATA_TYPE    *elems[QUEUE_NODE_NUM];  /* 缓冲区地址 */
} QueueBuffer;

extern QueueBuffer rx_queue;

/* 信息输出 */
#define QUEUE_DEBUG_ON         1
#define QUEUE_DEBUG_ARRAY_ON		1

#define QUEUE_INFO(fmt,arg...)           printf("<<-QUEUE-INFO->> "fmt"\n",##arg)
#define QUEUE_ERROR(fmt,arg...)          printf("<<-QUEUE-ERROR->> "fmt"\n",##arg)
#define QUEUE_DEBUG(fmt,arg...)          do{\
                                          if(QUEUE_DEBUG_ON)\
                                          printf("<<-QUEUE-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\
                                          }while(0)

#define QUEUE_DEBUG_ARRAY(array, num)    do{\
	 int32_t i;\
	 uint8_t* a = array;\
	 if(QUEUE_DEBUG_ARRAY_ON)\
	 {\
			printf("\n<<-QUEUE-DEBUG-ARRAY->>\n");\
			for (i = 0; i < (num); i++)\
			{\
					printf("%02x   ", (a)[i]);\
					if ((i + 1 ) %10 == 0)\
					{\
							printf("\n");\
					}\
			}\
			printf("\n");\
	}\
 }while(0)	

//输出队列的状态信息
#define cbPrint(cb)		    DATA_QUEUE_LOG("size=0x%x, read=%d, write=%d\n", cb.size, cb.read, cb.write);\
	  DATA_QUEUE_LOG("size=0x%x, read_using=%d, write_using=%d\n", cb.size, cb.read_using, cb.write_using);


QUEUE_DATA_TYPE* cbWrite(QueueBuffer *cb);
QUEUE_DATA_TYPE* cbRead(QueueBuffer *cb);
void cbReadFinish(QueueBuffer *cb);
void cbWriteFinish(QueueBuffer *cb);
//void cbPrint(QueueBuffer *cb);
QUEUE_DATA_TYPE* cbWriteUsing(QueueBuffer *cb) ;
int cbIsFull(QueueBuffer *cb) ; 
int cbIsEmpty(QueueBuffer *cb) ;

void rx_queue_init(void);
void pull_data_from_queue(void);
void push_data_to_queue(char *src_dat,uint16_t src_len);

#endif

 rx_data_queue.c

/**
  ******************************************************************************
  * @file    rx_data_queue.c
  * @author  fire
  * @version V1.0
  * @date    2015-01-xx
  * @brief   环形缓冲区,适用于接收外部数据时用作缓冲
  ******************************************************************************
  * @attention
  *
  * 实验平台:秉火 IOT STM32 开发板 
  * 论坛    :http://www.firebbs.cn
  * 淘宝    :https://fire-stm32.taobao.com
  *
  ******************************************************************************
  */ 

#include "./uart/rx_data_queue.h"



//实例化节点数据类型
QUEUE_DATA_TYPE  node_data[QUEUE_NODE_NUM]; 
//实例化队列类型
QueueBuffer rx_queue;

//队列缓冲区的内存池
__align(4) char node_buff[QUEUE_NODE_NUM][QUEUE_NODE_DATA_LEN] ;



/*环形缓冲队列*/

/**
  * @brief  初始化缓冲队列
  * @param  cb:缓冲队列结构体
  * @param  size: 缓冲队列的元素个数
  * @note 	初始化时还需要给cb->elems指针赋值
  */
void cbInit(QueueBuffer *cb, int size) 
{
    cb->size  = size;	/* maximum number of elements           */
    cb->read = 0; 		/* index of oldest element              */
    cb->write   = 0; 	 	/* index at which to write new element  */
//    cb->elems = (uint8_t *)calloc(cb->size, sizeof(uint8_t));  //elems 要额外初始化
}
 
//(此函数改成了宏,在头文件)
/**
  * @brief  输出缓冲队列当前的状态信息
  * @param  cb:缓冲队列结构体
  */
//void cbPrint(QueueBuffer *cb) 
//{
//    DATA_QUEUE_LOG("size=0x%x, read=%d, write=%d\n", cb->size, cb->read, cb->write);
//	  DATA_QUEUE_LOG("size=0x%x, read_using=%d, write_using=%d\n", cb->size, cb->read_using, cb->write_using);
//}
 
/**
  * @brief  判断缓冲队列是(1)否(0)已满
  * @param  cb:缓冲队列结构体
  */
int cbIsFull(QueueBuffer *cb) 
{
    return cb->write == (cb->read ^ cb->size); /* This inverts the most significant bit of read before comparison */ 
}
 
/**
  * @brief  判断缓冲队列是(1)否(0)全空
  * @param  cb:缓冲队列结构体
  */		
int cbIsEmpty(QueueBuffer *cb) 
{
    return cb->write == cb->read; 
}

/**
  * @brief  对缓冲队列的指针加1
  * @param  cb:缓冲队列结构体
  * @param  p:要加1的指针
  * @return  返回加1的结果
  */	
int cbIncr(QueueBuffer *cb, int p) 
{
    return (p + 1)&(2*cb->size-1); /* read and write pointers incrementation is done modulo 2*size */
}
 
/**
  * @brief  获取可写入的缓冲区指针
  * @param  cb:缓冲队列结构体
  * @return  可进行写入的缓冲区指针
  * @note  得到指针后可进入写入操作,但写指针不会立即加1,
           写完数据时,应调用cbWriteFinish对写指针加1
  */
QUEUE_DATA_TYPE* cbWrite(QueueBuffer *cb) 
{
    if (cbIsFull(cb)) /* full, overwrite moves read pointer */
    {
			return NULL;
		}		
		else
		{
			//当wriet和write_using相等时,表示上一个缓冲区已写入完毕,需要对写指针加1
			if(cb->write == cb->write_using)
			{
				cb->write_using = cbIncr(cb, cb->write); //未满,则增加1
			}
		}
		
	return  cb->elems[cb->write_using&(cb->size-1)];
}



/**
  * @brief 数据写入完毕,更新写指针到缓冲结构体
  * @param  cb:缓冲队列结构体
  */
void cbWriteFinish(QueueBuffer *cb)
{
    cb->write = cb->write_using;
}
 
/**
  * @brief  获取可读取的缓冲区指针
  * @param  cb:缓冲队列结构体
  * @return  可进行读取的缓冲区指针
  * @note  得到指针后可进入读取操作,但读指针不会立即加1,
					 读取完数据时,应调用cbReadFinish对读指针加1
  */
QUEUE_DATA_TYPE* cbRead(QueueBuffer *cb) 
{
		if(cbIsEmpty(cb))
			return NULL;
		
	//当read和read_using相等时,表示上一个缓冲区已读取完毕(即已调用cbReadFinish),
	//需要对写指针加1
	if(cb->read == cb->read_using)	
		cb->read_using = cbIncr(cb, cb->read);
	
	return cb->elems[cb->read_using&(cb->size-1)];
}


/**
  * @brief 数据读取完毕,更新读指针到缓冲结构体
  * @param  cb:缓冲队列结构体
  */
void cbReadFinish(QueueBuffer *cb) 
{	
		//重置当前读完的数据节点的长度
		cb->elems[cb->read_using&(cb->size-1)]->len = 0;
	
    cb->read = cb->read_using;
}



//队列的指针指向的缓冲区全部销毁
void camera_queue_free(void)
{
    uint32_t i = 0;

    for(i = 0; i < QUEUE_NODE_NUM; i ++)
    {
        if(node_data[i].head != NULL)
        {
					//若是动态申请的空间才要free
//            free(node_data[i].head);
            node_data[i].head = NULL;
        }
    }

    return;
}


/**
  * @brief  缓冲队列初始化,分配内存,使用缓冲队列时,
  * @param  无
  * @retval 无
  */
void rx_queue_init(void)
{
  uint32_t i = 0;

  memset(node_data, 0, sizeof(node_data));
		 
	/*初始化缓冲队列*/
	cbInit(&rx_queue,QUEUE_NODE_NUM);

    for(i = 0; i < QUEUE_NODE_NUM; i ++)
    {
        node_data[i].head = node_buff[i];
        
        /*初始化队列缓冲指针,指向实际的内存*/
        rx_queue.elems[i] = &node_data[i];
        
        DATA_QUEUE_LOG("node_data[i].head=0x%x,\r\nrx_queue.elems[i] =0x%x", (uint32_t)node_data[i].head,(uint32_t)rx_queue.elems[i]->head);

        memset(node_data[i].head, 0, QUEUE_NODE_DATA_LEN);
    }
		
	cbPrint(rx_queue);	
}



/**
  * @brief  往队列中写入数据的样例
  */
void push_data_to_queue(char *src_dat,uint16_t src_len)
{
	QUEUE_DATA_TYPE *data_p;
	uint8_t i;
	
	for(i=0;i<src_len;i++)
	{
		/*获取写缓冲区指针,准备写入新数据*/
		data_p = cbWrite(&rx_queue);
		
		if (data_p != NULL)	//若缓冲队列未满,开始传输
		{		
			//往缓冲区写入数据,如使用串口接收、dma写入等方式
			*(data_p->head + i) = src_dat[i];
				data_p->len++;
			printf("\r\ndata_p->len =%d",data_p->len);
		}else return;	
		
		cbPrint(rx_queue);	
	}	
	
	/*写入缓冲区完毕*/
	cbWriteFinish(&rx_queue);
	
	cbPrint(rx_queue);	

}


/**
  * @brief  从队列中取数据的样例
  */
void pull_data_from_queue(void)
{
	QUEUE_DATA_TYPE *rx_data;	
		
	/*从缓冲区读取数据,进行处理,*/
	rx_data = cbRead(&rx_queue); 

	if(rx_data != NULL)//缓冲队列非空
	{		
		//加上字符串结束符,方便直接输出字符串
		*(rx_data->head+rx_data->len) = '\0';
		
		QUEUE_DEBUG("接收到的数据:%s",rx_data->head);
		QUEUE_DEBUG_ARRAY((uint8_t*)rx_data->head,rx_data->len);

		//使用完数据必须调用cbReadFinish更新读指针
		cbReadFinish(&rx_queue);
	}
}

stm32f10x_it.c中添加函数:

// 串口中断服务函数
// 把接收到的数据写入缓冲区,在main函数中轮询缓冲区输出数据
void DEBUG_USART_IRQHandler(void)
{	
	uint8_t ucCh;
	QUEUE_DATA_TYPE *data_p; 
	
	if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET)
	{	
		ucCh  = USART_ReceiveData( DEBUG_USARTx );
		
		/*获取写缓冲区指针,准备写入新数据*/
		data_p = cbWrite(&rx_queue); 
		
		if (data_p != NULL)	//若缓冲队列未满,开始传输
		{		

			//往缓冲区写入数据,如使用串口接收、dma写入等方式
			*(data_p->head + data_p->len) = ucCh;
				
			if( ++data_p->len >= QUEUE_NODE_DATA_LEN)
			{
				cbWriteFinish(&rx_queue);
			}
		}else return;	
	}
	
	if ( USART_GetITStatus( DEBUG_USARTx, USART_IT_IDLE ) == SET )  //数据帧接收完毕
	{
			/*写入缓冲区完毕*/
			cbWriteFinish(&rx_queue);
		ucCh = USART_ReceiveData( DEBUG_USARTx );     //由软件序列清除中断标志位(先读USART_SR,然后读USART_DR)

	}
}

bsp_led.h

/* 和LED功能模块相关的程序 */
  
#ifndef __BSP_LED_H__
#define __BSP_LED_H__
  
#include "stm32f10x.h"
  
/*宏定义*/
#define GPIO_CLK_D4         RCC_APB2Periph_GPIOC        // 时钟
#define GPIO_PORT_D4        GPIOC                       // C端口
#define GPIO_PIN_D4         GPIO_Pin_2                  // PC2引脚
  
#define GPIO_CLK_D5         RCC_APB2Periph_GPIOC        // 时钟
#define GPIO_PORT_D5        GPIOC                       // C端口
#define GPIO_PIN_D5         GPIO_Pin_3                  // PC2引脚
  
/*参数宏定义*/
/*
digitalTOGGLE(p,i)是参数宏定义,p表示LED的端口号,ODR是数据输出寄存器,
查stm32f10x的官方中文手册的第8.2章的ODR寄存器,要点亮LED,根据原理图,要输出低电平0,
C语言中,^表示异或,即a^b表示a和b不同时输出为1,相同时输出为0,比如0^1=1,1^1=0,0^0=0,
这里为什么操作ODR,p是什么?查看stm32f10x.h文件,搜索GPIO_TypeDef就会明白,
i是LED的引脚对应的位电平,经过digitalTOGGLE(p,i) {p->ODR ^= i;}之后,
第一次p为0,i一直为1,第一次异或结果输出1,第二次输出0,第三次输出1,这样间断输出010101,灯不断亮灭
*/
// LED灯的状态翻转
//#define digitalTOGGLE(p,i)    {p->ODR ^= i;}  
// 输出高电平(让LED端口置1,BSRR寄存器用于位置1)
#define digitalHi(p,i)          {p->BSRR = i;}
// 输出低电平(让LED端口置0,BRR寄存器用于位清除)
#define digitalLo(p,i)          {p->BRR = i;}
 
// LED状态翻转
//#define LED1_TOGGLE           digitalTOGGLE(GPIO_PORT_D4,GPIO_PIN_D4)
//#define LED2_TOGGLE           digitalTOGGLE(GPIO_PORT_D5,GPIO_PIN_D5)
// D4这个LED亮
#define D4_LED_ON               digitalLo(GPIO_PORT_D4,GPIO_PIN_D4)
// D4这个LED灭
#define D4_LED_OFF              digitalHi(GPIO_PORT_D4,GPIO_PIN_D4)
// D5这个LED亮
#define D5_LED_ON               digitalLo(GPIO_PORT_D5,GPIO_PIN_D5)
// D5这个LED灭
#define D5_LED_OFF              digitalHi(GPIO_PORT_D5,GPIO_PIN_D5)
 
  
/*配置GPIO*/
void LED_GPIO_Config(void);
  
#endif  /*__BSP_LED_H__*/

bsp_led.c

/* 和LED功能模块相关的程序头文件 */
 
/*绝对路径,也可在Options for target中设置头文件*/
#include "./led/bsp_led.h" 
  
/*GPIO初始化*/
void LED_GPIO_Config(void)
{
    /*外设结构体*/
    GPIO_InitTypeDef GPIO_InitStruct_D4, GPIO_InitStruct_D5;  
      
    /*第一步:打开外设的时钟,看stm32f10x_rcc.c这个文件的RCC_APB2PeriphClockCmd函数介绍*/
    RCC_APB2PeriphClockCmd(GPIO_CLK_D4, ENABLE);
      
    /*第二步:配置外设的初始化结构体*/
    GPIO_InitStruct_D4.GPIO_Pin = GPIO_PIN_D4;          // PC2的那盏LED灯(D4)的引脚
    GPIO_InitStruct_D4.GPIO_Mode = GPIO_Mode_Out_PP;    // 推挽输出模式
    GPIO_InitStruct_D4.GPIO_Speed = GPIO_Speed_10MHz;   // 引脚速率
      
    GPIO_InitStruct_D5.GPIO_Pin = GPIO_PIN_D5;          // PC3的那盏LED灯(D5)的引脚
    GPIO_InitStruct_D5.GPIO_Mode = GPIO_Mode_Out_PP;    // 推挽输出模式
    GPIO_InitStruct_D5.GPIO_Speed = GPIO_Speed_10MHz;   // 引脚速率
      
    /*第三步:调用外设初始化函数,把配置好的结构体成员写到寄存器里面*/
    GPIO_Init(GPIO_PORT_D4, &GPIO_InitStruct_D4);
    GPIO_Init(GPIO_PORT_D5, &GPIO_InitStruct_D5);
     
    /* 默认情况下D4和D5是不亮的 */
    D4_LED_OFF;
    D5_LED_OFF;
}

bsp_buzzer.h

#ifndef __BSP_BUZZER_H__
#define __BSP_BUZZER_H__
 
#include "stm32f10x.h"
 
// 蜂鸣器用到的引脚(PC1)
#define GPIO_BUZZER_CLK             RCC_APB2Periph_GPIOC
#define GPIO_BUZZER_PORT            GPIOC
#define GPIO_BUZZER_PIN             GPIO_Pin_1
 
/*蜂鸣器GPIO端口初始化*/
void GPIO_Buzzer_Init(void);
 
/*蜂鸣器发声*/
void Buzzer_Lissen(void);

/*关闭蜂鸣器*/
void Buzzer_Close(void);
 
#endif  /*__BSP_BUZZER_H__*/

bsp_buzzer.c

#include "./buzzer/bsp_buzzer.h"
 
/*延迟函数*/
static void Delay(__IO uint32_t time)
{
    for(;time!=0;time--);
}
 
/*蜂鸣器GPIO端口初始化*/
void GPIO_Buzzer_Init(void)
{
    // 结构体
    GPIO_InitTypeDef GPIO_InitStruct;
     
    // 开时钟
    RCC_APB2PeriphClockCmd(GPIO_BUZZER_CLK, ENABLE);
     
    // 实例化
    GPIO_InitStruct.GPIO_Pin = GPIO_BUZZER_PIN;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
     
    // 初始化
    GPIO_Init(GPIO_BUZZER_PORT, &GPIO_InitStruct);
}
 
/*蜂鸣器发声*/
void Buzzer_Lissen(void)
{
    uint32_t i = 10000;
    // 产生PWM波,高低电平振荡发声
    //while(i--)
    //{
        GPIO_SetBits(GPIO_BUZZER_PORT, GPIO_BUZZER_PIN);
        Delay(100*i);
        GPIO_ResetBits(GPIO_BUZZER_PORT, GPIO_BUZZER_PIN);
        Delay(200*i);
    //}
}

/*关闭蜂鸣器*/
void Buzzer_Close(void)
{
	//uint32_t j = 10000;
    // 产生PWM波,高低电平振荡发声
    //while(j--)
    //{
        GPIO_ResetBits(GPIO_BUZZER_PORT, GPIO_BUZZER_PIN);
    //}
}

main.c

#include "stm32f10x.h"
#include "./uart/bsp_usart.h"
#include "./led/bsp_led.h"
#include "./buzzer/bsp_buzzer.h"
 
int main(void)
{
    // 接收到的数据存放到局部变量中
    uint8_t temp;
     
    /* LED初始化 */   
    LED_GPIO_Config();
	GPIO_Buzzer_Init();
     
    /* USART串口通信初始化 */
    USART_Config();
	
	// 控制LED和蜂鸣器
    while(1)
    {    
		temp = USART_ReceiveData(DEBUG_USARTx);     
         
        switch(temp)
        {
			case '0':
                D4_LED_OFF;
                break;
			
            case '1':
                D4_LED_ON;
                break;
			
			case '2':
                Buzzer_Lissen();
                break;
			
			case '3':
                Buzzer_Close();
                break;
        }
    }
}

Java代码:

index.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">

<title>Web控制51单片机的各个功能模块之案例测试</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">

<script type="text/javascript">
	// 开LED
	function goON() {
		var parameterId = document.getElementById("parameterId");
		parameterId.value = "on";
		document.forms[0].submit();
	}
	
	// 关LED
	function goOFF() {
		var parameterId = document.getElementById("parameterId");
		parameterId.value = "off";
		document.forms[0].submit();
	}
	
	// 开蜂鸣器
	function buzzer_goON() {
		var parameterId = document.getElementById("parameterId");
		parameterId.value = "buzzer_on";
		document.forms[0].submit();
	}
	
	// 关蜂鸣器
	function buzzer_goOFF() {
		var parameterId = document.getElementById("parameterId");
		parameterId.value = "buzzer_off";
		document.forms[0].submit();
	}
</script>
</head>

<body>
	<h2>========= Java和STM32串口通信实验 =========</h2>
	说明:按下“开灯”按钮打开LED灯,按下“关灯”按钮关闭LED灯
	<br><br>
	<form name="myForm" action="operator" method="post" style="margin-left: 200px;">
		<input type="hidden" name="parameter" id="parameterId"/>
		<table cellpadding="7" cellspacing="10">
			<tr>
				<td>
					<input type="hidden" name="on" value="1" readonly="readonly"/>
					<input type="button" value="开灯" onclick="goON();" style="font-size: 20px;"/>
				</td>
				<td>
					<input type="hidden" name="off" value="0" readonly="readonly"/>
					<input type="button" value="关灯" onclick="goOFF();" style="font-size: 20px;"/>
				</td>
			</tr>
			<tr>
				<td>
					<input type="hidden" name="buzzer_on" value="2" readonly="readonly"/>
					<input type="button" value="开蜂鸣器" onclick="buzzer_goON();" style="font-size: 20px;"/>
				</td>
				<td>
					<input type="hidden" name="buzzer_off" value="3" readonly="readonly"/>
					<input type="button" value="关蜂鸣器" onclick="buzzer_goOFF();" style="font-size: 20px;"/>
				</td>
			</tr>
		</table>
	</form>
</body>
</html>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
	<servlet>
		<servlet-name>operator</servlet-name>
		<servlet-class>com.lvshitech.java51.server.MyServer</servlet-class>
	</servlet>

	<servlet-mapping>
		<servlet-name>operator</servlet-name>
		<url-pattern>/operator</url-pattern>
	</servlet-mapping>
</web-app>

 

MyServer.java

package com.lvshitech.java51.server;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.lvshitech.java51.tool.Tools;

@SuppressWarnings("serial")
public class MyServer extends HttpServlet {
	
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		// 获取参数
		String parameter = req.getParameter("parameter");
		String on = req.getParameter("on");
		String off = req.getParameter("off");
		String buzzer_on = req.getParameter("buzzer_on");
		String buzzer_off = req.getParameter("buzzer_off");
		
		try {
			if(null==parameter || "".equals(parameter)) {
				System.out.println("接收到的参数为空!");
			} else {
				if("on".equals(parameter)) {
					Tools.action(on);
				} 
				if("off".equals(parameter)) {
					Tools.action(off);
				}
				if("buzzer_on".equals(parameter)) {
					Tools.action(buzzer_on);
				}
				if("buzzer_off".equals(parameter)){
					Tools.action(buzzer_off);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			Tools.closePort();
		}
		
		req.getRequestDispatcher("index.jsp").forward(req, resp);
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		this.doPost(req, resp);
	}
}

Tools.java

package com.lvshitech.java51.tool;

import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;
import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.UnsupportedCommOperationException;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;

public class Tools {
	
	public static SerialPort serialPort=null;
	
	/*类方法 不可改变 不接受继承
	 * 扫描获取可用的串口
	 * 将可用串口添加至list并保存至list
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static final ArrayList<String> uartPortUseAblefind() {
		//获取当前所有可用串口 
		//由CommPortIdentifier类提供方法
		Enumeration<CommPortIdentifier> portList=CommPortIdentifier.getPortIdentifiers();
		ArrayList<String> portNameList=new ArrayList();
		//添加并返回ArrayList
		while(portList.hasMoreElements()) {
			String portName=portList.nextElement().getName();
			portNameList.add(portName); 
		}
		return portNameList;
	}

	/*
	 * 串口常见设置
	 * 1)打开串口
	 * 2)设置波特率 根据单板机的需求可以设置为57600 ...
	 * 3)判断端口设备是否为串口设备
	 * 4)端口是否占用
	 * 5)对以上条件进行check以后返回一个串口设置对象new UARTParameterSetup()
	 * 6)return:返回一个SerialPort一个实例对象,若判定该com口是串口则进行参数配置
	 *   若不是则返回SerialPort对象为null
	 */
	public static final SerialPort portParameterOpen(String portName,int baudrate) {
		try {  //通过端口名识别串口
			CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
			//打开端口并设置端口名字 serialPort和超时时间 2000ms
			CommPort commPort=portIdentifier.open(portName,1000);
			//进一步判断comm端口是否是串口 instanceof
			if(commPort instanceof SerialPort) {
				System.out.println("该COM端口是串口!串口名称是:" + portName);
				//进一步强制类型转换
				serialPort=(SerialPort)commPort;
				//设置baudrate 此处需要注意:波特率只能允许是int型 对于57600足够
				//8位数据位
				//1位停止位
				//无奇偶校验
				serialPort.setSerialPortParams(baudrate, SerialPort.DATABITS_8,SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
				//串口配制完成 log
				System.out.println("串口参数设置已完成,波特率为"+baudrate+",数据位8bits,停止位1位,无奇偶校验");
			} else {	//不是串口
				System.out.println("该com端口不是串口,请检查设备!");
				//将com端口设置为null 默认是null不需要操作
			}

		} catch (NoSuchPortException e) {
			e.printStackTrace();
		} catch (PortInUseException e) {
			e.printStackTrace();
		} catch (UnsupportedCommOperationException e) {
			e.printStackTrace();
		}

		return serialPort;      
	}

	/*
	 * 串口数据发送以及数据传输作为一个类
	 * 该类做主要实现对数据包的传输至下单板机
	 */

	/*
	 * 上位机往单板机通过串口发送数据
	 * 串口对象 seriesPort 
	 * 数据帧:dataPackage
	 * 发送的标志:数据未发送成功抛出一个异常
	 */
	public static void uartSendDatatoSerialPort(SerialPort serialPort,byte[] dataPackage) {
		OutputStream out=null;
		try {
			out=serialPort.getOutputStream();
			out.write(dataPackage);
			out.flush();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			//关闭输出流
			if(out!=null) {
				try {
					out.close();
					out=null;
					//System.out.println("数据已发送完毕!");
				} catch (IOException e) {
					e.printStackTrace();
				}   
			}
		}           
	}

	/*
	 * 上位机接收数据
	 * 串口对象seriesPort
	 * 接收数据buffer
	 * 返回一个byte数组
	 */
	public  static  byte[] uartReceiveDatafromSingleChipMachine(SerialPort serialPort) {
		byte[] receiveDataPackage=null;
		InputStream in=null;
		try {
			in=serialPort.getInputStream();
			// 获取data buffer数据长度
			int bufferLength=in.available();
			while(bufferLength!=0) {
				receiveDataPackage=new byte[bufferLength];
				in.read(receiveDataPackage);
				bufferLength=in.available();

			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return receiveDataPackage;
	}   

	public static void action(String parameter) throws Exception {
		// 打开串口
		SerialPort serialPort = portParameterOpen("COM3", 115200);
		// 要发送的数据
		String dataSend = parameter;

		int i=1;
		//while(true) {
			// 发送数据到单片机
			byte []datByte = dataSend.getBytes();
			uartSendDatatoSerialPort(serialPort, datByte);
			System.out.println("-------------------------------------------------------");
			System.out.println((i++) + ". 发送到串口的数据:" + dataSend);

			// 休眠500ms,等待单片机反应
			//Thread.sleep(500);

			// 从单片机接收到的数据
			/*
			byte[] dat = uartReceiveDatafromSingleChipMachine(serialPort);
			if(dat != null && dat.length > 0) {
				String dataReceive = new String(dat, "GB2312");
				System.out.println((i++) + ". 从串口接收的数据:" + dataReceive);
			} else {
				System.out.println("接收到的数据为空!");
			}
			*/
		//}
	}
	
    /**
     * 关闭串口
     * @param serialport 待关闭的串口对象
     */
    public static void closePort() {
        if (serialPort != null) {
            serialPort.close();
            serialPort = null;
        }
    }
}

 

 

 

 

 

 

 

 

 

 

 

posted @ 2018-08-21 00:04  半生戎马,共话桑麻、  阅读(2348)  评论(2)    收藏  举报
levels of contents