最近,在微信公众号的推荐里面看到了这款单片机,厦门感芯科技的MC3172芯片,具有硬件多线程实现的功能,可以摆脱单片机那种RTOS的线程调度问题,不用考虑资源占用问题。在一定程序上,可以代替功能简单的FPGA,具有了完全并行的处理能力。重点是这款芯片还可以免费申请,就去申请体验了一下,3天左右就寄到了,到了周末正好可以玩一下。
这款芯片有两个特别的地方:
1,并行多线程,有点大家理解;
2,12个通信接口可配置。
开发板很简单,基本上引出了所有的IO,上图。
开发板自带下载器,软件开发软件使用的是
官方资料合集:1. MC3172芯片资料合集稳定版 - - - 厦门感芯科技有限公司
开源仓库:https://gitee.com/gxchip1.
工程模板:https://gitee.com/gxchip/mc3172_template2.
CMAKE工程:https://github.com/dreamcmi/MC3172-CMake
开发工具:1. MRS:http://www.mounriver.com/download
安装完软件后,开始干活,以点灯开始,想了想,这次做个呼吸灯把,查了下呼吸灯的原理,网上就有现成的,拿来就用。呼吸灯的原理参考了:
(143条消息) STM32——呼吸灯_hustcw98的博客-CSDN博客_stm32呼吸灯
手头有一个转接板,很久以前做的,有led,串行adc(LTC2360),原理图如下。
当然,LTC2360的参考电压,应该用基准电压芯片,因为是试验板,就没有太讲究。
开发板通过SPI接口采集电压,根据电压值,改变呼吸灯的频率。很简单的一个设计。开发板的布局如下图使用,使用到的IO如下,PA12、PA13、PA14、PA15这四个用作GPIO,PC0、PC1、PC2、PC3用作SPI接口控制LTC2360,
使用杜邦线完成两个板子的连接。
硬件连接完成了,开始写代码,在写代码之前,先进行线程配置。
配置界面比较友好,存储器有三种配置方式,可根据实际需要配置,时钟源有四种选择方式。共有四组线程,每组线程平分主时钟(200/4=50M),每组里面有16个线程,在进行平分,需要多少个线程,选择多少个线程。
直接打开工程模板,界面如下图。
左边为工程结构,右边为编辑区域。双击main.c
找到配置的两个线程,开始代码编辑
呼吸灯采用线程1完成。
//delay函数来自【代码分享】利用内核定时器做通用delay函数 / 感芯科技 / WhyCan Forum(哇酷开发者社区)
void delay_us(u32 nus)
{
u32 ticks;
u32 told,tnow,tcnt=0;
GET_CORE_CNT(told); //读取内核定时器数值
ticks=nus*48; //目标延时节拍数=需要延时时间(us)*CORE_CLK(MHz)/4
if(ticks!=0)
{
while(1)
{
GET_CORE_CNT(tnow);
if(tnow!=told)
{
if(tnow<told)tcnt=0xFFFFFFFF-told+tnow; //CORE_CNT递增32位,计算已延时节拍数
else tcnt=tnow-told;
if(tcnt>=ticks)break; //延时完成
}
};
}
}
void GPIO_TEST(u32 gpio_sel)//, u32 aa)
{
u32 var;
u32 period;
// 启动GPIOA并设置特权组及时钟频率
INTDEV_SET_CLK_RST(GPIOB_BASE_ADDR,(INTDEV_RUN|INTDEV_IS_GROUP0|INTDEV_CLK_IS_CORECLK_DIV2));
// 使能GPIOA PIN0引脚
GPIO_SET_OUTPUT_EN_VALUE(gpio_sel, GPIO_PIN0 | GPIO_PIN1 | GPIO_PIN2 | GPIO_PIN3 , GPIO_SET_ENABLE);
delay = 500;
while(1)
{
period = delay;
GPIO_SET_OUTPUT_PIN_TO_1(gpio_sel, GPIO_PIN0 | GPIO_PIN1 | GPIO_PIN2 | GPIO_PIN3);//LED=1
// 延时1s
delay_us(20000);
/************************/
for (var = 0; var < period; ++var)
{
GPIO_SET_OUTPUT_PIN_TO_0(gpio_sel, GPIO_PIN0 | GPIO_PIN1 | GPIO_PIN2 | GPIO_PIN3);//LED =0
delay_us(var);
GPIO_SET_OUTPUT_PIN_TO_1(gpio_sel, GPIO_PIN0 | GPIO_PIN1 | GPIO_PIN2 | GPIO_PIN3);//LED=1
delay_us(period-var);
if(period !=delay) //如果period已经更新,立即退出本次调节,按照新的数据,重新调节
break;
}
// GPIOA PIN0输出0
GPIO_SET_OUTPUT_PIN_TO_0(gpio_sel, GPIO_PIN0 | GPIO_PIN1 | GPIO_PIN2 | GPIO_PIN3);//LED=0
delay_us(20000);
for (u32 var = period; var >0; --var)
{
GPIO_SET_OUTPUT_PIN_TO_0(gpio_sel, GPIO_PIN0 | GPIO_PIN1 | GPIO_PIN2 | GPIO_PIN3);//LED =0
delay_us(var);
GPIO_SET_OUTPUT_PIN_TO_1(gpio_sel, GPIO_PIN0 | GPIO_PIN1 | GPIO_PIN2 | GPIO_PIN3);//LED=1
delay_us(period-var);
if(period !=delay) //如果period已经更新,立即退出本次调节,按照新的数据,重新调节
break;
}
}
}
void thread0_main(void)
{
while(1){
//user code section
//GPIO_EXAMPLE(GPIOA_BASE_ADDR);
GPIO_TEST(GPIOB_BASE_ADDR);//,GPIO_PIN0);
}
thread_end();
}
其中delay为全局变量,多个线程之间,可以通过全局变量进行信息传递。
LTC2360的控制使用线程2
void thread1_main(void)
{
volatile u8 rx_data_temp[2];
u16 ad_value=0;
u8 tx_data_wp=0;
u8 rx_data_rp=0;
INTDEV_SET_CLK_RST(GPCOM8_BASE_ADDR,(INTDEV_RUN|INTDEV_IS_GROUP0|INTDEV_CLK_IS_CORECLK_DIV2));
GPCOM_SET_IN_PORT(GPCOM8_BASE_ADDR,(GPCOM_MASTER_IN_IS_P3));
GPCOM_SET_OUT_PORT(GPCOM8_BASE_ADDR,( \
GPCOM_P0_OUTPUT_ENABLE|GPCOM_P1_OUTPUT_ENABLE|GPCOM_P2_OUTPUT_ENABLE|GPCOM_P3_OUTPUT_DISABLE| \
GPCOM_P0_IS_HIGH| GPCOM_P1_IS_MASTER_CLK|GPCOM_P2_IS_MASTER_OUT|GPCOM_P3_IS_HIGH
));
GPCOM_SET_COM_MODE(GPCOM8_BASE_ADDR,(GPCOM_SPI_MASTER_MODE3|GPCOM_TX_MSB_FIRST|GPCOM_RX_MSB_FIRST));
GPCOM_SET_COM_SPEED(GPCOM8_BASE_ADDR,24000000,1000000);
GPCOM_SET_OVERRIDE_GPIO(GPCOM8_BASE_ADDR, ( \
GPCOM_P0_OVERRIDE_GPIO| \
GPCOM_P1_OVERRIDE_GPIO| \
GPCOM_P2_OVERRIDE_GPIO| \
GPCOM_P3_OVERRIDE_GPIO|GPCOM_P3_INPUT_ENABLE \
));
tx_data_wp=GPCOM_GET_TX_WP(GPCOM8_BASE_ADDR);
rx_data_rp=GPCOM_GET_RX_WP(GPCOM8_BASE_ADDR);
while(1){
//user code section
for (u32 var = 0; var < 100; ++var)
{
NOP();
}
GPCOM_SET_OUT_PORT(GPCOM8_BASE_ADDR,( \
GPCOM_P0_OUTPUT_ENABLE|GPCOM_P1_OUTPUT_ENABLE|GPCOM_P2_OUTPUT_ENABLE|GPCOM_P3_OUTPUT_DISABLE| \
GPCOM_P0_IS_LOW| GPCOM_P1_IS_MASTER_CLK| GPCOM_P2_IS_MASTER_OUT|GPCOM_P3_IS_HIGH
));
GPCOM_SEND_TX_DATA(GPCOM8_BASE_ADDR,0,0xff);
GPCOM_SEND_TX_DATA(GPCOM8_BASE_ADDR,1,0xff);
tx_data_wp+=2;
GPCOM_SEND_TX_WP(GPCOM8_BASE_ADDR,tx_data_wp);
while(!(GPCOM_TX_FIFO_EMPTY(GPCOM8_BASE_ADDR))){};
tx_data_wp=GPCOM_GET_TX_WP(GPCOM8_BASE_ADDR);
while(rx_data_rp!=GPCOM_GET_RX_WP(GPCOM8_BASE_ADDR)){
rx_data_temp[rx_data_rp&0x3]=GPCOM_GET_RX_DATA(GPCOM8_BASE_ADDR,rx_data_rp);
rx_data_rp+=1;
};
rx_data_rp = 0;
ad_value = rx_data_temp[0];
ad_value = ad_value<<8;
ad_value = ad_value + rx_data_temp[1];
delay = ad_value>>6;
GPCOM_SET_OUT_PORT(GPCOM8_BASE_ADDR,( \
GPCOM_P0_OUTPUT_ENABLE|GPCOM_P1_OUTPUT_ENABLE|GPCOM_P2_OUTPUT_ENABLE|GPCOM_P3_OUTPUT_DISABLE| \
GPCOM_P0_IS_HIGH| GPCOM_P1_IS_MASTER_CLK| GPCOM_P2_IS_MASTER_OUT|GPCOM_P3_IS_HIGH
));
}
thread_end();
}
LTC2360是12-bit的,连续读取两次,拼接完成后是16位,要把低4位处理一下,单片机通过SPI读取数据都是通过连续写0或者连续写ff实现的,数据读取必须配合写函数。
通过SPI写数据,数据写成功后,必须更新tx_data_wp,这一点要注意。
tx_data_wp=GPCOM_GET_TX_WP(GPCOM8_BASE_ADDR);
……
{
tx_data_wp+=2;
GPCOM_SEND_TX_WP(GPCOM8_BASE_ADDR,tx_data_wp);
while(!(GPCOM_TX_FIFO_EMPTY(GPCOM8_BASE_ADDR))){};
tx_data_wp=GPCOM_GET_TX_WP(GPCOM8_BASE_ADDR);
}
目前开发板不支持在线仿真,看不到tx_data_wp变量的数据,初步猜测这个变量,是变化的。
至此,程序的功能,全部完成了。开始编译。
程序下载使用自主研发的工具,在工程模板里面就有。
总而言之,这个单片机还是很好入门的,前期不少人进行了评测,资料比较充足。开发板的论坛地址为哇酷开发者社区。