发 帖  
原厂入驻New
发烧友10周年庆典,全网超值优惠来袭!千元现金券,下单抽奖赶紧参与》》
[讨论] 朱兆祺STM32项目(硬件设计、软件编程)手记(连载)
2013-10-2 15:38:51  52013
分享
本帖最后由 zzq宁静致远 于 2013-11-21 18:37 编辑

    8月初以来,在论坛,“朱兆祺带你一步一步学习嵌入式(连载)”(http://bbs.elecfans.com/jishu_357014_1_1.html)以及“朱兆祺教你如何攻破C语言学习、笔试与机试的难点(连载)”(http://bbs.elecfans.com/jishu_354666_1_1.html)得到大家的认可,说明这两个帖子对大家有很大的帮助。这两个帖子都是我接触C语言以来到现在承接单片机ARM项目的经验总结,我希望通过这两个帖子,各位嵌入式学习者能得到正能量,以最快的方式进步。C语言这个帖子我已经引领网友进入实战项目中,我的C语言功底不是单纯传授书本知识,更重要的是能让各位领略到我这个帖子对你做项目到底有多大的用处。
    网友认为,朱兆祺是不是只会编程,只会C语言,只会Linux。OK,朱兆祺趁这个国庆假期,我想此刻很多网友都在游历祖国山河,我就将使用STM32做过的项目小做一下整理,由浅入深、深入浅出,告诉你一个从未接触过STM32的学习者,怎么去做好一个项目,怎样可以代码重用,怎样给项目预留好升级空间。既然是教大家,就使用STM32最强大的那款(STM32F103ZET6、144个引脚),以这个为控制芯片。其实只要你做好模块化编程,控制芯片早已不重要(为什么?后面你会恍然大悟的)。
    在进行STM32学习过程中,你们手中最好有一块STM32板子:
http://item.taobao.com/item.htm?spm=a1z10.1.w137712-3105488089.8.X83zzd&id=22208871338

购买的时候告诉客服你是看到“朱兆祺STM32项目(硬件设计、软件编程)手记(连载)”购买的,只需要49元,但是每人仅能购买一块。
    朱兆祺STM32项目(硬件设计、软件编程)手记(连载)正式开始。

朱兆祺STM32项目手记目录

第一节  STM32核心板介绍:
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=385613&pid=2309703&fromuid=222350

第二节  STM32小试牛刀——LED
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=385613&pid=2309723&fromuid=222350


第三节  UART——调试帮手:
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=385613&pid=2309734&fromuid=222350

第四节  直流电机PID控制:
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=385613&pid=2313310&fromuid=222350

第五节  直流电机PID控制程序:
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=385613&pid=2330272&fromuid=222350

第六节  指针函数在STM32程序中的巧妙应用
http://bbs.elecfans.com/forum.ph ... &fromuid=222350


第七节  PID在智能小车中的应用
http://bbs.elecfans.com/forum.ph ... 2926&fromuid=222350

第八节  关于BOOT问题
http://bbs.elecfans.com/forum.php?mod=redirect&goto=findpost&ptid=385613&pid=2420842&fromuid=222350

     












134
2013-10-2 15:38:51   1 评论 分享淘帖
1 条评论
197 个讨论
本帖最后由 dongyumin 于 2013-10-2 21:42 编辑

第一节   STM32核心板介绍



    控制芯片为:STM32F103ZET6,144个引脚。在实际项目中,不是说功能越强大就选择它,而是要从芯片的优点,价格等多方面考虑。但是在这里,你们学习必须要一个载体,折中即选择它了。图片见附件1:STM32F103ZET6
STM32F103ZET6

引脚定义。图片见附件2:引脚定义。
引脚定义


在每个STM32的芯片上都有两个管脚BOOT0和BOOT1(PB2),这两个管脚在芯片复位时的电平状态决定了芯片复位后从哪个区域开始执行程序。
(1)BOOT1=xBOOT0=0从用户闪存启动,这是正常的工作模式。
(2)BOOT1=0BOOT0=1从系统存储器启动,这种模式启动的程序功能由厂家设置。
(3)BOOT1=1BOOT0=1从内置SRAM启动,这种模式可以用于调试。
大家可以通过跳冒的连接设置不同的执行程序,一般BOOT0和BOOT1(PB2)跳线都跳到GND(地)。
图片见附件3:BOOT。
BOOT

这里LED采用灌入式驱动,R = (3.3V - V(LED))/ (LED的驱动电流),这就是LED电阻的算法,这个是限流电阻。大家记得举一反三。
图片见附件4:LED。
LED

复位电路,右边引脚是连接NRST,当电源一上电,由于电阻两端电压不能突变,此时NRST为低电平,此时STM32芯片复位;当充电到高电平(一般来说到1.8V就认为是高电平),STM32工作;当你需要对其复位,按下KEY,NRST被拉为低电平,复位。
图片见附件5:复位电路。
复位电路

这是USB供电电路与USB驱动电路。
图片见附件6:USB。
USB
至于其他功能我们在往后接触到了再一一讲解。







本帖最后由 zzq宁静致远 于 2013-10-12 09:47 编辑

第二节  STM32小试牛刀——LED


    这是基本上所有的学习者第一步学习的程序,因为简单。但是我看过的程序都是千篇一律,其实再简单的一个程序都要注重模块化编程,这是朱兆祺要告诉大家的第一个经验,因为只有这样,你的代码才能复用。

我们看下最底层我们的配置,这一层是直接和底层硬件打交道:
/********************************************************************
**  函数名称:LED_Configuration
** 函数功能:对LED1-LED5的配置
**  入口参数:None
** 出口参数:None
********************************************************************/
void LED_Configuration (void)
{
    /*
        LED_InitStructure  structure
        typedef struct
        {
            u16 GPIO_Pin;
            GPIOSpeed_TypeDef GPIO_Speed;
            GPIOMode_TypeDef GPIO_Mode;
        } GPIO_InitTypeDef ;
    */
GPIO_InitTypeDef GPIO_InitStructure;

    /* Configure all unused GPIO port pins in Analog Input mode (floating input
    trigger OFF), this will reduce the power consumption and increase the device
    immunity against EMI/EMC *************************************************/
    /* 将GPIOA、GPIOB、GPIOC、GPIOD、GPIOE使能 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |
                           RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD |
                           RCC_APB2Periph_GPIOE, ENABLE);

    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_All;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
/*
        void GPIO_Init(GPIO_TypeDef* GPIOx,
                       GPIO_InitTypeDef* GPIO_InitStruct)
        根据GPIO_InitStruct中指定的参数初始化外设GPIOx寄存器
        
*/
#if 0
    GPIO_Init(GPIOA | GPIOB | GPIOC | GPIOD | GPIOE ,
              &GPIO_InitStructure) ;
#endif

#if 1
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    GPIO_Init(GPIOD, &GPIO_InitStructure);
    GPIO_Init(GPIOE, &GPIO_InitStructure);
#endif
    /* 将GPIOA、GPIOB、GPIOC、GPIOD、GPIOE禁能 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |
                           RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD |
                           RCC_APB2Periph_GPIOE, DISABLE);
         
#ifdef USE_STM3210E_EVAL
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF | RCC_APB2Periph_GPIOG, ENABLE);
    GPIO_Init(GPIOF, &GPIO_InitStructure);
    GPIO_Init(GPIOG, &GPIO_InitStructure);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF | RCC_APB2Periph_GPIOG, DISABLE);
#endif /* USE_STM3210E_EVAL */

    /* Configure IO connected to LD1, LD2, LD3 and LD4 leds *********************/
    /* Enable GPIO_LED clock */
    /*  
      #define GPIO_LED                   GPIOF   
      #define RCC_APB2Periph_GPIO_LED    RCC_APB2Periph_GPIOF
      使能GPIOF
    */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO_LED, ENABLE);
    /*
        GPIOF的678910四个引脚控制五个LED
        0000 0000 0000 0000
    */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIO_LED, &GPIO_InitStructure);
    /*
        void GPIO_Write(GPIO_TypeDef* GPIOx, u16 PortVal)
        向指定GPIO数据端口写入数据
        GPIOx:x可以是A,B,C,D或者E,来选择GPIO外设
        PortVal: 待写入端口数据寄存器的值
    */
    GPIO_Write(GPIOF, 0xffff);
}

底层文件配置完成之后,中间层是作为底层硬件和上层应用之间的枢纽:
#ifndef   _LEDCONFIG_H_
#define   _LEDCONFIG_H_
/* 包含配置文件 */
#include "stm32f10x_lib.h"
#include "platform_config.h"

/* 连接LED1引脚输出高电平,即LED1熄灭 */
#define   LED1OFF()    GPIO_SetBits(GPIO_LED , GPIO_Pin_6)      
/* 连接LED1引脚输出低电平,即LED1点亮 */
#define   LED1ON()     GPIO_ResetBits(GPIO_LED , GPIO_Pin_6)   

/* 连接LED2引脚输出高电平,即LED2熄灭 */
#define   LED2OFF()    GPIO_SetBits(GPIO_LED , GPIO_Pin_7)      
/* 连接LED2引脚输出低电平,即LED2点亮 */
#define   LED2ON()     GPIO_ResetBits(GPIO_LED , GPIO_Pin_7)     

/* 连接LED3引脚输出高电平,即LED3熄灭 */
#define   LED3OFF()    GPIO_SetBits(GPIO_LED , GPIO_Pin_8)      
/* 连接LED3引脚输出低电平,即LED3点亮 */
#define   LED3ON()     GPIO_ResetBits(GPIO_LED , GPIO_Pin_8)   

/* 连接LED4引脚输出高电平,即LED4熄灭 */
#define   LED4OFF()    GPIO_SetBits(GPIO_LED , GPIO_Pin_9)      
/* 连接LED4引脚输出低电平,即LED4点亮 */
#define   LED4ON()     GPIO_ResetBits(GPIO_LED , GPIO_Pin_9)     

/* 连接LED5引脚输出高电平,即LED5熄灭 */
#define   LED5OFF()    GPIO_SetBits(GPIO_LED , GPIO_Pin_10)      
/* 连接LED5引脚输出低电平,即LED5点亮 */
#define   LED5ON()     GPIO_ResetBits(GPIO_LED , GPIO_Pin_10)     


#endif

作为顶层文件,是单独的一个文件,这一层所要具备的是不管放在什么平台、什么系统下面都可以运行。
int main(void)
{
      
#ifdef DEBUG
    debug();
#endif
/* 系统时钟配置 */
    RCC_Configuration();   
/* 中断向量配置 */
    NVIC_Configuration();
/* LED配置 */
    LED_Configuration();
    while (1)
    {
      
  LED1ON();
  
    }
}


    通过这个程序,你看到了是哪个引脚在控制LED灯吗?不知道,那么,我换一个平台是不是照样可以运行。其实还可以进一步进行包装,但是要注意,是不是模块化怎么样就怎么样,非也,技术含量就在于此。


本帖最后由 zzq宁静致远 于 2013-10-12 09:51 编辑

第三节  UART——调试帮手

是不是改到定时器了,对,这是惯例,朱兆祺不是一个走惯例的人,UART,因为这个东西,会给你输出你想要的任何东西,这样你才能了解STM32此刻在干什么。比如你在追你的女神,你压根就不知道别人在想什么,你拿什么去追?用钱去砸还是死皮赖脸纠缠人家。知己知彼方能百战不殆。我将我想要的信息全部通过UART打印出来,我就不信我不知道你在想什么做什么。

按照我们上一节的模块化思想,最底层为configuration.c文件,我们可以将所有底层配置都放在这个文件中,作为底层驱动文件。
/********************************************************************
**  函数名称:UART1_Configuration
** 函数功能:对UART1的配置
**  入口参数:None
** 出口参数:None
********************************************************************/
void UART1_Configuration (void)
{
    /*
        typedef struct
        {
            u32 USART_BaudRate;
            u16 USART_WordLength;
            u16 USART_StopBits;
            u16 USART_Parity;
            u16 USART_Mode;
            u16 USART_HardwareFlowControl;  
        } USART_InitTypeDef;
*/
USART_InitTypeDef       USART_InitStructure;
/*
        typedef struct
        {
            u16 USART_Clock;
            u16 USART_CPOL;
            u16 USART_CPHA;
            u16 USART_LastBit;
        } USART_ClockInitTypeDef;
*/
    USART_ClockInitTypeDef  USART_ClockInitStructure;

    /*
        将GPIOA_Pin9和GPIOA_Pin10设置为UART模式
*/
    GPIO_InitTypeDef        GPIO_InitStructure;
    /* Enable USARTx clocks */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_AFIO, ENABLE);
    /* 打开GPIOA时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE) ;
    /* Configure USART1 Tx (PA.09) as alternate function push-pull */
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9;
/* 复用推挽输出 */
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
   
    /* Configure USART1 Rx (PA.10) as input floating */
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_10;
/* 浮空输入 */
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /******************************************************
        UART的配置
******************************************************/
    /*
        USART_CLOCK提示了USART时钟使能还是失能
        USART_Clock_Enable :时钟高电平活动
        USART_Clock_Disable:时钟低电平活动
    */
    USART_ClockInitStructure.USART_Clock   = USART_Clock_Disable;
    /*
        USART_CPOL指定了SLCK引脚上时钟输出的极性
        USART_CPOL_High:时钟高电平
        USART_CPOL_Low :时钟低电平
*/
USART_ClockInitStructure.USART_CPOL    = USART_CPOL_Low;
    /*
        USART_CPHA指定了下SLCK引脚上时钟输出的相位,
        和CPOL位一起配合来产生用户希望的时钟/数据的采样关系。
        USART_CPHA_1Edge:时钟第一个边沿进行数据捕获
        USART_CPHA_2Edge:时钟第二个边沿进行数据捕获
*/
USART_ClockInitStructure.USART_CPHA    = USART_CPHA_2Edge;
    /*
        USART_LastBit来控制是否在同步模式下,
        在SCLK引脚上输出最后发送的那个数据字
        (MSB)对应的时钟脉冲。
        USART_LastBit_Disable:最后一位数据的时钟脉冲不从SCLK输出
        USART_LastBit_Enable :最后一位数据的时钟脉冲从SCLK输出
*/
USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable;
    /* Configure the USART1 synchronous paramters */
    USART_ClockInit(USART1, &USART_ClockInitStructure);

    /*
        该成员设置了USART传输的波特率
*/
    USART_InitStructure.USART_BaudRate     = 115200;
/*
        USART_WordLength提示了在一个帧中传输或者接收到的数据位数
        USART_WordLength_8b:8位数据
        USART_WordLength_9b:9位数据
*/
    USART_InitStructure.USART_WordLength   = USART_WordLength_8b;
    /*
        USART_StopBits定义了发送的停止位数目
        USART_StopBits_1:在帧结尾传输1个停止位
        USART_StopBits_0.5:在帧结尾传输0.5个停止位
        USART_StopBits_2:在帧结尾传输2个停止位
        USART_StopBits_1.5:在帧结尾传输1.5个停止位
*/
USART_InitStructure.USART_StopBits     = USART_StopBits_1;
    /*
        USART_Parity定义了奇偶模式
        USART_Parity_No:奇偶失能
        USART_Parity_Even:偶模式
        USART_Parity_Odd:奇模式
*/
USART_InitStructure.USART_Parity       = USART_Parity_No ;
    /*
        USART_HardwareFlowControl指定了硬件流控制模式使能还是失能
        USART_HardwareFlowControl_None:硬件流控制失能
        USART_HardwareFlowControl_RTS:发送请求RTS使能
        USART_HardwareFlowControl_CTS:清除发送CTS使能
        USART_HardwareFlowControl_RTS_CTS:RTS和CTS使能
*/
USART_InitStructure.USART_HardwareFlowControl =   
                          USART_HardwareFlowControl_None;
    /*
        USART_Mode指定了使能或者失能发送和接收模式
        USART_Mode_Tx:发送使能
        USART_Mode_Rx:接收使能
*/
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    /* Configure USART1 basic and asynchronous paramters */
    USART_Init(USART1, &USART_InitStructure);
    //USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

    /* Enable USART1 */
    USART_Cmd(USART1, ENABLE);

}


接下来是中间枢纽:
#include "stm32f10x_lib.h"
#include "platform_config.h"
#include "UART1Config.h"
#include <stdio.h>
/********************************************************************
**  函数名称:UART1SendByte
** 函数功能:UART1字节发送数据
**  入口参数:None
** 出口参数:None
********************************************************************/
void UART1SendByte(unsigned short int usiData)
{
    while (!(USART1->SR & USART_FLAG_TC));
    USART_SendData(USART1, usiData);
}

/********************************************************************
**  函数名称:UART1Write
** 函数功能:UART1写数据
**  入口参数:None
** 出口参数:None
********************************************************************/
void UART1Write(unsigned char *ucpData, unsigned short int usiLen)
{
    u16 i;
for (i=0; i<usiLen; i++)
{
  UART1SendByte(ucpData);
}
}

/********************************************************************
**  函数名称:fputc
** 函数功能:
**  入口参数:None
** 出口参数:None
********************************************************************/
int fputc(int ch, FILE *f)
{
    /* Write a character to the USART */
    USART_SendData(USARTx, (unsigned char)ch);
    /* Loop until the end of transmission */
    while (USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET) {
    }
  return ch;
}

最后顶层为应用层:
/* UART1配置 */
UART1_Configuration();
    UART1Write((unsigned char*)"\r\n串口初始化成功.....\r\n",
          sizeof("串口初始化成功.....")) ;


我在这里再次强调模块化编程思想的重要性,这个思想不仅能让你的程序瞬间逆袭,还能为你以后的编程达到事半功倍的效果。

评分

参与人数 1威望 +1 +1 收起 理由
CrazyMCU + 1 + 1 您的帖子很精彩,期待您分享的下一个帖子!.

查看全部评分

顶一个
2013-10-2 20:42:44 评论

举报

期待后续~
2013-10-2 22:00:44 评论

举报

前排留名。楼主加油
2013-10-2 22:01:29 评论

举报

初学者,必须赞一个
2013-10-3 10:27:07 评论

举报

搬个小板凳来听朱老师讲课
2013-10-3 12:39:18 评论

举报

强烈需要这么详细的文章!!!
2013-10-3 15:37:28 评论

举报

支持,留名,待学习
2013-10-3 22:42:30 评论

举报

第四节  直流电机PID控制

直流电机控制,要想精准、闭环控制,那么肯定少不了PID控制。网友看到这里是不是该疑惑了,你不是学习嵌入式Linux的嘛?你怎么懂工业控制呢,别忘了,我的专业是自动化,自动化中最经典的代表专业课程是运动控制原理,这课程可以说是PID控制的根本。
这一节,我通过PCB设计到PID程序,一一给你解剖清楚。控制组在全国电子设计竞赛中占有比较重要的分量,在校做控制组的学生们,这一节对你们绝对分量十足。在工业控制中,PID调节更是举足轻重。以我多年做控制的经验,以驱动两个直流电机为导航,深入解析下直流电机的PID控制。其实在很多环境下都要使用到PID调节,学会PID调节,百益而无一害。

直流电机驱动原理图见附件:直流电机驱动原理图。
使用L298控制两个直流电机。

PID程序见下节,精彩继续。

直流电机驱动原理图

直流电机驱动原理图
{:1:}{:1:}{:1:}{:1:}{:1:}{:1:}{:1:}{:1:}{:1:}{:1:}{:1:}
2013-10-4 08:18:33 评论

举报

搬个凳子,那个笔记,来听课,就是太高深了,迷糊
很不错,小伙计看起来也很年轻,哈哈,帮顶~~~~~~~
2013-10-4 11:22:42 评论

举报

期待楼主多分享一些工程方面的经验,这些小例程就一带而过吧!楼下不喜勿喷!
2013-10-5 02:50:53 评论

举报

期待楼主多分享一些工程方面的经验
2013-10-5 09:33:15 评论

举报

angel_3311 发表于 2013-10-5 02:50
期待楼主多分享一些工程方面的经验,这些小例程就一带而过吧!楼下不喜勿喷! ...

关注后续,绝对工程经验,分量十足
谢谢分享               
2013-10-8 09:26:58 评论

举报

嗯,不错,好好学习一下。
2013-10-8 20:13:07 评论

举报

撰写讨论

你正在撰写讨论

如果你是对讨论或其他讨论精选点评或询问,请使用“评论”功能。

高级模式
您需要登录后才可以回帖 登录 | 注册

发讨论
课程
    关闭

    站长推荐 上一条 /10 下一条

    快速回复 返回顶部 返回列表