OpenEdv-开源电子网

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

STM32 PWM蜂鸣器音乐盒(寄存器版)+温度监测+串口输出+RTC+SPI接口FLASH存储+按键检测+流水灯

[复制链接]

1

主题

2

帖子

0

精华

新手入门

积分
18
金钱
18
注册时间
2017-12-26
在线时间
1 小时
发表于 2017-12-26 22:23:53 | 显示全部楼层 |阅读模式
   自己在博客发的,自己转过来,不知道贴地址是否违规,如果违规,麻烦版主帮忙编辑掉,原地址:http://teczm.blog.163.com/blog/static/2884553772017112683638308/?newFollowBlog.下面直接复制原文喽,希望大家多多交流。


   功能在标题中都体现出来了,具体的我自己会大概总结一下,然后总体上的流程大概是下面这个样子(其中plat_init 做系统初始化以及各模块的初始化,以及任务的创建,musicPlay 这个函数主要是根据时间来改变pwm的周期和占空比):
               

/*@brief: 主函数,工程入口.
*@param: None.
*@return: None.
*/
int main(void)
{
    plat_init();  
    while(1)
        {
        musicPlay();
        }
}

/*@brief: Platform init.
*@param: None.
*@return: None.
*/
void plat_init(void)
{
    stm32_sys_init();                           //系统初始化:设置系统时钟       
    uart_config();
    uart_printf("Platform initialized,program start!\r\n");
    led_config();                        //LED初始化
    key_config(led_modeset);
    w25q64_config(rtc_set);
    rtc_config(w25q64_record_time);
    pwm_config();   
    ds18b20_config();
    stm32_create_task(key_scan,TASK_PRIO_HIGH,10);
    stm32_create_task(led_indicate,TASK_PRIO_LOW,200);
    stm32_create_task(rtc_broadcast,TASK_PRIO_LOW,1000*10);
    stm32_create_task(ds18b20_read_temper,TASK_PRIO_LOW,1000*60);
    stm32_timer_init();                      //定时器初始化
}

/*brief: 演奏乐曲
*param: None.
*return: None.
*/
void musicPlay(void)
{
        static uint32_t music_tick = 0;
        static uint16_t i=0;        
    if(music_htys.mTime != 0)
    {        
        if(stm32_get_time_ms() >= music_tick)
        {
            music_tick = stm32_get_time_ms() + music_htys.mTime;
            pwm_update(music_htys[i++].mName);
        }
    }
    else
    {
        i = 0;
    }
}

                初始化部分,有一些模块用到了函数回调,注册了事件函数,比如按键模块,触发事件的时候执行对应的动作,做的很粗鄙,算是一个思想吧,有时间自己再整理整理。比如下面的按键模块,按键按下的时候会调用对应的按键事件,跑对应的流水灯效果和串口输出指示对应事件的产生(这个地方可能会有点绕,因为我在对应的事件处理程序中只设置了灯的模式,而点灯的动作是留到初始化时创建的任务里面去做的):

/*@breief: Key config.
*@param: None.
*@return: None.
*/
void key_config(key_evt_handler phandler)
{
    RCC->APB2ENR |= (Bit2_En|Bit6_En);                         //打开GPIOA和GPIOE的时钟
    GPIOA->ODR |= ((uint32_t)0<<KEY_UP_Pos);
    GPIOA->CRL &= ((~((uint32_t)0x03<<(KEY_UP_Pos<<2)))&(~((uint32_t)0x03<<((KEY_UP_Pos<<2)+2))));
    GPIOA->CRL |= ((uint32_t)0x2<<((KEY_UP_Pos<<2)+2));       //PA0输入,下拉
   
    GPIOE->ODR |= ((1<<KEY_DOWN_Pos)|(1<<KEY_LEFT_Pos)|(1<<KEY_RIGHT_Pos));
    GPIOE->CRL &= (((~((uint32_t)0x03<<(KEY_DOWN_Pos<<2)))&(~((uint32_t)0x03<<((KEY_DOWN_Pos<<2)+2))))
                  &((~((uint32_t)0x03<<(KEY_LEFT_Pos<<2)))&(~((uint32_t)0x03<<((KEY_LEFT_Pos<<2)+2))))
                  &((~((uint32_t)0x03<<(KEY_RIGHT_Pos<<2)))&(~((uint32_t)0x03<<((KEY_RIGHT_Pos<<2)+2)))));
    GPIOE->CRL |= (((uint32_t)0x2<<((KEY_DOWN_Pos<<2)+2))
                  |((uint32_t)0x2<<((KEY_LEFT_Pos<<2)+2))
                  |((uint32_t)0x2<<((KEY_RIGHT_Pos<<2)+2)));  //PE2 PE3 PE4输入,上拉
    pkey_handler = phandler;
}

/****************************************************************************
* Function Name  : led_modeset
* Description    : 设置LED的状态
* Input          : None
* Output         : None
* Return         : None
****************************************************************************/
void led_modeset(void *evt)
{
    key_evt = *(uint32_t *)evt;
}



        自己用定时器做了一个简单的任务调度,两个优先级,直接分配的数组,没有用动态申请的方式,毕竟图个简便快捷。

static task_t stm32_task_queue[TASK_PRIO_MAX][TASK_NUM_MAX] = {NULL};

/*@brief: Create new task.
*@param task: Pointer to task function.
*@param prio: Task priority.
*@return interval: Task execute interval,ms.
*/
int8_t stm32_create_task(ptask task,uint8_t prio,uint32_t interval)
{
    uint8_t i;
    if(prio > TASK_PRIO_MAX)
        return -1;
    for(i = 0;i<TASK_NUM_MAX;i++)
    {
        if(stm32_task_queue[prio].task == NULL)
        {
            stm32_task_queue[prio].task = task;
            stm32_task_queue[prio].interval = interval;
            if(i != 0)
            {
                stm32_task_queue[prio][i-1].next = &stm32_task_queue[prio];
            }
            return 0;
        }            
    }
    return -1;   
}
/*@brief: STM32 dispatch task..
*@param : None.
*@return: None.
*/
void stm32_dispatch_task(void)
{
    uint8_t i,j;
    static uint64_t count = 0;   
    task_t *ptask;
    count++;
    for(j=0;j<TASK_PRIO_MAX;j++)
    {
        ptask = &stm32_task_queue[j][0];
        for(i=0;i<TASK_NUM_MAX;i++)
        {
            if((ptask != NULL)&&(ptask->task != NULL))
            {
                if((count *TIM2_INTERVAL)%ptask->interval == 0)
                {
                    ptask->task();
                    ptask = ptask->next;
                }
            }
            else
                break;
        }
    }
}



以上,基本上就把自己这个工程的思路交代清除了,接下来从头说说自己第一次用这个芯片遇到的一些坑吧。

  • 第一个就是系统时钟初始化了,大家都知道单片机要跑起来肯定是少不了clock的。追求精度和稳定性的话,我们一般肯定是外部晶体或者时钟源优先了。具体不多说,各家芯片基本上都是同样的操作,选择时钟源,等待切换成功,如果需要倍频还需要借助PLL的帮助。这里我就卡住了一段事件,因为发现倍频到72MHz之后程序不运行了,后来才发现是系统频率太高,片上flash老胳膊老腿跟不上,需要增加等待时间,看了闪存编程手册才发现这一点,怪自己当初看手册不详细,user manual里面是有提到的。
  • 读ds18b20的温度时,用示波器看什么都对,就是读不出来,后来google才发现,百度下了个不知道哪来的手册,气死我了,这里强烈推荐大家尽量谷歌。
  • 中间有几次发现程序莫名奇妙的会跑飞,有时候又正常,后来debug发现原来是定时器初始化的比较早,当进定时器中断做任务的dispatch的时候,有些模块的回调函数还没有初始化,这个时候就会出问题。
  • 还有一个问题自己现在也不是太明白,就是在操作w25q64的时候,比如sector erase和write enable,如果我只发送,忽略DO出来的数据不做读取,这个时候看波形,通讯时正常的,但是w25q64内部貌似没有执行对应的动作,因为我发现,没有擦除成功,虽然看手册上这两条指令把DO线也画出来了,但是理论上读不读应该没关系才对。


欢迎大家讨论交流,代码写得匆忙又丑陋,我就不全部贴上了,不过如果有什么感兴趣的地方可以拿出来和大家分享。

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

1

主题

2

帖子

0

精华

新手入门

积分
18
金钱
18
注册时间
2017-12-26
在线时间
1 小时
 楼主| 发表于 2017-12-26 22:24:35 | 显示全部楼层
无语了,复制过来,格式都乱了,不好意思,第一次发
回复 支持 反对

使用道具 举报

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

本版积分规则



关闭

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

正点原子公众号

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

GMT+8, 2025-5-22 21:26

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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