2019-01-14 17:05:35 qq_20222919 阅读数 590
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    3995 人正在学习 去看看 朱有鹏

最近需要用到单片机随机数,但是用rand()产生的随机数,发现每次单片机上电时产生的随机数都是一样的,没有实现真正的随机数。查资料发现要用到srand(t)产生一个随机种子,同样获取随机种子的时候也需要一个随机信号,随机信号可以通过系统时间或者悬空的AD引脚产生。但是对于单片机来说每次上电后系统时间都是一样的,所以每次上电产生的随机数也是一样的。也可以读取悬空的AD引脚值,这样每次读取AD的值是随机的,用这个产生随机数是可行的。但是由于条件限制没有悬空的AD引脚可以用。难道就没有更简单的方式实现每次上电都产生不同的随机数吗?突然想到了每次上电时单片机内存中有些地址的值是随机的,那么能不能每次一上电就去读内存中的值,然后用这些值去产生随机种子。然后写了一段测试代码,测试发现这种方法果然可行。可以实现单片机上电产生随机数,每次上电产生的随机数都不同。

测试用的是STM8S003单片机。

测试代码如下:

#include "iostm8s103F3.h"

#include "stdlib.h"

void main(void)

{

 u8 tem = 0;

     //系统初始化

srand(* ( u16 * ) ( 0x000008 ));   //直接读取内存地址的值,当做随机种子

 tem = rand() % 10;     //产生0--9随机数



}

 

2019-05-10 09:39:25 Behold1942 阅读数 530
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    3995 人正在学习 去看看 朱有鹏

以前一直以为单片机中无法使用随机数,我也不知道自己哪里来的这种错觉。直到昨天项目上需要用到随机数,才去查了一下,竟然真可以在单片机中生成随机数,以下为一个生成随机数简单的测试demo:

#include “stdio.h”
#include "stdlib.h"   //rand()函数和srand()函数的头文件。 


#define MAX_RANDOM              80             //随机数最大值
#define MIN_RANDOM              1              //随机数最小值


/*
 @function:for genergate a random  num .
 @param: the @seed is a num for generate random num.
 */
unsigned int random(unsigned int seed)
{
  unsigned int value;
  srand(seed);
  value = rand() % (MAX_RANDOM + 1- MIN_RANDOM) + MIN_RANDOM;  //获取一个随机数(1-80)
  return value;
}



void main()
{

    while(1)
    {
         static unsigned int num;
         printf("generate random num:%d\r\n",random(num)); 
         delay_ms(1000);
         num++;        //根据累加的num值生成多个随机数
    }

}

 

2014-11-13 23:08:07 u014183377 阅读数 664
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    3995 人正在学习 去看看 朱有鹏

     在单片机编程中,我们经常会用到一些无符号数与有符号数的混合运算,另外我们所用的单片机很有可能是16位或者8位的,这样,编程时所用的一些变量的取值范围会对我们的 运算有所限制.比如说8位的单片机无符号数最大值为255,有符号最大数为127;16位单片机无符号数最大值为65535,有符号数最大值为32767.对于32的单片机来说,因为我们一般所处理的值很少能超过有符号数的最大取值,所以比较少遇到下面出现的问题.

        在一些运算中,我们希望有些数能表示正负,这就得用有符号数,而有些数的取值会超过有符号数的最大值,这时我们就得用无符数来表示.下面是我编程时遇到的两个问题(用的是MC9S12XS128处理器,16位的单片机).

       变量的声明如下:

        int iError;

        unsigned  int uiExpectSpeed;

        unsigned int uiCurrentSpeed;

       语句如下:

       iError  = (uiExpectSpeed - uiCurrentSpeed)/3;       //(1) 第一个语句

       在调试的过程中发现这个iError的值有时候会特别大,最后才发现是上面的这句语句出错了!然后修改成下面两句结果就对了:

       iError  = uiExpectSpeed - uiCurrentSpeed;            //(2)第二个语句

       iError  = iError/3;                                                          //(3)第三个语句

       不同类型的数据在进行混合运算时会有一个隐试的类型转换过程,有符号数与无符号数混合运算,有符号数会被转换成无符号数后再参加运算.

       在上面的第一个语句中,如果uiExpectSpeed 比uiCurrentSpeed的值大,也就是uiExpectSpeed - uiCurrentSpeed结果为一正值,那不会出现啥问题,但当uiExpectSpeed 比uiCurrentSpeed的值小时就出现问题了,此时uiExpectSpeed - uiCurrentSpeed的临时结果存放在16位的寄存器中,且最高位1,对于有符号数来说会把这一个位解释为符号位,1表示负数,而对于无符号来说这个位就表示数值,接着这个临时的结果除以3后,所得到的结果的最高位变为了0此时该结果会转换为一个有符合数(不管是有符号数,还是无符号数,最高位为0时,所表示的数值就是一样的),赋给iError.本应该得到一个负数的,但最终却得到了一个比较大的正数!在第一个语句中,如果没有除以3,而是两个数作差后直接赋给iError则是不会出错的,虽然uiExpectSpeed - uiCurrentSpeed运算的结果是一个很大的正数(寄存器的最高位为1),但在这个临时结果赋给iError这个变量时,会先把这个值转换为一个有符号数赋给iError.其实,在把uiExpectSpeed - uiCurrentSpeed运算的结果赋给iError时是把所有的位原封不动的复制到iErrorr所表示的内存单元中的,只是我们是以有符号数来解释这个内存单元中的内容,所以这个很大的正数就变成了一个负数!(数据在处理器内是以补码表示的,对于数据是正还是负只是人们的解释不同而已).所以我就用后面的两句替换了第一句,这样不管uiExpectSpeed - uiCurrentSpeed的差值是正还是负都能得到正确的结果了.

   

      下面是我在做超声波测距时遇到的又一个很隐蔽的问题:

       unsigned int start; //表示计时开始时计数器的值
       unsigned int end; //表示计时结束时计数器的值
       unsigned int error;      

      unsigned int distance; //表示距离
      unsigned int time; //表示从计时开始到结束所用的时间
      unsigned int remainder;//余数

      start  = TCNT;// 计时开始, TCNT为16位的计时器寄存器

     ..............一段时间后(这段时间小于计时器TCNT从0计数到最大值65535所表示的时间)...........

     end = TCNT; //计时结束

     error = end - start; //注意,end有可能比start小,但由于都是无符号数,所以最后得到的差值就是这段时间内计数器TCNT的增量.

      time = error/625; //单位为ms  TCNT每1ms内数值增加625(这个数与TCNT所用的时钟有关)
      distance = 17*time; //单位为cm, 距离为速度乘以时间再除以2就是声波所传波的距离

      这块由于是分步计算的,所以会有比较大的误差(主要是由于error/625后的余数被丢弃了)     于是我改成如下语句:

 

     start  = TCNT;// 计时开始, TCNT为16位的计时器寄存器

     ..............一段时间后(这段时间小于计时器TCNT从0计数到最大值65535所表示的时间)...........

     end = TCNT; //计时结束

     error = end - start; //注意,end有可能比start小,但由于都是无符号数,所以最后得到的差值就是这段时间内计数器TCNT的增量.

     distance = (17*error)/625; //单位为cm, 将上面的最后两句结合成一句,先乘后除就会减小误差

     但改后上面distance = (17*error)/625; 这句就错了,因为error的值可能很大,最大可以达到65535,所以17*error结果很有可能会超过65535,但这个处理器是16位的,也就是说这个处理器的数据寄存器为16位,最大的表示数值也就65535,所以17*error大于65535后就会被截断存入寄存器中.也就是说存入寄存器中的值为(17*error)e536,当再用这个值除以625时得到的很有可能就是0或者个位数的值,不管怎样,此时得到的结果都是错误的值了!!

     结合上面两种情况,最后我改成如下:   

      start  = TCNT;// 计时开始, TCNT为16位的计时器寄存器

     ..............一段时间后(这段时间小于计时器TCNT从0计数到最大值65535所表示的时间)...........

     end = TCNT; //计时结束

     error = end - start; //注意,end有可能比start小,但由于都是无符号数,所以最后得到的差值就是这段时间内计数器TCNT的增量.

     time = error/625; //单位为ms
     remainder = error - time*625;//计算上一句中丢弃的余数,没有用remainder = errorb5,是因为除法很耗时!!
     distance = 17*time + (17*remainder + 312)/625; //单位为cm,此处的312(625/2)是考虑到四舍五入的.

 

来自:http://blog.csdn.net/dlutxie/article/details/6709510

2015-06-24 12:30:41 qq_14997473 阅读数 21848
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    3995 人正在学习 去看看 朱有鹏

      去年冬天在帮学校附近一家密室逃脱店做一些电子机关,其中一个打地鼠项目需要用到单片机产生随机数,用于实现随机让几个地鼠“钻”出来。一开始想法很单纯,不就是随机函数么,之前C语言课上就学过啦。可是真正用这个方法做了之后却发现并没有想象之中那么简单,程序像是写死的,复位后每次的顺序都是固定的,并没有随机的效果,于是我仔细研究了一下,总结了单片机生成随机数的几种常见方法。

     首先,要明确一点:一个独立的单片机系统,如果不引入一个随机信号,永远不能实现随机的效果。这里的随机信号可以是用户的交互、时间、采集的外部数据等等,我们常用伪随机数列+随机数种子的方式实现随机的效果,而这些随机信号就是用来做随机数种子。

     我使用的生成伪随机数代码如下:

……
#include<stdlib.h>
……
#define MAX              16             //随机数最大值
#define MIN              1              //随机数最小值
 
unsigned int random(unsigned int xxx)
{
  unsigned int value,iii;
  for(iii=0;iii<xxx;iii++)
  {
    value = rand() % (MAX + 1- MIN) + MIN;               //获取一个随机数(1-16)
  }
  return value;
}

或者利用srand()函数,将上述函数改写成

unsigned int random(unsigned int xxx)
{
unsigned int value;
  srand(xxx);
  value = rand() % (MAX + 1 - MIN)+ MIN;               //获取一个随机数(1-16)
  return value;
}

说明:

srand()函数位于<stdlib.h>表头文件中,用来设置rand()产生随机数时的随机数种子。

定义函数 void srand (unsigned int seed);

参数seed必须是个整数,如果每次seed都设相同值,rand()所产生的随机数值每次就会一样。


在上述程序中将用户按下按键时定时器中的ms时间值后两位作为函数的输入参数,这样就能获得一个随机值。


单片机生成随机数的几种常用随机信号如下:

1. 用一路AD采集温度或电源噪声,取后几位作为随机信号;

2. 将用户的交互信号时间作为随机信号;

3. 利用时钟芯片或RTC获取当前时间再经处理作为随机信号;

4. 自己搭一个硬件电路用来产生随机信号,如利用单片机的内置时钟设为1M,外接一个用温度敏感电阻搭建一个特征频率10M的RC振荡电路,用慢时钟去采集快时钟,然后经过简单处理,就可以得到一个真正的随机数了。

 

 

 

 

 

2016-03-20 11:52:44 zhanghuaichao 阅读数 2426
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    3995 人正在学习 去看看 朱有鹏

今天突然用到了用单片机和上位机进行传递浮点数方法是这样的。

1.将浮点数转化为整数,然后传递整数的ascII码形式。

2.进行编码,这个暂时我是这样理解的,比方说a=7.345678这样的浮点数。

可以进行编码  成一个帧数据    帧头 +数据位+ 整数编码+加小数点+小数编码

具体的解码方法是:a/1取整,得到整数7,然后再添加一个小数点编码!(如0x55),然后再用a-7得到小数部分,然后将小数扩大10倍。再取整然后继续进行取整,以此类推将

小数都编码成功。然后发送过去就可以了。

3.第三种方法也是利用内存,不用知到浮点数的具体编码方式,因为在单片机中浮点数占4个字节,就知道这一点就可以了,我们可以采用联合体的方式,

float value=-17.484;
union                            //联合体定义
{
    u8 a[4];
    float b;
}temp;

void  Change_to_byte(void)
{
     temp.b   = value;                //将数据存入联合休中
     array[0] = temp.a[0];        //一个字节一个字节的取出来
     array[1] = temp.a[1];
     array[2] = temp.a[2];
     array[3] = temp.a[3];
}

再用串口把array[0...4]发送出去就可以了,大功告成。

4.收到这种方法的启发,当然我们也可以使用指针来做。

float value=-9.4567;
float *p=&value;
unsigned char *q;
/*******************************************************************************
* 函数名   : main
* 函数描述     : 主函数
* 输入参数      : 无
* 输出结果     : 无
* 返回值        : 无
*******************************************************************************/
int main(void)

int j;


/*设置系统时钟 */
  RCC_Configuration();
/* 设置GPIO端口 */
  GPIO_Configuration();
/* 设置 USART */
USART_Configuration();
q=(unsigned char *)p;
    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) != SET);
for(j=0;j<4;j++)//发送四个字节,表示一个浮点数
    {
        USART_SendData(USART1,q[j]); 
        while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) != SET);
    }


没有更多推荐了,返回首页