原创 跟我学STM32之1 跑马灯

2009-12-31 20:21 5932 10 15 分类: MCU/ 嵌入式

写在前面的话
    自从去年接触STM32以来,已经有一年多了,看上STM32,当时确实是看上他的性价比,可以用串口下载,编译器也有KEIL支持,以前用过51,后面转学AVR,把KEIL放到一边了,大概两三年没动KEIL了,但是当时我认为,自己也快毕业了,大学里面就学了51和AVR,MSP430做过板子,但是不能编程,可能我的设计有问题,于是就没去弄它了,也接触过凌阳的61板子,玩了玩语音识别,到后大三的时候就想学ARM了,因为很多时候AVR资源确实是不够的,我做HIFI耳放就体会到了这一点.后面左看右看看上了LPC的2148,于是网购了一片,专门为她设计了一款板子,结果还是不能下载,良久,无果,弃之.
     某日,得TFTLCD一片,喜之,用AVR驱动,终成功! 结果发现MEGA32,存储的BMP图片才一点点大,我想这样是不行了,得升级,至少也要搞个全屏的图片出来.于是又开始瞄上了ARM,加上之前失败的教训和AVR资源不足的缺点,仔细选型,最终看上了STM32.价格低,编程方便,资源多. 于是下狠心,买了两片回来,并为其设计了一个开发板.于是开始了我的STM32之旅...
    转眼一年过去了,STM32上的资源也大部分过了一遍,从最基本的IO操作,到DMA,现在还剩下USB,CAN等没整,等以后整了之后再和大家分享.
    现在STM32在网友间用的已经很普遍了,这里我只是把我当时学习的一些代码整理一下发上来,给需要的网友参考借鉴.也欢迎各位指出我代码里面的错误,感激不尽!
    本人有强烈的控制欲,所以不屑用库,所有代码都是对寄存器操作,任何问题都可以很快找到答案,对库有癖好的就可以不用看了.灯论坛的板子好了,我会移植到论坛的板子上验证一下.本人文采不好,就不再废话了,希望我的代码能帮到各位,如果对我的代码有任何疑问,欢迎提问.共勉!
                                                              正点原子@深圳南山一农民房
                                                              2009/12/22
跑马灯
刚开始学STM32,最烦的莫过于繁多的寄存器了,如果你想找STM32的书,我劝你还是免了,能写STM32的书还没问世,至少我没见到像样的.无非是抄数据手册的.所以,要求不高的话,建议拿一本<<CM权威指南>>+<<STM32用户手册>>+KEIL自带的例程边看边学,这样很容易上手.因为KEIL可以电脑仿真,还提供一些寄存器才查看,这样就会比较明白.
对于时钟的操作,我有了一些比较好的设置函数,放在sys.c里面,具体的如下:
#include <stm32f10x_lib.h>
#include "sys.h"
//系统时钟初始化
//使用外部8M晶振,PLL到72M频率      
//正点原子@SCUT
//2009/12/09
//V1.3
//把NVIC KO了,没有使用任何库文件!
//中断初始化 RTC,USART已经测试,其他待测试!
//设置向量表偏移地址
//NVIC_VectTab:基址
//Offset:偏移量
//CHECK OK
//091207
void MY_NVIC_SetVectorTable(u32 NVIC_VectTab, u32 Offset)  
{
   //检查参数合法性
assert_param(IS_NVIC_VECTTAB(NVIC_VectTab));
assert_param(IS_NVIC_OFFSET(Offset));   
SCB->VTOR = NVIC_VectTab|(Offset & (u32)0x1FFFFF80);//设置NVIC的向量表偏移寄存器
//用于标识向量表是在CODE区还是在RAM区
}
//设置NVIC分组
//NVIC_Group:NVIC分组 0~4 总共5组
//CHECK OK
//091209
void MY_NVIC_PriorityGroupConfig(u8 NVIC_Group)  
{
u32 temp,temp1;   
temp1=(~NVIC_Group)&0x07;//取后三位
temp1<<=8;
temp=SCB->AIRCR;  //读取先前的设置
temp&=0X0000F8FF; //清空先前分组
temp|=0X05FA0000; //写入钥匙
temp|=temp1;   
SCB->AIRCR=temp;  //设置分组               
}
//设置NVIC
//NVIC_PreemptionPriority:抢占优先级
//NVIC_SubPriority       :响应优先级
//NVIC_Channel           :中断编号
//NVIC_Group             :中断分组
//注意优先级不能超过设定的组的范围!否则会有意想不到的错误
//CHECK OK
//091209
void MY_NVIC_Init(u8 NVIC_PreemptionPriority,u8 NVIC_SubPriority,u8 NVIC_Channel,u8 NVIC_Group)  
{
u32 temp;
u8 IPRADDR="NVIC"_Channel/4;  //每组只能存4个,得到组地址
u8 IPROFFSET="NVIC"_Channel%4;//在组内的偏移
IPROFFSET=IPROFFSET*8+4;    //得到偏移的确切位置
//MY_NVIC_PriorityGroupConfig(NVIC_Group);//设置分组
temp=NVIC_PreemptionPriority<<(4-NVIC_Group);   
temp|=NVIC_SubPriority&(0x0f>>NVIC_Group);
temp&=0xf;//取低四位
if(NVIC_Channel<32)NVIC->ISER[0]|=1<<NVIC_Channel;//使能中断位(要清除的话,相反操作就OK)
else NVIC->ISER[1]|=1<<(NVIC_Channel-32);   
NVIC->IPR[IPRADDR]|=temp<<IPROFFSET;//设置响应优先级和抢断优先级                  
}
//不能在这里执行所有外设复位!否则至少引起串口不工作.      
//把所有时钟寄存器复位
//CHECK OK
//091209
void MYRCC_DeInit(void)
{                    
RCC->APB1RSTR = 0x00000000;//复位结束   
RCC->APB2RSTR = 0x00000000;
   
   RCC->AHBENR = 0x00000014;  //flash时钟,闪存时钟使能.DMA时钟关闭   
   RCC->APB2ENR = 0x00000000; //外设时钟关闭.      
   RCC->APB1ENR = 0x00000000;   
RCC->CR |= 0x00000001;     //使能内部高速时钟HSION                  
RCC->CFGR &= 0xF8FF0000;   //复位SW[1:0],HPRE[3:0],PPRE1[2:0],PPRE2[2:0],ADCPRE[1:0],MCO[2:0]      
RCC->CR &= 0xFEF6FFFF;     //复位HSEON,CSSON,PLLON
RCC->CR &= 0xFFFBFFFF;     //复位HSEBYP      
RCC->CFGR &= 0xFF80FFFF;   //复位PLLSRC, PLLXTPRE, PLLMUL[3:0] and USBPRE
RCC->CIR = 0x00000000;     //关闭所有中断
//配置向量表      
#ifdef  VECT_TAB_RAM
MY_NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);
#else   
MY_NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
#endif
}
//THUMB指令不支持汇编内联
//采用如下方法实现执行汇编指令WFI
//CHECK OK
//091209
__asm void WFI_SET(void)
{
WFI;   
}
//进入待机模式  
//check ok
//091202
void Sys_Standby(void)
{
SCB->SCR|=1<<2;//使能SLEEPDEEP位 (SYS->CTRL)   
   RCC->APB1ENR|=1<<28;     //使能电源时钟     
RCC->APB1ENR|=1<<27;     //使能备份时钟     
PWR->CSR|=1<<8;          //设置WKUP用于唤醒
PWR->CR|=1<<2;           //清除Wake-up 标志
PWR->CR|=1<<1;           //PDDS置位   
WFI_SET();     //执行WFI指令
//__WFI();        
}   
//后备寄存器写入操作
//reg:寄存器编号
//reg:要写入的数值
////check ok
////091202
//void BKP_Write(u8 reg,u16 dat)
//{
//  RCC->APB1ENR|=1<<28;     //使能电源时钟     
// RCC->APB1ENR|=1<<27;     //使能备份时钟     
// PWR->CR|=1<<8;           //取消备份区写保护
// switch(reg)
// {
//  case 1:
//   BKP->DR1=dat;
//   break;
//  case 2:
//   BKP->DR2=dat;
//   break;
//  case 3:
//   BKP->DR3=dat;
//   break;
//  case 4:
//   BKP->DR4=dat;
//   break;
//  case 5:
//   BKP->DR5=dat;
//   break;
//  case 6:
//   BKP->DR6=dat;
//   break;
//  case 7:
//   BKP->DR7=dat;
//   break;
//  case 8:
//   BKP->DR8=dat;
//   break;
//  case 9:
//   BKP->DR9=dat;
//   break;
//  case 10:
//   BKP->DR10=dat;
//   break;
// }
//}     
//系统软复位
//CHECK OK
//091209
void Sys_Soft_Reset(void)
{   
SCB->AIRCR =0X05FA0000|(u32)0x04;   
}  
//外部8M,则得到72M的系统时钟
//pll:选择的倍频数,从2开始,最大值为16
//CHECK OK
//091209
void Stm32_Clock_Init(u8 PLL)
{
unsigned char temp="0";   
MYRCC_DeInit();    //复位并配置向量表
RCC->CR|=0x00010000;  //外部高速时钟使能HSEON
while(!(RCC->CR>>17));//等待外部时钟就绪
RCC->CFGR=0X00000400; //APB1/2=DIV2;AHB=DIV1;
PLL-=2;//抵消2个单位
RCC->CFGR|=PLL<<18;   //设置PLL值 2~16
RCC->CFGR|=1<<16;   //PLLSRC ON
FLASH->ACR|=0x32;   //FLASH 2个延时周期
RCC->CR|=0x01000000;  //PLLON
while(!(RCC->CR>>25));//等待PLL锁定
RCC->CFGR|=0x00000002;//PLL作为系统时钟  
while(temp!=0x02)     //等待PLL作为系统时钟设置成功
{   
  temp=RCC->CFGR>>2;
  temp&=0x03;
}   
}      

设置好时钟之后,其他的就好办了,和AVR的操作差不多了(记得IO操作的时候先开启时钟!!!),看如下的主函数代码:
//LED端口定义
#define LED0 (1<<10)// PC10
#define LED1 (1<<11)// PC11
#define LED2 (1<<12)// PC12   
#define LED3 (1<<2) // PD2
//LEDIO 操作函数               
#define LED0_SET(x) GPIOC->ODR=(GPIOC->ODR&~LED0)|(x ? LED0:0)
#define LED1_SET(x) GPIOC->ODR=(GPIOC->ODR&~LED1)|(x ? LED1:0)                 
#define LED2_SET(x) GPIOC->ODR=(GPIOC->ODR&~LED2)|(x ? LED2:0)
#define LED3_SET(x) GPIOD->ODR=(GPIOD->ODR&~LED3)|(x ? LED3:0)
//LED IO初始化
void led_init(void)
{
RCC->APB2ENR|=1<<4;    //使能PORTC时钟      
GPIOC->CRH&=0XFFF000FF;
GPIOC->CRH|=0X00033300;//PC.10/11/12推挽输出     
    GPIOC->ODR|=0X1C00;    //PC.10/11/12 输出高
RCC->APB2ENR|=1<<5;    //使能PORTD时钟
GPIOD->CRL&=0XFFFFF0FF;
GPIOD->CRL|=0X300;     //PD.2推挽输出
GPIOD->ODR|=1<<2;      //PD.2输出高
}   
int main(void)
{
Stm32_Clock_Init(9);//72M
delay_init(72);     //延时初始化
//uart_init(72,9600); //设置波特率
led_init();   
while (1)
   {        
     LED0_SET(0);
  delay_ms(500);
  LED0_SET(1);
  LED1_SET(0);
  delay_ms(500);
  LED1_SET(1);  
  LED2_SET(0);
  delay_ms(500);
  LED2_SET(1);   
  LED3_SET(0);
  delay_ms(500);
  LED3_SET(1);         
    }
}


至此,一个跑马灯程序就完成了.


源码:https://static.assets-stash.eet-china.com/album/old-resources/2009/12/22/5bbbfc5d-2aec-4794-aec3-7110ffecc983.rar


 


这两天把STMSKY的板子焊接好了,以后代码就基于这个平台了


新代码:https://static.assets-stash.eet-china.com/album/old-resources/2009/12/31/bcceda0a-b9ea-4668-896b-a2f0ee475589.rar


这里代码做了点修改,下面是运行结果:


可以看到红绿蓝三个灯以此点亮.


点击看大图


点击看大图


点击看大图

PARTNER CONTENT

文章评论5条评论)

登录后参与讨论

用户584305 2013-5-9 13:09

你好 我是初学这 我想你这个硬件板子是哪里买的 我也想买一个

用户607352 2011-3-31 16:06

谢谢

用户306328 2011-3-9 12:32

很不错。

liujun6037_345432000 2010-12-5 21:52

呵呵,还是很多人不用库的 。

用户1540889 2010-12-3 22:42

闻原子老大是网上唯一不用库的总BOSS,特来膜拜1下
相关推荐阅读
正点原子 2013-05-17 23:47
【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第六十一章 战舰STM32开发板综合实验(标准例程终结篇)
   第六十一章 战舰STM32开发板综合实验        前面已经给大家讲了55个实例了,本章将设计一个综合实例,作为本指南的最后一个实验 ,该实验向大家展示了STM...
正点原子 2013-05-03 23:02
【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第六十章 UCOSII实验3-消息队列、信号量集和软件定时器
   第六十章 UCOSII实验3-消息队列、信号量集和软件定时器   上一章,我们学习了UCOSII的信号量和邮箱的使用,本章,我们将学习消息队列、信号量集和软件定时器...
正点原子 2013-05-03 20:42
【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第五十七章 ENC28J60网络实验
第五十七章 ENC28J60网络实验   本章,我们将向大家介绍ALIENTEK ENC28J60网络模块及其使用。本章,我们将使用ALIENTEK ENC28J60网络模块...
正点原子 2013-05-01 23:00
【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第五十九章 UCOSII实验2-信号量和邮箱
第五十九章 UCOSII实验2-信号量和邮箱      上一章,我们学习了如何使用UCOSII,学习了UCOSII的任务调度,但是并没有用到任务间的同步与通信,本章我们将学习两个最基本的...
正点原子 2013-04-30 10:55
【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第五十八章 UCOSII实验1-任务调度
  第五十八章 UCOSII实验1-任务调度      前面我们所有的例程都是跑的裸机程序(裸奔),从本章开始,我们将分3个章节向大家介绍UCOSII(实时多任务操作系...
正点原子 2013-04-26 23:16
【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第五十七章 ENC28J60网络实验
 第五十七章 ENC28J60网络实验  本章,我们将向大家介绍ALIENTEK ENC28J60网络模块及其使用。本章,我们将使用ALIENTEK ENC28J60网络模块和uIP 1...
EE直播间
更多
我要评论
5
10
关闭 站长推荐上一条 /1 下一条