本帖最后由 summarize 于 2013-5-19 14:47 编辑
废话少说,先上stm8s103 IAR库工程代码压缩包。
Schedule-IAR-STM8S103.rar
(416.35 KB, 下载次数: 538)
2013-5-19 14:15 上传
点击文件名下载附件
工程是在stm8s103f3单片机上调度通过,已经用消息实现了 UART1_TX模块的共享,即UART1_RX接收到的数据+0x11后再通过UART_TX模块发送回去,同时ADC1 通道3的转换结果也通过UART1_TX模块发送出去.见下图
1.ADC1转换结果每1秒上传一次到PC。测试式给ADC1通道3供的是5V电,所以结果是0x03ff.即1023.
共享时接收到的数据.png (61.3 KB, 下载次数: 0)
下载附件
2013-5-19 14:16 上传
3.支持非抢占式优先级调度,优先级顺序就是创建任务时的顺序,由高到底。其实现思想是,每一个任务运行结束后,都重新回到第一个创建的任务处按顺序查找某个任务是否满足运行条件,所以先创建的任务会先被“发现”其满足运行条件并运行之,核心代码如下
a.任务控制块数据结构
- struct SchTcb
- {
- #if SCH_CFG_Q_EN > 0u
- void *pData; //消息指针
- SCH_UINT8 Size; //消息大小
- #endif
- SCH_DLY_TYPE TimeCounter; //定时计数器,时基为 "SCH_SYS_TICKS_MS"
- void (*pTask)(); //任务指针
- struct SchTcb *pNextTCB; //下一个任务控制块指针
- };
复制代码b.调度核心
- void SCHTaskSchedStart(void)
- {
- SCHED_SART:
-
- pCurTCB = pFirstTCB; //指向第一个创建的任务,之后按创建时的顺序执行下去
- while (1)
- {
- SCHTimeTick(); //如果任务Tick满足条件,则将其置于可执行状态
- if (SCH_TASK_RUN == pCurTCB->TimeCounter) //任务处于可执行状态
- {
- pCurTCB->TimeCounter = SCH_TASK_PEND; //设置为挂起状态,保证任务只执行一次
- pCurTCB->pTask(); //执行当前任务控制块指向的任务
- goto SCHED_SART; //每执行完一个任务,都重新查找一次可执行最高优先级任务
- }
- pCurTCB = pCurTCB->pNextTCB; //指向下一个任务控制块,查找下个任务是否可执行
- }
- }
复制代码“schedule.c”和"schedule.h"已经设置为只读属性,无特殊情况不建议修改,"sch_cfg.h"则为开放给用户的接口,可定义数据类型、调度器节拍和配置是否使用消息。
本人水平有限,欢迎大家测试、指正不足。
寄存器版本出来了.附件如下!
寄存器版本开启了TIM1_PWM(两个通道)中断和IO下降沿中断(PD4),其中IO这个中断在"iostm8s103f3.h"是没有定义的,这个头文件只定义了从0x0c之后的中断,于是我自己根据手册增加了前面0x00~0x0b中断定义.同时将寄存器版本的所有初始化代码贴出来,这些初始化代码都有详细的注释,有了它,你甚至可以不用看手册了,它必将是初学者的福音及工程师的利器.
- /* Includes ------------------------------------------------------------------*/
- #include "user.h"
- /* Private typedef -----------------------------------------------------------*/
- /* Private define ------------------------------------------------------------*/
- /* Private macro -------------------------------------------------------------*/
- /* Private variables ---------------------------------------------------------*/
- /* Private function prototypes -----------------------------------------------*/
- /* Private functions ---------------------------------------------------------*/
- static void TIM1_PWM_Config(void);
- static void TIM4_Config(void);
- static void GPIO_Config(void);
- static void UART1_Config(void);
- static void ADC_Config(void);
- static void IWDG_Config(void);
- static void WWDG_Config(void);
- static void IT_Config(void);
- /* Public functions ----------------------------------------------------------*/
- void CLK_Config(void)
- {
- //配置内部16M时钟作为时钟源
- //CLK_ICKR_HSIEN = 1; //使能内部高速时钟(复位值)
- CLK_ICKR_bit.LSIEN = 1; //使能内部低速时钟
- CLK_CKDIVR_bit.HSIDIV = 0; //主时钟分频 0-3对应 1 2 4 8分频
- CLK_CKDIVR_bit.CPUDIV = 0; //CPU时钟分频 0-7对应 1 2 4 8 16 32 64 128分频
- //CLK_SWR = 0xE1; //HSI为主时钟源(复位值)
- //CLK_SWCR_SWEN = 1; //使能自动切换时钟
- //CLK_SWR = 0xD2; //0xD2:LSI为主时钟源(仅当LSI_EN选项位为1时)
- //CLK_SWR = 0xB4; //0xB4:HSE为主时钟源
- asm("nop");
- asm("nop");
- asm("nop");
- asm("nop");
- asm("nop");
- asm("nop");
- asm("nop");
- asm("nop");
- asm("nop");
- asm("nop");
- asm("nop");
- asm("nop");
- asm("nop");
- asm("nop");
- asm("nop");
- asm("nop");
- //外设时钟控制寄存器 CLK_PCKENR1
- // CLK_PCKENR1 = 0x00; //复位值为0xff,即默认为开启状态
- // PCKEN10: I2C
- // PCKEN11: SPI
- // PCKEN12: UART1
- // PCKEN13: UART2/3 实测此位才是UART1时钟使能(stm8s103f3)
- // PCKEN14: TIM4
- // PCKEN15: TIM2
- // PCKEN16: TIM3
- // PCKEN17: TIM1
- CLK_PCKENR1 = b10011000; //使能TIM1,TIM4,UART1 时钟
- //外设时钟控制寄存器 CLK_PCKENR2
- // CLK_PCKENR2 = 0x00; //复位值为0xff,即默认为开启状态
- // PCKEN20: Reserved
- // PCKEN21: Reserved
- // PCKEN22: AWU
- // PCKEN23: ADC
- // PCKEN24: Reserved
- // PCKEN25: Reserved
- // PCKEN26: Reserved
- // PCKEN27: CAN
- CLK_PCKENR2 = b00001000; //使能 ADC 时钟,
- //LK_CSSR_CSSEN = 1; //使能时钟监控功能,外间时钟出问题时切换到内部时钟源HSI/8 CLK_CSSR中的CSSD位被置位
- //LK_CCOR = ; //时钟输出寄存器
- }
- void Peripherals_Config(void)
- {
- GPIO_Config();
- TIM1_PWM_Config();
- TIM4_Config();
- UART1_Config();
- ADC_Config();
- IWDG_Config();
- WWDG_Config();
- IT_Config(); //中断相关配置
- }
- static void IWDG_Config(void)
- {
- IWDG_KR = 0x55; //关写保护
- IWDG_PR = 0x00; //预分频系数4 0-6对应 4 8 16 32 64 128 256 分频
- IWDG_RLR = 0xFF; //最长超时15.90 ms
- IWDG_KR = 0xAA; //恢复写保护,同时相关于清狗指令->用IWDG_RLR的数值刷新计数器的内容,避免了产生看门狗的复位
- //其实以上看门狗配置均为上电复位值
- IWDG_KR = 0xCC; //启动独立看门狗
- IWDG_KR = 0xAA; //清狗
- }
- static void WWDG_Config(void)
- {
- WWDG_WR = 0x7f;
- WWDG_CR = (uint8_t)(0x80 | WWDG_COUNTER_INIT);
- WWDG_WR = (uint8_t)((uint8_t)(~0x80) & (uint8_t)(0x40 | WWDG_WINDOW_VALUE));
- }
- static void GPIO_Config(void)
- {
- PA_ODR = b00000000; //初始化输出低电平
- PA_DDR = b11111111; // 1=output
- PA_CR1 = b11111111; //1=上拉输入或推挽输出 0=浮空输入或开漏输出 ADC时应选浮空输入,无中断
- PA_CR2 = b00000000; //1=使能外部中断或10M输出 0=禁止外部中断或2M输出 中断边沿在EXTI_CRX中设置
- PB_ODR = b00000000; //初始化输出低电平
- PB_DDR = b11111111; // 1=output PB4 INPUT
- PB_CR1 = b11111111; //1=上拉输入或推挽输出 0=浮空输入或开漏输出 ADC时应选浮空输入,无中断
- PB_CR2 = b00000000; //1=使能外部中断或10M输出 0=禁止外部中断或2M输出 中断边沿在EXTI_CRX中设置
- PC_ODR = b00000000; //初始化输出电平
- PC_DDR = b11111111; // 1=output PC4 INPUT
- PC_CR1 = b11111111; //1=上拉输入或推挽输出 0=浮空输入或开漏输出 ADC时应选浮空输入,无中断
- PC_CR2 = b00000000; //1=使能外部中断或10M输出 0=禁止外部中断或2M输出 中断边沿在EXTI_CRX中设置
- // 如果硬件上没有连接RST脚,则SWIM引脚(PD1)不能配置为输出口,否则不能连上ST-LINK
- PD_ODR = b00100000; //初始化输出低电平 PD5=TX初始化输出高电平,PD6=RX配置为输入
- PD_DDR = b10100011; // 1=output 0=input PD2=AIN3 PD3=AIN4
- PD_CR1 = b11111111; //1=上拉输入或推挽输出 0=浮空输入或开漏输出 ADC时应选浮空输入,无中断
- PD_CR2 = b00010000; //1=使能外部中断或10M输出 0=禁止外部中断或2M输出 中断边沿在EXTI_CRX中设置
- //PD4作为下降沿中断测试输入引脚,配置为上拉输入
-
- PE_ODR = b00000000; //初始化输出低电平
- PE_DDR = b11111111; // 1=output
- PE_CR1 = b11111111; //1=上拉输入或推挽输出 0=浮空输入或开漏输出 ADC时应选浮空输入,无中断
- PE_CR2 = b00000000; //1=使能外部中断或10M输出 0=禁止外部中断或2M输出 中断边沿在EXTI_CRX中设置
- }
- static void TIM1_PWM_Config(void)
- {
- //须要在选项字节中配置使能对应的PWM功能管脚,同时开启对应的时钟
- TIM1_PSCRH = 0x00; //预分频器 16bit fCK_CNT = fCK_PSC / (PSCR[15:0] + 1)
- TIM1_PSCRL = 0x00;
- TIM1_ARRH = (uint8_t)((800 - 1) >> 8); //自动重载值 16bit 即周期寄存器 16M/800=20K
- TIM1_ARRL = (uint8_t)((800 - 1));
- //TIM1_CR1
- //TIM1_CR1_bit.CEN = 1; //CEN:允许计数,最后再开启
- TIM1_CR1_bit.UDIS = 0; //UDIS:禁止更新
- TIM1_CR1_bit.URS = 0; //URS:更新请求源
- TIM1_CR1_bit.OPM = 0; //0:在发生更新事件时,计数器不停止;
- TIM1_CR1_bit.DIR = 0; //0:计数器向上计数;
- TIM1_CR1_bit.CMS = 0; //00:边沿对齐模式。计数器依据方向位(DIR)向上或向下计数。
- TIM1_CR1_bit.ARPE = 1; //1:TIM1_ARR寄存器由预装载缓冲器缓冲。
- TIM1_EGR_bit.UG = 1; //产生一次更新事件,更新分频系数与TIMx_CNTR
- TIM1_RCR = 0x00; //重复计数寄存器,即Reload事件发生了多少次:TIM1_RCR+1
- //这意味着在PWM模式中,(TIM1_RCR+1)对应着:
- //- 在边沿对齐模式下,PWM周期的数目;
- //- 在中心对称模式下,PWM半周期的数目;
- // 通道1;
- TIM1_CCMR1_bit.CC1S = 0; //CC1S[1:0]:捕获/比较1 选择
- TIM1_CCMR1_bit.OC1FE = 0; //OC1FE:输出比较1 快速使能
- TIM1_CCMR1_bit.OC1PE = 1; //OC1PE:输出比较1预装载使能
- TIM1_CCMR1_bit.OC1M = 7; //110:PWM模式1,111:模式2
- TIM1_CCMR1_bit.OC1CE = 0; //0:OC1REF 不受ETRF输入(来自TIM1_TRIG引脚)的影响; 1:一旦检测到ETRF输入高电平,OC1REF=0。
- TIM1_CCER1_bit.CC1E = 1; //1=输出使能
- TIM1_CCER1_bit.CC1P = 0; //0=高电平有效 P
- TIM1_CCER1_bit.CC1NE = 1; //1=输出使能 --
- TIM1_CCER1_bit.CC1NP = 0; //0=高电平有效--互补输出 N
- TIM1_CCR1H = 0x00;
- TIM1_CCR1L = 0x00; //占空比寄存器,CH1
- // 通道2;
- TIM1_CCMR2_bit.CC2S = 0; //CC1S[1:0]:捕获/比较1 选择
- TIM1_CCMR2_bit.OC2FE = 0; //OC1FE:输出比较1 快速使能
- TIM1_CCMR2_bit.OC2PE = 1; //OC1PE:输出比较1预装载使能
- TIM1_CCMR2_bit.OC2M = 7; //110:PWM模式1,111:模式2
- TIM1_CCMR2_bit.OC2CE = 0; //0:OC1REF 不受ETRF输入(来自TIM1_TRIG引脚)的影响; 1:一旦检测到ETRF输入高电平,OC1REF=0。
- TIM1_CCER1_bit.CC2E = 1; //1=输出使能
- TIM1_CCER1_bit.CC2P = 0; //0=高电平有效 P
- TIM1_CCER1_bit.CC2NE = 1; //1=输出使能 -- //
- TIM1_CCER1_bit.CC2NP = 0; //0=高电平有效--互补输出 N
- TIM1_CCR2H = 0x00;
- TIM1_CCR2L = 0x00; //占空比寄存器,CH2
- // TIM1_CR2 = ;
- // TIM1_SMCR = ; 从模式控制寄存器
- // TIM1_ETR = ; 外部触发寄存器
- //TIM1_IER 中断使能寄存器
- TIM1_IER_bit.UIE = 1; //更新中断
- TIM1_IER_bit.CC1IE = 0; //捕获/比较1中断
- TIM1_IER_bit.CC2IE = 0; //捕获/比较2中断
- TIM1_IER_bit.CC3IE = 0; //捕获/比较3中断
- TIM1_IER_bit.CC4IE = 0; //捕获/比较4中断
- TIM1_IER_bit.COMIE = 0; //COM中断
- TIM1_IER_bit.TIE = 0; //触发中断
- TIM1_IER_bit.BIE = 0; //刹车中断
- // TIM1_SR1 = ; 状态寄存器 1
- // TIM1_SR2 = ; 状态寄存器 2
- // TIM1_EGR = ; 事件产生寄存器
- //刹车寄存器
- TIM1_BKR_bit.MOE = 1; //1:如果设置了相应的使能位(TIM1_CCERX寄存器的CCIE位),则使能OC和OCN输出
- TIM1_CR1_bit.CEN = 1; //CEN:允许计数
- }
- /**
- * @brief Configure TIM2 to generate an update interrupt each 0.5ms
- * @param None
- * @retval None
- */
- void TIM4_Config(void)
- {
- //IM4_PSCR_bit.PSC = 7; //分频值: 2^PSC[2:0] 定时器时钟源为内部时钟(fMASTER)。 (1/16M)*128=8us
- //IM4_ARR = 124; //自动重载值 (124+1)*8us=1ms
- TIM4_PSCR_bit.PSC = 4; //分频值: 2^PSC[2:0] 1/16M)*2^4=1us
- TIM4_ARR = 99; //自动重载值 (99+1)us
- TIM4_EGR_bit.UG = 1; //产生一次更新事件,更新分频系数与TIM4_CNTR
- TIM4_CR1_bit.ARPE = 1; //1:TIM4_ARR寄存器通过缓冲预装载
- TIM4_SR_bit.UIF = 0; //TIM4 溢出中断标志
- TIM4_IER_bit.UIE = 1; //使能TIM4 溢出中断
- TIM4_CR1_bit.CEN = 1; //使能TIM4
- }
- /**
- * @brief UART1 Configuration for interrupt communication
- * @param None
- * @retval None
- */
- //
- static void UART1_Config(void)
- {
- WORD_UNION UART1_BRR;
- UART1_CR1_bit.PIEN = 0; //校验中断使能 0:中断被禁止 1:当USART_SR中的PE为1时,产生USART中断
- UART1_CR1_bit.PS = 0; //0:偶校验。 1:奇校验
- UART1_CR1_bit.PCEN = 0; //1:奇偶校验控制被使能
- UART1_CR1_bit.WAKE = 0; //唤醒方法
- UART1_CR1_bit.M = 0; //8bit数据格式
- UART1_CR1_bit.UART0 = 0; //0:UART使能 1:UART预分频器和输出禁用
- UART1_CR1_bit.T8 = 0; //9bit数据格式 下的发送数据位8
- UART1_CR1_bit.R8 = 0; //9bit数据格式 下的接收数据位8
- UART1_CR3_bit.STOP = 0; //00:1个停止位;01:保留 10:2个停止位;11:1.5个停止位;
- UART1_BRR.all = 1666; //16M/1666=9603
- //UART1_BRR2=UART_DIV[15:12]高4位 UART_DIV[3:0]低4位
- UART1_BRR2 = UART1_BRR.byte.H & 0xf0;
- UART1_BRR2 |= UART1_BRR.byte.L & 0x0f;
-
- //UART1_BRR1=UART_DIV[11:4]
- UART1_BRR1 = (uint8_t)(UART1_BRR.all>>4); //中间8位
- //UART1_CR2_bit.SBK = 1; //SBK: 发送断开帧
- //UART1_CR2_bit.RWU = 1; //RWU: 接收唤醒
- UART1_CR2_bit.REN = 1; //接收使能
- UART1_CR2_bit.TEN = 1; //发送使能
- //UART1_CR2_bit.ILIEN= 1; //ILIEN: IDLE中断使能
- UART1_CR2_bit.RIEN = 1; //接收中断使能 1:当USART_SR中的OR或者RXNE为1时,产生USART中断。
- //UART1_CR2_bit.TCIEN= 1; //发送完成中断使能 1:当USART_SR中的TC为1时,产生USART中断。要发送时再使能中断(会立即产生中断)
- //UART1_CR2_bit.TIEN = 1; //发送中断使能 1:当USART_SR中的TXE为1时,产生USART中断。
- //UART1_SR_bit.TC &= 0; //读该位来清零
- //UART1_DR = 0xaa; //写入要发送的数据
- }
- /**
- * @brief Configure ADC
- *
- * @param None
- * @retval None
- */
- static void ADC_Config(void)
- {
- // 当作为模拟输入时可以关闭输入施密特触发器来降低功耗
- ADC_TDRH = b00000000; //1=ADC输入禁止施密特触发 ***********ADC时应选浮空输入,无中断
- ADC_TDRL = b00011000; //共16通道 AIN3-AIN4
- ADC_CR1_bit.ADON = 0; //ADON: A/D 转换开/关
- ADC_CR1_bit.CONT = 0; // 0:单次转换模式 1:连续转换模式
- ADC_CR1_bit.SPSEL = 0; // 预分频选择位 000->111对应 2 3 4 6 8 10 12 18分频
- //ADC_CR2_bit.SCAN = 1; //1:使能扫描模式 0:不使能扫描模式
- ADC_CR2_bit.ALIGN = 1; //1:数据右对齐。(低8字节在ADC_DRL寄存器,其余高字节位在ADC_DRH寄存器)读顺序
- //应先读低位,再读高位字节或整个一起读
- ADC_CR2_bit.EXTSEL = 0; //外部事件触发和启动ADC 00:内部定时器1 TRG事件 01:ADC_ETR 引脚上的外部中断 其它值保留
- ADC_CR2_bit.EXTTRIG = 0; //EXTTRIG: 外触发使能位
-
- //ADC_CR3_bit.OVR = 0; //1:数据缓存发生了数据溢出事件
- ADC_CR3_bit.DBUF = 0; //1:数据缓存功能使能,扫描模式时使用.
- ADC_CSR_bit.CH = 4; // CH[3:0] : 选择转换通道 或指定从AN0到ANx扫描转换
- //ADC_CSR_bit.AWDIE = 0; //1: 使能AWD模拟看门狗中断
- //ADC_CSR_bit.EOCIE = 1; //1:使能转换结束中断
- //ADC_CSR_bit.AWD = 0; //AWD: 模拟看门狗标志
- ADC_CSR_bit.EOC = 0; //EOC: 转换结束
-
- ADC_CR1_bit.ADON = 1; //第一次置1将把ADC从低功耗模式下唤醒,之后才是启动ADC
- //ADC_DBxRH 缓冲寄存器
- //ADC_DBxRL
- //ADC_DRL 数据寄存器
- //ADC_DRH
- }
- void IT_Config(void)
- {
- // 中断配置------------------------------------------
- //ITC_SPR3_bit.VECT11SPR = 0;//Timer1最低优先级,
- //ITC_SPR4_bit.VECT15SPR = 0;//Timer3最低优先级,
- //ITC_SPR6_bit.VECT21SPR = 0; //UART1/3接收满 最低优先级
- //ITC_SPR6_bit.VECT22SPR = 2; //adc中断为最高优先级
- //ITC_SPR6_bit.VECT23SPR = 0; //Timer4最低优先级,2为最高,3为禁止优先级(复位默认值)
- //EXTI_CR1:外部中断控制寄存器1 设置何种边沿触发
- EXTI_CR1_bit.PAIS = 0;//[1:0] 00:下降沿和低电平触发
- EXTI_CR1_bit.PBIS = 0;//[3:2] 01:仅上升沿触发
- EXTI_CR1_bit.PCIS = 0;//[5:4] 10:仅下降沿触发
- EXTI_CR1_bit.PDIS = 2;//[7:6] 11:上升沿和下降沿触发
- //EXTI_CR2;
- EXTI_CR2_bit.PEIS = 0;//[1:0] GPIOE,配置同EXTI_CR1
-
- EXTI_CR2_bit.TLIS = 0;//[2] TLI 0:下降沿触发 , 1:上升沿触发
- }
复制代码同时更正了寄存器版本UART发送时的一个bug,i++应该是在给发送寄存器赋值之后才能执行;否则可能会出现少发送数据的情况,附件及修正部分代码如下- for (i = 0; i < u8DataSize; )//i++) //应该是在UART1->DR=x 后才能执行i++
- {
- if(UART1_GetFlagStatus(UART1_FLAG_TXE) == SET)
- {
- //UART1->DR = s_u8TxBuffer[i];
- UART1->DR = s_u8TxBuffer[i++]; //在此执行i++
- //UART1_ITConfig(UART1_IT_TXE, ENABLE);
- SCHCurTaskDly(1 / SCH_SYS_TICKS_MS); //延时发送1个字节所须要的时间
- }
- else
- {
- SCHCurTaskDly(0 / SCH_SYS_TICKS_MS); //无延时,下次直接进来查询
- }
- }
复制代码如果大家有兴趣,后续我可以出stm32版本的.一周热门 更多>