admin
发表于: 2015-9-22 22:06:19 | 显示全部楼层

MCU型号:stm8s105s4

开发环境:ST Visual Develop

开发平台:STM8S_StdPeriph_Lib_V1.1.1


CPU频率及所有外设频率/时钟

系统复位后,所有外设时钟均处于开的状态。用户可通过清除CLK_PCKENR1或CLK_PCKENR2中的PCKEN位来关闭相应的外设时钟。但是在关闭外设的时钟前,用户必须设置相应的位禁用该外设。

为了使能一个外设,用户必须先设置寄存器CLK_PCKENR中对应的PCKEN位,然后设置外设控制寄存器中的外设使能位。

AWU计数器是由独立于fMASTER的内部或外部时钟(LSI或HSE)驱动,因此,即使寄存器的时钟已被关掉,该外设依然可以继续运行。

例如禁用所有外设时钟:

  1. CLK_PCKENR1 = 0x00;// close all clks of Peripheral
  2. CLK_PCKENR2 = 0x00;
复制代码

开启定时器TIME1定时器时钟:

  1. CLK_PCKENR1 |= 0x20;  //具体参考STM8S_Reference 59页
复制代码

CPU分频因子:CPU时钟(fCPU)由主时钟(fMASTER)分频而来,分频因子由时钟分频寄存器(CLK_CKDIVR)中的位CPUDIV[2:0]决定。共7个分频因子可供选择(1至128中,2的幂)。如图13所示。

fCPU为CPU和窗口看门狗提供时钟。

时钟分频寄存器(CLK_CKDIVR)


通用端口GPIO

和其他的单片机一样,我是习惯从端口开始学习。Stm8s105s系列最多有7组I/O端口,A~G,而根据不同的封装可能没有其中的一些,在这里根据具体项目,我选择的是44脚封装的。使用任何的外设前,我们都要根据需要的将参考手册和数据手册看一边,当然端口也不能另外了。

作为通用的IO口,每一个GPIO端口都有5个对应的寄存器如下表:

注意:初始复位时,所有引脚设置为浮空输入。

其中Px_ODR是ODR[7:0]:端口输出数据寄存器位; (1)在输出模式下,写入寄存器的数值通过锁存器加到相应的引脚上。读ODR寄存器,返回之前锁存的寄存器值。  (2)在输入模式下,写入ODR的值将被锁存到寄存器中,但不会改变引脚状态。ODR寄存器在复位后总是为0。位操作指令(BSET, BRST) 可以用来设置DR寄存器来驱动相应的引脚,但不会影响到其他引脚。

Px_IDR: IDR[7:0]:端口输入数据寄存器位

不论引脚是输入还是输出模式,都可以通过该寄存器读入引脚状态值。该寄存器为只读寄存器。 0:逻辑低电平 1:逻辑高电平

Px_DDR: DDR[7:0]:数据方向寄存器位 ,这些位可通过软件置1或置0,选择引脚输入或输出  0: 输入模式  1: 输出模式

Px_CR1:       C1[7:0]控制寄存器位

这些位可通过软件置1或置0,用来在输入或输出模式下选择不同的功能。在 输入模式时(DDR=0): 0:浮空输入 1::带上拉电阻输入

在输出模式时(DDR=1): 0:模拟开漏输出(不是真正的开漏输出) 1:  推挽输出, 由CR2相应的位做输出摆率控制

Px_CR2: C2[7:0]控制寄存器位 相应的位通过软件置1或置0,用来在输入或输出模式下选择不同的功能。在输入模式下,由CR2相应的位使能中断。如果该引脚无中断功能,则对该引脚无影响。 在输出模式下,置位将提高IO速度。此功能适用O3和O4输出类型。(参见引脚描述表)

在 输入模式时(DDR=0): 0: 禁止外部中断 1: 使能外部中断

在 输出模式时(DDR=1): 0:输出速度最大为2MHZ. 1:输出速度最大为10MHZ

在stm8的库里面已经将这些外设都进行了封装定义,并提供这些外设的SPI,也就是我们所说的stm8的库函数。下面我们看看任何结构化的定义这些IO的寄存器的。


  1. typedef struct GPIO_struct

  2. {

  3.   vu8 ODR; /*!< Output Data Register */

  4.   vu8 IDR; /*!< Input Data Register */

  5.   vu8 DDR; /*!< Data Direction Register */

  6.   vu8 CR1; /*!< Configuration Register 1 */

  7.   vu8 CR2; /*!< Configuration Register 2 */

  8. }GPIO_TypeDef;
复制代码

所有的IO通用寄存器,定义成GPIO_TypeDef这种类型的结构体,结构体中的每个寄存器都是u8类型,这个可以查看stm8库函数类型声明,其实等价于volatie unsigned char这种类型。那么我们就有了GPIO_TypeDef这种类型。下面是各个端口的结构化定义:

  1. #define GPIOA ((GPIO_TypeDef *) GPIOA_BaseAddress)
  2. #define GPIOB ((GPIO_TypeDef *) GPIOB_BaseAddress)
  3. #define GPIOC ((GPIO_TypeDef *) GPIOC_BaseAddress)
  4. #define GPIOD ((GPIO_TypeDef *) GPIOD_BaseAddress)
  5. #define GPIOE ((GPIO_TypeDef *) GPIOE_BaseAddress)
  6. #define GPIOF ((GPIO_TypeDef *) GPIOF_BaseAddress)
复制代码

下面是各个端口的起始地址:


  1. #define GPIOA_BaseAddress       0x5000
  2. #define GPIOB_BaseAddress       0x5005
  3. #define GPIOC_BaseAddress       0x500A
  4. #define GPIOD_BaseAddress       0x500F
  5. #define GPIOE_BaseAddress       0x5014
  6. #define GPIOF_BaseAddress       0x5019
  7. #define GPIOG_BaseAddress       0x501E
  8. #define GPIOH_BaseAddress       0x5023
  9. #define GPIOI_BaseAddress       0x5028
复制代码

上面的部分是硬件的抽象成软件的部分,有了这些我们就可以进行对寄存器操作了,比如上面的PA_DDR的操作,现在就可以写成GPIO->DDR=VALUE;

这样进行每个端口设置时,肯定要设置使用哪个引脚,速度是多少,哪种模式,这几个对每组端口都是一样的,st有对他进行了封装,如下:


  1. typedef enum

  2. {

  3.   GPIO_MODE_IN_FL_NO_IT      = (u8)0b00000000,  /*!< Input floating, no external interrupt */

  4.   GPIO_MODE_IN_PU_NO_IT      = (u8)0b01000000,  /*!< Input pull-up, no external interrupt */

  5.   GPIO_MODE_IN_FL_IT         = (u8)0b00100000,  /*!< Input floating, external interrupt */

  6.   GPIO_MODE_IN_PU_IT         = (u8)0b01100000,  /*!< Input pull-up, external interrupt */

  7.   GPIO_MODE_OUT_OD_LOW_FAST  = (u8)0b10100000,  /*!< Output open-drain, low level, 10MHz */

  8.   GPIO_MODE_OUT_PP_LOW_FAST  = (u8)0b11100000,  /*!< Output push-pull, low level, 10MHz */

  9.   GPIO_MODE_OUT_OD_LOW_SLOW  = (u8)0b10000000,  /*!< Output open-drain, low level, 2MHz */

  10.   GPIO_MODE_OUT_PP_LOW_SLOW  = (u8)0b11000000,  /*!< Output push-pull, low level, 2MHz */

  11.   GPIO_MODE_OUT_OD_HIZ_FAST  = (u8)0b10110000,  /*!< Output open-drain, high-impedance level,10MHz */

  12.   GPIO_MODE_OUT_PP_HIGH_FAST = (u8)0b11110000,  /*!< Output push-pull, high level, 10MHz */

  13.   GPIO_MODE_OUT_OD_HIZ_SLOW  = (u8)0b10010000,  /*!< Output open-drain, high-impedance level, 2MHz */

  14.   GPIO_MODE_OUT_PP_HIGH_SLOW = (u8)0b11010000   /*!< Output push-pull, high level, 2MHz */

  15. }GPIO_Mode_TypeDef;
复制代码

具体GPIO_Mode_TypeDef;可以查看库中的GPIO.H文件中的详细定义。

看下st给出的库提供了那些操作端口的函数吧:

这些函数的详细信息可以参考st的库说明,最好去看下函数的原型代码,这样会有更好的理解。当然了,我在使用的时候也会做一般的说明。


  1. void GPIO_DeInit(GPIO_TypeDef* GPIOx);

  2. void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_Pin_TypeDef GPIO_Pin, GPIO_Mode_TypeDef GPIO_Mode);

  3. void GPIO_Write(GPIO_TypeDef* GPIOx, u8 PortVal);

  4. void GPIO_WriteHigh(GPIO_TypeDef* GPIOx, GPIO_Pin_TypeDef PortPins);

  5. void GPIO_WriteLow(GPIO_TypeDef* GPIOx, GPIO_Pin_TypeDef PortPins);

  6. void GPIO_WriteReverse(GPIO_TypeDef* GPIOx, GPIO_Pin_TypeDef PortPins);

  7. u8 GPIO_ReadInputData(GPIO_TypeDef* GPIOx);

  8. u8 GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);

  9. BitStatus GPIO_ReadInputPin(GPIO_TypeDef* GPIOx, GPIO_Pin_TypeDef GPIO_Pin);

  10. void GPIO_ExternalPullUpConfig(GPIO_TypeDef* GPIOx, GPIO_Pin_TypeDef GPIO_Pin, FunctionalState NewState);
复制代码


介绍到这里,对通用的I/O口有个大概的了解了吧,下面我说说我自己使用的情况:

1、首先新建工程Project,将库文件GPIO.c和GPIO.h复制到该过程目录下,

2、调用st库函数初始化端口IO:

  1. /*函数名(端口PA口,端口第几个引脚PA4,PA5,PA6,IO模式推挽式输出_低电平_10M)*/

  2. GPIO_Init(PORTA, (PIN4 |PIN5 |PIN6), GPIO_MODE_OUT_PP_LOW_FAST);

  3. GPIO_Init(PORTB, (PIN0 |PIN1 |PIN2 |PIN3), GPIO_MODE_OUT_PP_HIGH_SLOW);

  4. /*函数名(端口名PB,端口第几个引脚PB0,PB1,PB2,PB3,IO模式推挽式输出_高电平_2M)*/
复制代码

这样调用函数,初始化端口完毕。

3、在控制输出时,可以调用st库函数,这里我为了快捷,是直接对输出寄存器进行操作。如:GPIOA->ODR  |= (1<<4); PA口的的第5为置高。

例如:


  1. Main()

  2. { u16 i;

  3. GPIO_Init(PORTA, (PIN4 |PIN5 |PIN6), GPIO_MODE_OUT_PP_LOW_FAST);

  4. While(1)

  5. {     GPIOA->ODR  ^= (1<<4);   //灯闪烁

  6. For(i=0;i<3000;i++) ;  //延时

  7. }

  8. }
复制代码

串口通信UART

Stm8微控制器家族的通用同步异步收发器(UART1,UART2或UART3)提供了一种灵活的方法与使用工业标准NRZ异步创行数据格式的外部设备之间进行全双共数据交换。Stm8的UART提供宽范围的波特率选择,并且支持多处理器通讯。

后面关于宏定义有关的都不再介绍,详情请看st固件库,从这里开始只介绍如何使用st提供的固件库搭建自己的程序。

因为我用的芯片stm8s105s4只有UART2,所有这里直接调用固件库中的uart2.c和uart2.h就可以了。Uart初始化函数如下:        

  1. UART2_DeInit();  // uart2中的个寄存器清零

  2.     /* UART1 configuration ------------------------------------------------------*/

  3.     /* UART1 configured as follow:

  4.           - BaudRate = 9600 baud

  5.           - Word Length = 8 Bits

  6.           - One Stop Bit

  7.           - Odd parity

  8.           - Receive and transmit enabled  //允许发送、接收

  9.           - UART1 Clock disabled    */

  10.     /* Configure the UART1 */

  11.     UART2_Init((u32)9600, UART2_WORDLENGTH_8D, UART2_STOPBITS_1, UART2_PARITY_NO, UART2_SYNCMODE_CLOCK_DISABLE, UART2_MODE_TXRX_ENABLE);

  12.     //* Enable the UART1 Receive interrupt: this interrupt is generated when the

  13.        //UART1 receive data register is not empty

  14. UART2_ITConfig(UART2_IT_RXNE_OR, ENABLE);  //开uart2接收中断
复制代码

需要了解UART2_Init();可以查看uart2.c中的原型函数

这样,UART2通信已经配置好了,接下来就是使用了。

发送函数如下:


  1. void UART2_SendData8(u8 Data)

  2. {     u8 i;

  3.     /* Transmit Data */

  4.     UART2->DR = Data;

  5.               while((UART2->SR &0x40)==0) ;    //我修改的

  6.               UART2->SR &= ~(1<<6);            //我修改的

  7. }
复制代码

在你需要发送数据的时候,直接调用UART2_SendData8(u8 Data)就可以了。在串口接收中断中调用读取数据函数:

  1. u8 UART2_ReceiveData8(void)
  2. {
  3.     return ((u8)UART2->DR);
  4. }
复制代码

也可以直接读取寄存器中的数据,注意:在中断中读完数据后,退出中断前都需要先清中断标志位,调用函数如下:

UART2_ClearITPendingBit(UART2_IT_LBDF);否则中断一直存在。


ADC转换配置:

ADC1和ADC2是10位的逐次比较型模拟数字转换器。提供多达16路多功能的输入通道(实际准确的通道数量在数据手册的引脚描述说明)。A/D转换的各通道可以执行单次和连续的转换模式。

相对与ADC2、ADC1具有一些扩展功能,包括扫描模式,带缓存的连续模式以及模拟看门狗。请参考数据手册来了解不同型号的ADC1和ADC2的功能信息。

ADC开—关控制

通过置位ADC_CR1寄存器的ADON位来开启ADC。当首次置位ADON位时,ADC从低功耗模式唤醒。为了启转换必须第二次使用写指令来置位ADC_CR1寄存器的ADON位。在转换结束时ADC会保持在上电状态,用户只需要置位ADON位来启动下次转换。如果长时间没有使用ADC,推荐ADC切换到低功耗模式来降低功耗,这可以通过清零ADON位来实现。

当ADC模块上电后,所选通道对应的I/O输出模块是被禁用的,因此推荐在ADC上电前要选适合的ADC转换通道。

ADC时钟

ADC的时钟是由Fmaster时钟经过预分频后提供的。时钟的预分频因子是由ADC_CR1寄存器的SPSEL[2:0]决定的。

数据对齐

ADC_CR2寄存器中的ALIGN位于选择转换后数据的对齐方式:

右对齐:8个低位数据被写入ADC_DL中,其余在ADC_DH中,读取时必须先读低位再读高位。

左对齐:8个高位数据被写入ADC_DH中,其余在ADC_DL中,读取时必须先读高位再读低位。

我利用ST公司提供的固件库实验如下:

初始化如下(单次转换):

  1. void Sys_ADC1_3_Int(void)

  2. {

  3. /*  Init GPIO for ADC1 */

  4.     GPIO_Init(GPIOB, (GPIO_PIN_0 |GPIO_PIN_1 |GPIO_PIN_2), GPIO_MODE_IN_FL_NO_IT);  //初始化ADC 端口         

  5.        CLK_PeripheralClockConfig(CLK_PERIPHERAL_ADC , ENABLE);

  6.        ADC1_DeInit();

  7.     // Init ADC2 peripheral

  8.     ADC1_Init(ADC1_CONVERSIONMODE_SINGLE, ADC1_CHANNEL_0, ADC1_PRESSEL_FCPU_D2, ADC1_EXTTRIG_TIM, DISABLE, ADC1_ALIGN_RIGHT, ADC1_SCHMITTTRIG_CHANNEL0, DISABLE);  

  9.     //ADC1_ITConfig( ADC1_IT_EOCIE,ENABLE);  //Enable EOC interrupt 只有在使用ADC中断的时候才打开这一项

  10.          /*Start Conversion */

  11.     //ADC1_StartConversion();//启动ADC转换      

  12. }
复制代码

这里ADC读取分使用中断方式和不使用两种

不使用中断,启动ADC后等待转换完毕:

  1. ADC1_Init(ADC1_CONVERSIONMODE_SINGLE, ADC1_CHANNEL, ADC1_PRESSEL_FCPU_D2, ADC1_EXTTRIG_TIM, DISABLE, ADC1_ALIGN_RIGHT, ADC1_CHANNEL, DISABLE);  //配置ADC通道及ADC转换模式              /*数据右对齐*/

  2. ADC1_StartConversion();//启动ADC转换         /*Start Conversion */   

  3. while((ADC1->CSR & 0x80 ) != 0x80 ) ;   //等待ADC转换完毕

  4. Adc_Value  =  ADC1_GetConversionValue();  //读取ADC转换数据,先低位,后高位
复制代码

中断方式: 在ADC中断函数中,直接读取ADC转换数据,

  1. Adc_Value  =  ADC1_GetConversionValue();  //读取ADC转换数据,先低位,后高位
复制代码

在主程序中隔一段时间选择ADC通道和启动ADC就可以了。

  1.     ADC1_Init(ADC1_CONVERSIONMODE_SINGLE, ADC1_CHANNEL_0, ADC1_PRESSEL_FCPU_D2, ADC1_EXTTRIG_TIM, DISABLE, ADC1_ALIGN_RIGHT, ADC1_SCHMITTTRIG_CHANNEL0, DISABLE);  

  2.     //ADC1_ITConfig( ADC1_IT_EOCIE,ENABLE);  //Enable EOC interrupt 只有在使用ADC中断的时候才打开这一项
复制代码


跳转到指定楼层
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

主题 12 | 回复: 16



手机版|

GMT+8, 2024-4-20 07:33 , Processed in 0.132109 second(s), 4 queries , Gzip On, MemCache On. Powered by Discuz! X3.5

YiBoard一板网 © 2015-2022 地址:河北省石家庄市长安区高营大街 ( 冀ICP备18020117号 )

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