建设各网站需要多久,php完整网站开发案例,wordpress微信文章,浙江建设监理协会官方网站keil5使用c编写stm32控制程序 一、前言二、配置图解三、std::cout串口重定向四、串口中断服务函数五、结尾废话 一、前言
想着搞个新奇的玩意玩一玩来着#xff0c;想用c编写代码来控制stm32#xff0c;结果在keil5中#xff0c;把踩给我踩闷了#xff0c;这里简单记录一下… keil5使用c编写stm32控制程序 一、前言二、配置图解三、std::cout串口重定向四、串口中断服务函数五、结尾废话 一、前言
想着搞个新奇的玩意玩一玩来着想用c编写代码来控制stm32结果在keil5中把踩给我踩闷了这里简单记录一下。注意一定要按照如下流程进行操作一步都不要跟丢了。
二、配置图解
所需要的一些文件放在百度网盘了。 先把最新的库函数和CMSIS安装好。 我这里为了方便就直接安装在了keil5的文件夹路径里。
废话不多说直接上图解。 记得把use microlib的勾选去掉。配置和我图片上一样就没问题。 那这样配置过后会不会就好用了当然不是还要使用最新的标准库函数才行。 如何配置以后方便移植勒当然是这样操作啦。 这里用的是普中科技32f103zet6板子的案例教程。 如图找到你工程目录下的CMSIS把之前老版本的删除掉。 注意自己找到你最新的CMSIS的安装路径。 再把你Keil_v5\ARM\Packs\ARM\CMSIS\5.9.0\CMSIS\Core\Include里边的文件全部复制过去。 在把Keil_v5\ARM\Packs\Keil\STM32F1xx_DFP\2.4.0\Device\Include里边的system_stm32f10x.h 和Keil_v5\ARM\Packs\Keil\STM32F1xx_DFP\2.4.0\Device\Source里边的system_stm32f10x.c复制出来。 然后再继续在Keil_v5\ARM\Packs\Keil\STM32F1xx_DFP\2.4.0\Device\Source\ARM找到你对应单片机的后缀文件。
最后你工程目录的CMSIS下边就应该是这些文件。 2.再把Libraries下的STM32F10x_StdPeriph_Driver里的inc和src文件替换为最新的固件库的。 安装的最新的固件库的文件位置在Keil_v5\ARM\Packs\Keil\STM32F1xx_DFP\2.4.0\Device\StdPeriph_Driver里。 3.替换user文件下的如下文件 找到文件路径在Keil_v5\ARM\Packs\Keil\STM32F1xx_DFP\2.4.0\Device\StdPeriph_Driver\templates文件下 在找打之前的Keil_v5\ARM\Packs\Keil\STM32F1xx_DFP\2.4.0\Device\Include里的这个文件 用这些把之前的文件替换掉就行。 接下来先把你工程中的一些c结尾文件按如下配置
就可以编译运行了但是直接运行会报错。 建议先进入stm32f10x_conf.h文件中把 ”#include RTE_Components.h注释掉因为我们按照做模板的配置操作的没有进入之前的动态环境中去配置东西如果选择了如下配置的东西就会在你的工程文件下生成RTE的一个文件夹。如果你选择了这样操作的话就不会报这个错误。如果要配置的话记得把依赖勾选完整如果依赖勾选正确会显示绿色如果不正确会显示黄色这里不在赘述纯粹是为你满足你们的好奇心。
当然现在也可以不用管因为后边会配置自定义的串口输出会勾选一些配置到时候他就会自动生成这个RTE_Components.h所需要的环境。
这下配置好了浅浅的点个灯吧点灯大师已经准备上线了直接操作。
写个led.h
/* LED时钟端口、引脚定义 */
#ifndef _led_H
#define _led_H#include system.h#define LED_PORT GPIOC
#define LED_PIN (GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7)
#define LED_PORT_RCC RCC_APB2Periph_GPIOC//这里是stm32的管脚的按位操作。
#define led1 PCout(1) //D2指示灯连接的是PC1管脚
#define led2 PCout(2) //D2指示灯连接的是PC2管脚#ifdef __cplusplusclass Led{
public:Led(){LED_GPIO_Config();}void LED_GPIO_Config(void);void TurnOn( u16 port, bool status);~Led(){std::coutstd::move(I am relased!)std::endl;};
private:};#endif在写个led.cpp
#include led.hvoid Led::LED_GPIO_Config()
{GPIO_InitTypeDef GPIO_InitStructure;//定义结构体变量RCC_APB2PeriphClockCmd(LED_PORT_RCC, ENABLE);GPIO_InitStructure.GPIO_Pin LED_PIN; //选择你要设置的IO口GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; //设置推挽输出模式GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; //设置传输速率GPIO_Init(LED_PORT, GPIO_InitStructure); /* 初始化GPIO */GPIO_SetBits(LED_PORT, LED_PIN); //将LED端口拉高熄灭所有LED}void Led::TurnOn(u16 port, bool status)
{if(status){GPIO_ResetBits(LED_PORT, port);} //将LED端口拉高熄灭所有LEDelse{GPIO_SetBits(LED_PORT, port);}
}
再写个main.cpp
#include system.hint main(void)
{SysTick_Init(72);std::shared_ptrLed led std::make_sharedLed();while(1){led1 !led1;delay_ms(500);led-TurnOn(GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7, false);delay_ms(500);led-TurnOn(GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7, true);delay_ms(500);}return 0;
}啊哈好了恭喜你成功成为一名光荣的点灯工程师。 单纯点灯 三、std::cout串口重定向
既然都用到c了那么这个特色的输出肯定不能放过那么怎么使用它把信息通过串口输出到上位机上啊别急一步一步来showtime
1.先点击这个运行环境配置图标 2.依次进入Compiler-I/O将里面的都勾选上其实也不用就勾选个STDOUT我感觉就可以了当然勾上也没有什么影响并将variant列依次选择如下图所示 这里配置完成后需要你在你自己的usart.h文件中实现
#ifdef __cplusplus
extern C
{
#endifint stdout_putchar(int ch);int stderr_putchar(int ch);
#ifdef __cplusplus
}
#endifusart.cpp函数中实现
//标准输出流
int stdout_putchar(int ch)
{USART_SendData(USART1,(u8)ch);while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)RESET){};return ch;
}标准错误流
int stderr_putchar(int ch)
{while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE)RESET);return (int)USART_ReceiveData(USART1);
}这样你就可以直接在main.cpp中调用函数了。这次点灯加上串口通信。
#include system.hint main(void)
{SysTick_Init(72);Init_Usart();std::shared_ptrLed led std::make_sharedLed();while(1){led1 !led1;delay_ms(500);led-TurnOn(GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7, false);delay_ms(500);led-TurnOn(GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7, true);delay_ms(500);std::coutstd::move(Usart_Test)std::endl;}return 0;
}这里多说几句这c在这里keil里边有些特性和标准的c不太一样这里的我就不过多阐述待你们使用的时候自然就明白了。
打开你的串口调试助手就可以得到如下的信息。 哇哦不仅现在点灯成功了还玩明白了这个c重定向串口输出了。
四、串口中断服务函数
那么接下来再试一试串口中断服务函数咋样 这个有个坑哈坑了我一天多给我人搞麻木了注意这个c的编译后的中断服务函数代码和原来stm32库函数开发的中断向量表对不上导致无法进入中断服务函数。 只需要在前面加上extern C即可链接原来stm32库函数中的中断服务函数了。 这里咱们上点强度使用GM65二维码扫描器来和控制小灯的亮和灭。并把读取到的二维码数据上传到pc。这里串口收发数据频繁的话可以使用DMA功能我这里就不做演示了为啥因为我这数据接收并不频繁又不用去搞什么优化摸摸鱼啦。都很简单随便找个教程看一看就行了。我就不写了。 usart.h
#ifndef __usart_H
#define __usart_H#include system.h //串口1
#define USART1_GPIO_PORT GPIOA
#define USART1_GPIO_CLK RCC_APB2Periph_GPIOA
#define USART1_TX_GPIO_PIN GPIO_Pin_9
#define USART1_RX_GPIO_PIN GPIO_Pin_10//串口2
#define USART2_GPIO_PORT GPIOA
#define USART2_GPIO_CLK RCC_APB2Periph_GPIOA
#define USART2_TX_GPIO_PIN GPIO_Pin_2
#define USART2_RX_GPIO_PIN GPIO_Pin_3#define BUFFER_SIZE 32 // 定义数组缓冲的最大长度#ifdef __cplusplus
extern C
{
#endifint stdout_putchar(int ch);int stderr_putchar(int ch);void usart_init(unsigned int baud);void usart_init2(unsigned int baud);void Init_Usart(void);void USART_Send_Byte(USART_TypeDef* USARTx, uint16_t Data);void USART_Send_String(USART_TypeDef* USARTx, char *str);
#ifdef __cplusplus
}
#endif
#endifusart.cpp
#include usart.h bool RxState{0};
u8 uart2_len{0}; //数据长度uart2_len1加上帧尾u8 RxCounter{0};u8 RxBuffer[BUFFER_SIZE]{0};u16 check; //标准输出流
int stdout_putchar(int ch)
{USART_SendData(USART1,(u8)ch);while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)RESET){};return ch;
}标准错误流
int stderr_putchar(int ch)
{while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE)RESET);return (int)USART_ReceiveData(USART1);
}void usart_init(unsigned int baud)
{GPIO_InitTypeDef GPIO_Init_Structure; //定义GPIO结构体USART_InitTypeDef USART_Init_Structure; //定义串口结构体NVIC_InitTypeDef NVIC_Init_Structure; //定义中断结构体NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);RCC_APB2PeriphClockCmd(USART1_GPIO_CLK, ENABLE); //开启GPIOA时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //开启APB2总线复用时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //开启USART1时钟//配置PA9 TXGPIO_Init_Structure.GPIO_Mode GPIO_Mode_AF_PP; //复用推挽GPIO_Init_Structure.GPIO_Pin USART1_TX_GPIO_PIN;GPIO_Init_Structure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init( USART1_GPIO_PORT, GPIO_Init_Structure);//配置PA10 RXGPIO_Init_Structure.GPIO_Mode GPIO_Mode_IN_FLOATING; //复用推挽GPIO_Init_Structure.GPIO_Pin USART1_RX_GPIO_PIN;GPIO_Init( USART1_GPIO_PORT, GPIO_Init_Structure);//串口相关配置USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //串口中断配置USART_Init_Structure.USART_BaudRate baud; //波特率设置为9600USART_Init_Structure.USART_HardwareFlowControl USART_HardwareFlowControl_None; //硬件流控制为无USART_Init_Structure.USART_Mode USART_Mode_Tx | USART_Mode_Rx; //模式设为收和发USART_Init_Structure.USART_Parity USART_Parity_No; //无校验位USART_Init_Structure.USART_StopBits USART_StopBits_1; //一位停止位USART_Init_Structure.USART_WordLength USART_WordLength_8b; //字长为8位 USART_Init(USART1, USART_Init_Structure); //初始化 USART_Cmd(USART1, ENABLE); //串口使能//中断结构体配置NVIC_Init_Structure.NVIC_IRQChannel USART1_IRQn;NVIC_Init_Structure.NVIC_IRQChannelCmd ENABLE;NVIC_Init_Structure.NVIC_IRQChannelPreemptionPriority 0;NVIC_Init_Structure.NVIC_IRQChannelSubPriority 1;NVIC_Init(NVIC_Init_Structure);}void usart_init2(unsigned int baud)
{GPIO_InitTypeDef GPIO_Init_Structure; //定义GPIO结构体USART_InitTypeDef USART_Init_Structure; //定义串口结构体NVIC_InitTypeDef NVIC_Init_Structure; //定义中断结构体NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);RCC_APB2PeriphClockCmd(USART2_GPIO_CLK, ENABLE); //开启GPIOA时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //开启APB2总线复用时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); //开启USART1时钟//配置PA2 TXGPIO_Init_Structure.GPIO_Mode GPIO_Mode_AF_PP; //复用推挽GPIO_Init_Structure.GPIO_Pin USART2_TX_GPIO_PIN;GPIO_Init_Structure.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init( USART2_GPIO_PORT, GPIO_Init_Structure);//配置PA3 RXGPIO_Init_Structure.GPIO_Mode GPIO_Mode_IN_FLOATING; //复用推挽GPIO_Init_Structure.GPIO_Pin USART2_RX_GPIO_PIN;GPIO_Init( USART2_GPIO_PORT, GPIO_Init_Structure);USART_ITConfig(USART2,USART_IT_RXNE,ENABLE); USART_Init_Structure.USART_BaudRate 9600; //波特率设置为9600USART_Init_Structure.USART_HardwareFlowControl USART_HardwareFlowControl_None; //硬件流控制为无USART_Init_Structure.USART_Mode USART_Mode_Tx | USART_Mode_Rx; //模式设为收和发USART_Init_Structure.USART_Parity USART_Parity_No; //无校验位USART_Init_Structure.USART_StopBits USART_StopBits_1; //一位停止位USART_Init_Structure.USART_WordLength USART_WordLength_8b; //字长为8位 USART_Init(USART2, USART_Init_Structure); USART_Cmd(USART2, ENABLE);//中断结构体配置NVIC_Init_Structure.NVIC_IRQChannel USART2_IRQn;NVIC_Init_Structure.NVIC_IRQChannelCmd ENABLE;NVIC_Init_Structure.NVIC_IRQChannelPreemptionPriority 0;NVIC_Init_Structure.NVIC_IRQChannelSubPriority 3;NVIC_Init(NVIC_Init_Structure);
}//二个串口初始化函数
void Init_Usart(void)
{usart_init(9600);usart_init2(9600);
}/*** 功能串口写字节函数* 参数1USARTx 串口号* 参数2Data 需写入的字节* 返回值None*/
void USART_Send_Byte(USART_TypeDef* USARTx, uint16_t Data)
{USART_SendData(USARTx, Data);while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE)RESET);
}
/*** 功能串口写字符串函数* 参数1USARTx 串口号* 参数2str 需写入的字符串* 返回值None*/
void USART_Send_String(USART_TypeDef* USARTx, char *str)
{uint16_t i0;do{USART_Send_Byte(USARTx, *(stri));i;}while(*(str i) ! \0);while(USART_GetFlagStatus(USART1, USART_FLAG_TC)RESET);
}/*
void USART1_IRQHandler(void)
{volatile char temp;if(USART_GetITStatus(USART1,USART_IT_RXNE)! RESET){temp USART_ReceiveData(USART1);USART_ClearITPendingBit(USART1,USART_IT_RXNE); //清空标志位}
}
*/unsigned short CRC16_XMODEM(unsigned char *puchMsg, unsigned int usDataLen)
{unsigned short wCRCin 0x0000;unsigned short wCPoly 0x1021;unsigned char wChar 0;while (usDataLen--){wChar *(puchMsg);//4,1 3,2 3,3, 1,4 1,5wCRCin ^(wChar 8);for(auto i 0; i 8; i){ if(wCRCin 0x8000){wCRCin (wCRCin 1) ^ wCPoly;}else{wCRCin wCRCin 1;}}}return(wCRCin);
} extern C void USART2_IRQHandler(void)
{if( USART_GetITStatus(USART2,USART_IT_RXNE)!RESET) //接收中断 {USART_ClearITPendingBit(USART2,USART_IT_RXNE); //清除中断标志RxBuffer[RxCounter] USART_ReceiveData(USART2);}if(RxBuffer[RxCounter-1]13) //0x0D{uart2_lenRxCounter-1;checkCRC16_XMODEM(RxBuffer[RxCounter],uart2_len1); //校验和crcif(((check0x00ff)RxBuffer[uart2_len1])(((check8)0x00ff)RxBuffer[uart2_len1])){RxState1; }}if(USART_GetFlagStatus(USART2,USART_FLAG_ORE) SET) //判断中断是否溢出{ USART_ClearFlag(USART2,USART_FLAG_ORE); USART_ReceiveData(USART2); } if(RxState){//find_key(RxBuffer,on);if(strstr((const char*)RxBuffer, on)){GPIO_ResetBits(LED_PORT, GPIO_Pin_4);}if(strstr((const char*)RxBuffer, off)){GPIO_SetBits(LED_PORT, GPIO_Pin_4);}for(auto x0;xuart2_len1;x){USART_SendData(USART1,RxBuffer[x]);delay_ms(10);}RxState0;RxCounter0;}
}main.cpp
#include system.hint main(void)
{SysTick_Init(72);Init_Usart();std::shared_ptrLed led std::make_sharedLed();while(1){delay_ms(500);led-TurnOn(GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7, false);delay_ms(500);led-TurnOn(GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7, true);delay_ms(500);}return 0;
}接下里就是编译调试了。什么都好就是胖了点不过问题不大有的是方法减肥这里我就不逼逼了。好在ZET6 512k的存储容量这小小100k还不够。 把编译好的hex文件下载到单片机上就可以观测到结果了。 简单的一个演示视频 扫描点灯 使用的gm65我接的二号串口GPIOC使用的5号口具体的文件代码我会分享给大家。因为有些东西我这上边没有写不够你拿到我这弄好的模板操作之后的就好弄了。
五、结尾废话
说着是使用c快乐的为啥感觉没用到多少其实正常的代码量不够的话其实写C更方便点还有就是这里边使用c11的感觉没那爽不够还行勉强够用了搞好c pulspuls以后软硬通吃就行那不香香啊。
啊 什么 你问我 代码在哪 别急马上给。