OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 3494|回复: 24

书山有路勤为径------2018年元旦

[复制链接]

24

主题

257

帖子

0

精华

高级会员

Rank: 4

积分
771
金钱
771
注册时间
2017-2-19
在线时间
133 小时
发表于 2017-8-16 09:15:43 | 显示全部楼层 |阅读模式
算算时间过得真快,已经进入8月了,还有5个月就要元旦了,开篇帖子记录自己学习STM32的经历。
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

24

主题

257

帖子

0

精华

高级会员

Rank: 4

积分
771
金钱
771
注册时间
2017-2-19
在线时间
133 小时
 楼主| 发表于 2017-8-16 10:47:17 | 显示全部楼层
本帖最后由 刘东君 于 2017-8-16 15:21 编辑

2017/8/15   二、 LED闪烁

1)LED控制pin PE5,PB5。           PB5=1,LED0灭,PB5=0,LED0亮。


                                                    PE5=1,LED1灭,PE5=0,LED1亮。


2)GPIOB端口的初始化,GPIOB是外设,使用外设就要是能相应时钟。

    QQ截图20170816102146.png
    ①GPIOB外设,在APB2下面。所以选择 void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);

         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE,ENABLE); //使能PB,PE时钟


      ②GPIOB端口初始化: 定义结构体变量:GPIO_InitTypeDef GPIO_InitStructure;    // 类似 char  i;


                                         GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;                       //结构体变量的每个成员赋值。
                                         GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
                                         GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;


                                         GPIO_Init(GPIOB,&GPIO_InitStructure);                         //将PB5配置为推挽输出 50MHZ
3)LED初始化: 整理代码


void LED_Init(void)
{
        GPIO_InitTypeDef GPIO_InitStructure;
        
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE,ENABLE); //使能PB,PE时钟
        
        GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;
        GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
        GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
        GPIO_Init(GPIOB,&GPIO_InitStructure);
        GPIO_SetBits(GPIOB,GPIO_Pin_5);

        GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;
        GPIO_Init(GPIOE,&GPIO_InitStructure);
        GPIO_SetBits(GPIOE,GPIO_Pin_5);
               
}
4)编写led.h文件

#ifndef  _LED_H
#define _LED_H
#include "sys.h"


#define LED0_OFF  GPIO_SetBits(GPIOB,GPIO_Pin_5)   
#define LEN0_ON          GPIO_ResetBits(GPIOB,GPIO_Pin_5)


#define LED1_OFF  GPIO_SetBits(GPIOE,GPIO_Pin_5)
#define LEN1_ON          GPIO_ResetBits(GPIOE,GPIO_Pin_5)


void LED_Init(void);


#endif



5)编写 main.c文件
#include "delay.h"
#include "led.h"




int main(void)
{


delay_init();
LED_Init();


while(1)
{
                LED1_OFF;
                LED0_OFF;
                delay_ms(300);
                LEN0_ON;
                LEN1_ON;
                delay_ms(300);
}
}












回复 支持 2 反对 0

使用道具 举报

15

主题

866

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
7645
金钱
7645
注册时间
2016-11-30
在线时间
648 小时
发表于 2017-8-16 09:21:27 | 显示全部楼层
支持!
回复 支持 反对

使用道具 举报

0

主题

12

帖子

0

精华

初级会员

Rank: 2

积分
135
金钱
135
注册时间
2017-7-22
在线时间
16 小时
发表于 2017-8-16 09:30:42 | 显示全部楼层
楼主加油
回复 支持 反对

使用道具 举报

24

主题

257

帖子

0

精华

高级会员

Rank: 4

积分
771
金钱
771
注册时间
2017-2-19
在线时间
133 小时
 楼主| 发表于 2017-8-16 09:41:25 | 显示全部楼层
本帖最后由 刘东君 于 2017-8-16 15:21 编辑

2017/8/15  一、 GPIO

GPIO相关:
1)GPIOA有7个寄存器 GPIOx_CRL、GPIOx_CRH、GPIOx_IDR、GPIOx_ODR、GPIOx_BSRR、GPIOx_BRR、GPIOx_LCKR。
2)GPIOA有PA0~PA15这16个端口IO,
3)GPIOA有8中配置模式,4种输入,4种输出。
     四种输入模式:输入浮空、输入上拉、输入下拉、模拟输入。
     四种输出模式:开漏输出、复用开漏输出、推挽输出、复用推挽输出。
4)寄存器GPIOx_CRL、GPIOx_CRH配置IO口选择哪种工作模式。
5)IDR只读寄存器。unsigned short int  i;                              //定义16位变量
                                   i=GPIOA_IDR;                                  //读取16位变量
6)ODR读写寄存器 。unsigned short int  i;                         //定义16位变量
                                        i=GPIOA_ODR;                         //读取16位变量
                                         GPIOA_ODR=0x8008;             //写入16位变量 、
7)位设置/清除寄存器BSRR。
                                    只用低16位 作为置1使用。           //对应位置1,相应PA1~PA15输出高电平
8)端口位清除寄存器BRR。
                                    只用低16位 作为清0使用。          //对应位置1,相应PA1~PA15输出低电平





回复 支持 反对

使用道具 举报

8

主题

71

帖子

0

精华

初级会员

Rank: 2

积分
183
金钱
183
注册时间
2017-4-5
在线时间
36 小时
发表于 2017-8-16 11:16:11 | 显示全部楼层
顶一顶
回复 支持 反对

使用道具 举报

18

主题

99

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
465
金钱
465
注册时间
2016-1-20
在线时间
89 小时
发表于 2017-8-16 11:18:40 | 显示全部楼层
顶                                       
回复 支持 反对

使用道具 举报

18

主题

139

帖子

0

精华

高级会员

Rank: 4

积分
562
金钱
562
注册时间
2014-10-29
在线时间
229 小时
发表于 2017-8-16 14:06:06 | 显示全部楼层
刘东君 坚持写完啊
回复 支持 反对

使用道具 举报

24

主题

257

帖子

0

精华

高级会员

Rank: 4

积分
771
金钱
771
注册时间
2017-2-19
在线时间
133 小时
 楼主| 发表于 2017-8-16 15:41:28 | 显示全部楼层
本帖最后由 刘东君 于 2017-8-16 15:48 编辑

2017/8/16  三、beep蜂鸣器实验
1) GPIO端口每个端口最大输出电流25mA,整个芯片最大输出电流150mA,蜂鸣器驱动电流30mA。     //PB8 电流1mA(设计)
2) 芯片复位时候GPIO端口处于输入浮空状态。
3)R38的10K电阻消除GPIO复位时候,引脚的不确定状态,确保蜂鸣器不会误响。                              //  Ib=(3.3V-0.7V)/1K=2.6mA。晶体管β=10(最小值)时,Ic=β*Ib=26mA,满足蜂鸣器驱动电流30mA  此晶体管结构属于基极偏置,用于开关电路
   QQ截图20170816152243.png
4)编写BEEP_Init(void)等初始化函数最后,要将输出端口设置为0或者固定状态。 //GPIO_ResetBits(GPIOB,GPIO_Pin_8); 良好习惯
#include "beep.h"
void BEEP_Init(void)
{
        GPIO_InitTypeDef GPIO_InitStructure;
        
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //使能PB,PE时钟
        
        GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8;
        
        GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
        
        GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
        
        GPIO_Init(GPIOB,&GPIO_InitStructure);
        
        GPIO_ResetBits(GPIOB,GPIO_Pin_8);      //良好习惯,
}
5)位带操作----类似51单片机的操作。
//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入

#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入

#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入

#define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入

#define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入

#define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //输出
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //输入

#define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //输出
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //输入

6)如果PA9,PA10复用为串口,位带操作是否可以使用?





回复 支持 反对

使用道具 举报

8

主题

89

帖子

0

精华

高级会员

Rank: 4

积分
686
金钱
686
注册时间
2017-8-8
在线时间
494 小时
发表于 2017-8-16 21:35:25 | 显示全部楼层
坚持啊小胸弟,不要太监了
回复 支持 反对

使用道具 举报

24

主题

257

帖子

0

精华

高级会员

Rank: 4

积分
771
金钱
771
注册时间
2017-2-19
在线时间
133 小时
 楼主| 发表于 2017-8-17 16:11:37 | 显示全部楼层
2017/8/17   三 、按键输入与通用定时器T2
(一) 按键输入
1)战舰V3 四个按键  KEY0、KEY1、KEY2、WK_UP。分别对应PE4、PE3、PE2、PA0;
2)GPIO初始化。其中KEY0、KEY1、KEY2为输入上拉模式,WK_UP 输入下拉模式。
3)程序编写:
(二)通用定时器T2



     



回复 支持 反对

使用道具 举报

2

主题

92

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
327
金钱
327
注册时间
2016-7-9
在线时间
49 小时
发表于 2017-8-17 18:14:05 | 显示全部楼层
顶.......................
回复 支持 反对

使用道具 举报

24

主题

257

帖子

0

精华

高级会员

Rank: 4

积分
771
金钱
771
注册时间
2017-2-19
在线时间
133 小时
 楼主| 发表于 2017-8-18 16:13:24 | 显示全部楼层
2017/8/18  四、时钟系统 QQ截图20170818160539.png

1)STM32有5个时钟源,HSI、HSE、PLL、LSI、LSE,一个时钟输出源MCO。
2)系统时钟来源:HSI、HSE、PLL,设计时候一般应用HSE外部8M晶振作为时钟源,然后经过PLL倍频到72M,最后AHB、APB1、APB2等总线在分频。
3)LSE外接32.768KHZ作为RTC实时时钟使用。
4)LSI独立开门口时钟。
5)USB时钟是48M或者72M
回复 支持 反对

使用道具 举报

1

主题

5

帖子

0

精华

新手上路

积分
23
金钱
23
注册时间
2017-8-17
在线时间
4 小时
发表于 2017-8-18 19:21:50 | 显示全部楼层
加油。。。。。。
回复 支持 反对

使用道具 举报

24

主题

257

帖子

0

精华

高级会员

Rank: 4

积分
771
金钱
771
注册时间
2017-2-19
在线时间
133 小时
 楼主| 发表于 2017-8-23 09:22:05 | 显示全部楼层
2017/8/23  五、 端口复用及重映射

1)GPIO与外设串口、ADC、DAC公用一个引脚,需要配置寄存器确定引脚是作为GPIO使用,还是作为外设使用。
2)端口复用步骤:
①IO口时钟使能。
②复用功能时钟使能。   //比如串口,就是串口时钟使能。
③IO口引脚8中配置模式查询。确定复用功能使用哪种模式。
④IO口初始化。
3)重映射目前不学习,使用概率比较低。
回复 支持 反对

使用道具 举报

17

主题

231

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2085
金钱
2085
注册时间
2016-8-8
在线时间
362 小时
发表于 2017-8-23 09:25:24 | 显示全部楼层
加油 我觉得自己也应该学习下你 开个贴记录下学习过程
黑夜给了我黑色的眼睛,我却用它来寻找白花花的银子,黄灿灿的金子,以及红彤彤的毛爷爷
回复 支持 反对

使用道具 举报

24

主题

257

帖子

0

精华

高级会员

Rank: 4

积分
771
金钱
771
注册时间
2017-2-19
在线时间
133 小时
 楼主| 发表于 2017-8-23 10:12:47 | 显示全部楼层
2017/8/24 六、NVIC中断优先级

1)NVIC中断的优先级分为两类:抢占优先级、响应优先级、由4位来控制。
2)中断优先级分配,就是确定4位中,那几位用来控制抢占优先级、那几位控制响应优先级。       //例如 2、2 。
3)抢占优先级高的(0>1)可以打断抢占优先级低的。
4)抢占优先级一样。响应优先级那个先响应,那个先执行,无打断操作。
5)抢占优先级一样,2个中断同时发生,响应优先级高的先执行。
6)NVIC中断的优先级只在开始配置一次,禁止中间修改。
7)中断配置步骤:
①系统运行后先设置中断优先级分组。调用函数:
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
整个系统执行过程中,只设置一次中断分组。
②针对每个中断,设置对应的抢占优先级和响应优先级:
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
③中断初始化函数


回复 支持 反对

使用道具 举报

24

主题

257

帖子

0

精华

高级会员

Rank: 4

积分
771
金钱
771
注册时间
2017-2-19
在线时间
133 小时
 楼主| 发表于 2017-8-24 09:43:13 | 显示全部楼层
2017/8/24  七  ADC采集

1) STM32拥有三个ADC采集通道,精度12位。
QQ截图20170824090936.png
2)ADC单次转换
//获得ADC值
//ch:通道值 0~3
u16 Get_Adc(u8 ch)   
{
          //设置指定ADC的规则组通道,一个序列,采样时间
        ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_1Cycles5 );        //ADC1,ADC通道,采样时间为239.5周期                                      

        ADC_SoftwareStartConvCmd(ADC1, ENABLE);                //使能指定的ADC1的软件转换启动功能       
         
        while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束

        return ADC_GetConversionValue(ADC1);        //返回最近一次ADC1规则组的转换结果
}

u16 Get_Adc_Average(u8 ch,u8 times)
{
        u32 temp_val=0;
        u8 t;
        for(t=0;t<times;t++)
        {
                temp_val+=Get_Adc(ch);
                delay_us(2);
        }
        return temp_val/times;
}        

        while(1)
        {

                static u8 i=0;
               
                                        adcx[0]=Get_Adc_Average(ADC_Channel_10,10);   //3ms 执行完成 4次ADC转换。
                                        adcx[1]=Get_Adc_Average(ADC_Channel_11,10);
                                        adcx[2]=Get_Adc_Average(ADC_Channel_12,10);
                                        adcx[3]=Get_Adc_Average(ADC_Channel_13,10);


                //adcx=Get_Adc_Average(ADC_Channel_1,10);
                LCD_ShowxNum(156,130+i*40,adcx,4,16,0);//显示ADC的值
                temp=(float)adcx*(3.3/4096);
                adcx=temp;
                LCD_ShowxNum(156,150+i*40,adcx,1,16,0);//显示电压值
                temp-=adcx;
                temp*=1000;
                LCD_ShowxNum(172,150+i*40,temp,3,16,0X80);
                LED0=!LED0;
                delay_ms(200);
                if(i>=3)
                {
                i=0;
                }
                else
                {
                        i++;
                }

               
        }
}


经测试执行4个通道采集任务最快1ms,此时屏蔽 delay_us(2);延时,设置单次采用时间为:ADC_SampleTime_1Cycles5 ,目前采用delay_us(2),实测时间为3ms


3)连续模式-----待续



回复 支持 反对

使用道具 举报

0

主题

125

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
221
金钱
221
注册时间
2017-5-26
在线时间
76 小时
发表于 2017-8-24 10:11:46 来自手机 | 显示全部楼层
向楼主学习!
回复 支持 反对

使用道具 举报

10

主题

158

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
262
金钱
262
注册时间
2017-4-20
在线时间
102 小时
发表于 2017-8-24 16:49:30 | 显示全部楼层
可以 加油~
回复 支持 反对

使用道具 举报

24

主题

257

帖子

0

精华

高级会员

Rank: 4

积分
771
金钱
771
注册时间
2017-2-19
在线时间
133 小时
 楼主| 发表于 2017-8-25 08:26:25 | 显示全部楼层

硬件时序逻辑,都是基于状态机工作,硬件是同时运行的,不存在分时问题(MCU核和定时器就可以相互独立运行的),但程序一个时刻只能做一件事,除非是多核。要把时间分片,就要有时基,裸奔的是自己分片,状态机迁移,时间基准就用一个定时器。就构成基于时基和状态机的单片机程序结构。做控制的化基本没有delay函数,除了通信等要求时基小的应用。
回复 支持 反对

使用道具 举报

24

主题

257

帖子

0

精华

高级会员

Rank: 4

积分
771
金钱
771
注册时间
2017-2-19
在线时间
133 小时
 楼主| 发表于 2017-9-5 16:48:24 | 显示全部楼层
本帖最后由 刘东君 于 2017-9-5 16:50 编辑

今天终于解决了这个问题,发出来共大家使用。

用J-Link进行硬件仿真时,如何观察某个函数的运行时间。

1)按照下图配置,选择硬件仿真。注意红色框框,点击Settings进入第二步
1.png
2)模式选择SW模式,然后点击Trace选项,进行第三步

2.png
3)勾选红色框框,然后配置系统时钟为72M,我的是72M,所以配置为72M,进入第四步
3.png
4)进入Debug,设置断点,然后运行到断点停止,注意右下角t1的时间点击右键将t1清零,进入第五步
4.png
5.png

5)点击运行,观察时间。
6.png
6)时间误差非常小,又学到一招。





回复 支持 反对

使用道具 举报

56

主题

520

帖子

0

精华

高级会员

Rank: 4

积分
964
金钱
964
注册时间
2014-11-18
在线时间
160 小时
发表于 2017-9-5 16:54:50 | 显示全部楼层
楼主牛逼阿。加油,看好你。
自己选择的路,成家前走完。
回复 支持 反对

使用道具 举报

24

主题

257

帖子

0

精华

高级会员

Rank: 4

积分
771
金钱
771
注册时间
2017-2-19
在线时间
133 小时
 楼主| 发表于 2017-10-12 14:57:17 | 显示全部楼层
继续,项目忙完了,接着搞起。

结构体与指针:


声明如下结构体:
typedef struct _BlockType_t
{
    unsigned long k;
    unsigned long * stackPtr;       
       
}BlockType_t;


定义两个变量:
BlockType_t  block;
BlockType_t * blockPtr;



void main()
{
       block.k=0x123;                                                              //结构体的成员变量赋值
       block.stackPtr=(unsigned long *) 0x2000 0011;              // 结构体成员变量赋值,因为成员stackPtr为指针变量,所以要赋值地址
       *( unsigned long *) (0X20000011 )=0x6;                       //向地址2000 0011写入数据0x6;
      *(block.stackPtr)=0x08;                                                //向地址2000 0011写入数据0x8;
      blockPtr = &block;                                                        //向结构体类型指针blockPtr赋地址
      while(1);
}

下面调试:

&block           地址:2000  0000
&block.k        地址: 2000 0000
block.k          值:0x123; 存放在 地址2000 0000

&block.stackPtr   地址:2000 0004
block.stackPtr     值:2000 0011(仍然是地址)——>值06,08;


blockPtr ->k    值0x123;
&(blockPtr ->k )   地址:2000 0000


注意下列写法错误:
blockPtr.k
blockPtr. stackPtr   

因为blockPtr是指针变量!blockPtr是指针变量!blockPtr是指针变量!而不是结构体变量。








回复 支持 反对

使用道具 举报

24

主题

257

帖子

0

精华

高级会员

Rank: 4

积分
771
金钱
771
注册时间
2017-2-19
在线时间
133 小时
 楼主| 发表于 2017-10-18 09:53:37 | 显示全部楼层
本帖最后由 刘东君 于 2017-10-18 09:59 编辑

指针与数组:

int i,k,m,n,h,g;
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1);
int *ptr1=(int *)(&a[1]+1);
int *q=(int *)(a+1);
int *z=a+1;

int main ()
{
        i=*(a+1);
        k=*(ptr-1);
        m=*ptr1;
        n=*q;
        h=sizeof(a);
        g=sizeof(a[0]);
}
QQ图片20171018095311.png
注意:
ptr1=&a;
ptr2=a;
ptr3=&a[0];

中 ptr1=ptr2=ptr3;
但是:
ptr4=&a+1;
ptr5=a+1;
ptr6=&a[0]+1;

ptr4不等于ptr5=ptr6;












回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-7-22 06:42

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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