先大致确定硬件
一个stm32f103C8最小系统作控制,DS8B20温度检测模块,8个LED数码管,74HC138D作3位到8位的扩展来作位选择,74HC245作信号放大来驱动数码管显示。建立工程文件夹SimpleElectronicThermometer,开发keil5,project -> new uvision project,建立一个工程。
在
Select Device for Target
界面选择STMicroelectronics->STM32F1 Series->STM32F103->STM32F103C8
。在Target下面
Add Group
- CMSIS: Cortex Microcontroller Software Interface Standard,微控制器软件接口标准, 来存放CMSIS Cortex-M3 Core Peripheral Access Layer System库 与 CMSIS Cortex-M3 Device Peripheral Access Layer System库。
- User: 存放main与自定义中断程序
- App:存放项目自定义的库
- StdPeriph_Driver: 存放标准外设驱动库
设置好工程的头文件引用目录
写一段GPIO输出来测试工程是否正常
#include "stm32f10x.h"
int main(void) {
GPIO_InitTypeDef gpioInitVar;
/*启用时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/* 初始化 PA0 引脚 */
GPIO_StructInit(&gpioInitVar);
gpioInitVar.GPIO_Mode = GPIO_Mode_Out_PP;
gpioInitVar.GPIO_Pin = GPIO_Pin_0;
gpioInitVar.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&gpioInitVar);
/* PA0引脚置0 */
GPIO_ResetBits(GPIOA,GPIO_Pin_0);
while(1);
}
简单说明GPIO的Mode
Stm32的GPIO有8种工作模式
- GPIO_Mode_AIN 模拟输入,GPIO会失效,引脚直接接入内部ADC。
- GPIO_Mode_IN_FLOATING 浮空输入,如果在该引脚悬空(无信号输入)的情况下,读取该端口的电平是不确定的。
- GPIO_Mode_IPD 下拉输入 ,悬空时默认为低电平。
- GPIO_Mode_IPU 上拉输入 ,悬空时默认为高电平。
- GPIO_Mode_Out_OD 开漏输出,当CPU输出逻辑0时,I/O端口输出低电平;而当CPU输出逻辑1时,该引脚处于开漏,也就是浮空状态(高阻态)。
- GPIO_Mode_Out_PP 推挽输出,当CPU输出逻辑0时,I/O端口输出低电平,而当CPU输出逻辑1时,I/O端口输出高电平。
- GPIO_Mode_AF_OD 复用开漏输出。
- GPIO_Mode_AF_PP 复用推挽输出。
- 接着来测试下usart 串口输出是否正常,工程中使用串口输出日志到电脑来调试程序。
先像使用GPIO一样使用USART
#include "stm32f10x.h"
#define DefaultDelayDuration (0xfff)
void delay(u16 duration){ u16 i=110; while(duration--) while(i--); }
const char str[] = "hello world\r\n";
int main(void)
{
u16 i=0,sz=sizeof(str);
USART_InitTypeDef usart1InitVar;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;//TX //´®¿ÚÊä³öPA9
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //¸´ÓÃÍÆÍìÊä³ö
GPIO_Init(GPIOA,&GPIO_InitStructure); /* ³õʼ»¯´®¿ÚÊäÈëIO */
usart1InitVar.USART_BaudRate = 9600;
usart1InitVar.USART_StopBits = USART_StopBits_1;
usart1InitVar.USART_WordLength = USART_WordLength_8b;
usart1InitVar.USART_Parity = USART_Parity_No;
usart1InitVar.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
usart1InitVar.USART_Mode = USART_Mode_Tx;
USART_Init(USART1,&usart1InitVar);
USART_Cmd(USART1, ENABLE); //ʹÄÜ´®¿Ú1
USART_ClearFlag(USART1, USART_FLAG_TC);
while(1)
{
for (i=0;i<sz;++i) {
USART_SendData(USART1,str[i]);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
}
// delay(100);
}
}
烧录测试后,无法在电脑端查看到输出。
查看芯片的引脚定义。
USART1的写端口USART1_TX与PA9是复用的。
所以要启用PA9引脚
#include "stm32f10x.h"
const char str[] = "hello world\r\n";
int main(void)
{
u16 i=0,sz=sizeof(str);
USART_InitTypeDef usart1InitVar;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;// PA9 与 USART1_TX 复用
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_Init(GPIOA,&GPIO_InitStructure);
usart1InitVar.USART_BaudRate = 9600;
usart1InitVar.USART_StopBits = USART_StopBits_1;
usart1InitVar.USART_WordLength = USART_WordLength_8b;
usart1InitVar.USART_Parity = USART_Parity_No;
usart1InitVar.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
usart1InitVar.USART_Mode = USART_Mode_Tx;
USART_Init(USART1,&usart1InitVar);
USART_Cmd(USART1, ENABLE); // 使能 USART1
USART_ClearFlag(USART1, USART_FLAG_TC);
while(1)
{
for (i=0;i<sz;++i) {
USART_SendData(USART1,str[i]);
}
}
}
烧录测试时发现,串口调试工具上是显示有接收数据的,但接受区没看到字符串,可能是太快了,加个延时试试。
#include "stm32f10x.h"
#define DefaultDelayDuration (0xfff)
void delay(u16 duration) {u16 i = 120; while(i--) while(duration--) ; }
const char str[] = "hello world\r\n";
int main(void)
{
u16 i=0,sz=sizeof(str);
USART_InitTypeDef usart1InitVar;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;// PA9 与 USART1_TX 复用
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_Init(GPIOA,&GPIO_InitStructure);
usart1InitVar.USART_BaudRate = 9600;
usart1InitVar.USART_StopBits = USART_StopBits_1;
usart1InitVar.USART_WordLength = USART_WordLength_8b;
usart1InitVar.USART_Parity = USART_Parity_No;
usart1InitVar.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
usart1InitVar.USART_Mode = USART_Mode_Tx;
USART_Init(USART1,&usart1InitVar);
USART_Cmd(USART1, ENABLE); // 使能 USART1
USART_ClearFlag(USART1, USART_FLAG_TC);
while(1)
{
for (i=0;i<sz;++i) {
USART_SendData(USART1,str[i]);
delay(DefaultDelayDuration);
}
}
}
烧录测试可以看到串口调试工具上缓慢的一行行输出hello world
。
其实正确的作法是等待上一个数据传输完成后,再传输下一个数据,把延时改成 while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET);
。
现在重定向一下printf 函数,只要重写 fputc 函数就好了,然后包装一个日志打印,在DEBUG模式下输出日志。
#include "stm32f10x.h"
#include <stdio.h>
int fputc(int ch,FILE *p)
{
USART_SendData(USART1,(u8)ch);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
return ch;
}
#define _DEBUG_
#ifdef _DEBUG_
#define debug_log(args,...) printf(args,##__VA_ARGS__)
#else
#define debug_log(args,...) ((void)0)
#endif
int main(void)
{
USART_InitTypeDef usart1InitVar;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_Init(GPIOA,&GPIO_InitStructure);
usart1InitVar.USART_BaudRate = 9600;
usart1InitVar.USART_StopBits = USART_StopBits_1;
usart1InitVar.USART_WordLength = USART_WordLength_8b;
usart1InitVar.USART_Parity = USART_Parity_No;
usart1InitVar.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
usart1InitVar.USART_Mode = USART_Mode_Tx;
USART_Init(USART1,&usart1InitVar);
USART_Cmd(USART1, ENABLE);
USART_ClearFlag(USART1, USART_FLAG_TC);
while(1)
{
debug_log("hello world\r\n");
}
}
这里用到一个宏函数技巧#define debug_log(args,...) printf(args,##__VA_ARGS__)
下篇继续